{"id":27649473,"url":"https://github.com/davebshow/asyncbolt","last_synced_at":"2025-04-24T03:12:19.993Z","repository":{"id":62560033,"uuid":"113408999","full_name":"davebshow/asyncbolt","owner":"davebshow","description":":zap:asyncbolt:zap: Bolt client/server protocol for Python asyncio","archived":false,"fork":false,"pushed_at":"2017-12-14T22:22:41.000Z","size":117,"stargazers_count":13,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-24T03:12:14.322Z","etag":null,"topics":["asyncio","bolt","client-server","neo4j"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davebshow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-07T05:54:55.000Z","updated_at":"2021-09-18T18:25:53.000Z","dependencies_parsed_at":"2022-11-03T13:15:15.320Z","dependency_job_id":null,"html_url":"https://github.com/davebshow/asyncbolt","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davebshow%2Fasyncbolt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davebshow%2Fasyncbolt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davebshow%2Fasyncbolt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davebshow%2Fasyncbolt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davebshow","download_url":"https://codeload.github.com/davebshow/asyncbolt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250552077,"owners_count":21449165,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","bolt","client-server","neo4j"],"created_at":"2025-04-24T03:12:19.370Z","updated_at":"2025-04-24T03:12:19.989Z","avatar_url":"https://github.com/davebshow.png","language":"Python","readme":"# :zap:asyncbolt:zap:\n\n:zap:`asyncbolt`:zap: [Neo4j](https://neo4j.com/) [Bolt](https://boltprotocol.org/) client/server protocol for Python.\n\n### Features\n\n* Implementation of the Bolt protocol for [asyncio](https://docs.python.org/3/library/asyncio.html) `asyncbolt.BoltClientProtocol`, `asyncbolt.ClientSession`, `asyncbolt.BoltServerProtocol`, `asyncbolt.ServerSession`\n* A [Bolt message transfer encoding](https://boltprotocol.org/v1/#message_transfer_encoding) stateless parser `asyncbolt.BoltParser`\n* Serializer/deserializer for [Bolt messages](https://boltprotocol.org/v1/#messaging) `asyncbolt.message_serializer`, `asyncbolt.message_deserializer`\n* Chunked read/write buffer `asyncbolt.ChunkedReadBuffer` and `asyncbolt.ChunkedWriteBuffer` implementations with interfaces for `asyncbolt.message_serializer`, and `asyncbolt.message_deserializer`.\n* 0 dependencies\n\n**_Python\u003e=3.6_**\n\n**WARNING** - *This project is in early stages of development--breaking changes and bugs coming soon*\n\n## Getting started\n\nInstall with pip:\n\n```\n$ pip install asyncbolt\n```\n\n### Basic Example\n\nSet up the server by subclassing `asyncbolt.ServerSession` and implementing the method `run`. Run is called when the server\nreceives a RUN message from the client:\n\n```python\nimport asyncio\nimport asyncbolt\n\n\nclass EchoServerSession(asyncbolt.ServerSession):\n    \"\"\"This is a descendant of asyncio.Protocol/asyncbolt.BoltServerProtocol\"\"\"\n    def run(self, statement, parameters):\n        return {'statement': statement, 'parameters': parameters}\n\n\n# The rest is pretty similar to asyncio...\n# Note that the first arg of create_server is a protocol class, not a factory\n# it will be called with any additional kwargs passed to to create_server\nloop = asyncio.get_event_loop()\ncoro = asyncbolt.create_server(EchoServerSession, loop=loop, host='localhost', port=8888, ssl=None)\nserver = loop.run_until_complete(coro)\n\n# Serve requests until Ctrl+C is pressed\nprint('Serving on {}'.format(server.sockets[0].getsockname()))\ntry:\n    loop.run_forever()\nexcept KeyboardInterrupt:\n    pass\nserver.close()\nloop.run_until_complete(server.wait_closed())\nloop.close()\n```\n\nUse an `asyncbolt.ClientSession` to talk to the server:\n\n```python\nimport asyncio\nimport asyncbolt\n\n\nasync def echo(loop):\n    client_session = await asyncbolt.connect(loop=loop, host='localhost', port=8888, ssl=None)\n    results = []\n    async for msg in client_session.run('Hello world', {}, get_eof=True):\n        results.append(msg)\n    return results\n\n\nloop = asyncio.get_event_loop()\nresults = loop.run_until_complete(echo(loop))\nprint(results)\n```\n\n### Client\n\nUsing the client is easy. The following shows how to use `asyncbolt.ClientSession` to talk to the [Neo4j](https://neo4j.com/) server.\nThis technique can be extended for use with any server that speaks Bolt.\n\n#### Connecting to Neo4j\n\nGet the server, unpack, cd, and fire it up:\n\n```\n$ wget dist.neo4j.org/neo4j-community-3.3.1-unix.tar.gz\n$ tar xvf neo4j-community-3.3.1-unix.tar.gz\n$ cd neo4j-community-3.3.1/\n$ bin/neo4j start\n```\n\n##### Authentication\n\nTypically, you will want to authenticate the client with the server. `asyncbolt.BoltClientProtocol` doesn't\nsupport authentication out of the box, but it is easy to create a subclass that does:\n\n```python\nimport asyncio\nimport asyncbolt\n\n\nclass Neo4jBoltClientProtocol(asyncbolt.BoltClientProtocol):\n\n    def __init__(self, loop, *, username=None, password=None):\n        super().__init__(loop)\n        self.username = username\n        self.password = password\n\n    def get_init_params(self):\n        return 'AsyncBolt/1.0', {\"scheme\": \"basic\", \"principal\": self.username, \"credentials\": self.password}\n```\n\nThe methods `__init__` and `get_init_params` are basically the only methods inheriting protocols need implement, as\nauthentication will typically be the only difference between server implementations, at least from the client's perspective.\n\n##### Connect and submit a query\n\nUse `asyncbolt.connect` to create an `asyncbolt.ClientSession` instance, passing the custom protocol class and its\nkwargs. Then use the run method to submit Cypher to the server:\n\n```python\nloop = asyncio.get_event_loop()\nclient_session = await asyncbolt.connect(loop=loop,\n                                         host='localhost',\n                                         port=7687,\n                                         protocol_class=Neo4jBoltClientProtocol,\n                                         username='neo4j', password='password')\n    \nasync for msg in client_session.run(\"RETURN 1 AS num\", {}):\n    print(msg)\n# ClientResponse(fields=[1], metadata={'result_available_after': 0, 'fields': ['num']}, eof=False)\n```\n\n##### Get metadata\n\nIf you are interested in extra metadata sent by the Neo4j server, be sure to set the `get_eof` kwarg to `True` when\ncalling the `run` method. For example, when you want to use query profiling/explanation:\n\n```python\nasync for msg in client_session.run(\"EXPLAIN RETURN 1 AS num\",  {}, get_eof=True):\n    print(msg)\n#ClientResponse(\n#    fields=None,\n#    metadata={'result_consumed_after': 0,\n#              'type': 'r',\n#              'plan': {'args': {'runtime': 'INTERPRETED',\n#                                'planner-impl': 'IDP',\n#                                'runtime-impl': 'INTERPRETED',\n#                                'version': 'CYPHER 3.3',\n#                                'EstimatedRows': 1.0,\n#                                'planner': 'COST'}, \n#                      'children': [\n#                        {'args': {'EstimatedRows': 1.0, 'Expressions': '{num : {  AUTOINT0}}'},\n#                         'children': [], 'identifiers': ['num'], 'operatorType': 'Projection'}],\n#                      'identifiers': ['num'], 'operatorType': 'ProduceResults'}},\n#    eof=True)\n```\n\n##### Run the Neo4j server Bolt protocol server test dialogue\n\n`asyncbolt.ClientSession` appears to communicate fluently with the Neo4j Server. The script `bolt_neo4_demo.py`\nimplements some of the examples from the Bolt protocol homepage. It can be run as follows.\n\nClone this repo, cd, and run:\n```buildoutcfg\n$ git clone https://github.com/davebshow/asyncbolt.git\n$ cd asyncbolt\n$ python bolt_neo4j_demo.py -u myusername -p mypassword\n```\n\nThis script sets logging to debug and produces the following output:\n\n```txt\nRunning the examples from the Bolt documentation...\n\nConnection made to: ('127.0.0.1', 7687)\nSending handshake with version info: b'\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\nUsing Bolt protocol version b'\\x00\\x00\\x00\\x01'\n\nWriting message to transport\n'b'\\x00C\\xb2\\x01\\x8dAsyncBolt/1.0\\xa3\\x86scheme\\x85basic\\x89principal\\x85neo4j\\x8bcredentials\\x88password\\x00\\x00''\n\nData received:\nb'\\x00\\x16\\xb1p\\xa1\\x86server\\x8bNeo4j/3.3.1\\x00\\x00'\n\nClient session initialized with server metadata:\n\n{'server': 'Neo4j/3.3.1'}\n\nRunning a Cypher query...\n\nPipelining statement and params:\nRETURN 1 AS num\n{}\n\nPipelining PULL_ALL\n\nWriting message to transport\n'b'\\x00\\x13\\xb2\\x10\\x8fRETURN 1 AS num\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00''\n\nData received:\nb'\\x00(\\xb1p\\xa2\\xd0\\x16result_available_after\\x00\\x86fields\\x91\\x83num\\x00\\x00\\x00\\x04\\xb1q\\x91\\x01\\x00\\x00\\x00\"\\xb1p\\xa2\\xd0\\x15result_consumed_after\\x00\\x84type\\x81r\\x00\\x00'\n\nClient received message: \n\nClientResponse(fields=[1], metadata={'result_available_after': 0, 'fields': ['num']}, eof=False)\n\nPipelining...\n\nPipelining statement and params:\nRETURN 1 AS num\n{}\n\nPipelining PULL_ALL\n\nPipelining statement and params:\nRETURN 1 AS num\n{}\n\nPipelining PULL_ALL\n\nWriting message to transport\n'b'\\x00\\x13\\xb2\\x10\\x8fRETURN 1 AS num\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00\\x00\\x13\\xb2\\x10\\x8fRETURN 1 AS num\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00''\n\nData received:\nb'\\x00(\\xb1p\\xa2\\xd0\\x16result_available_after\\x00\\x86fields\\x91\\x83num\\x00\\x00\\x00\\x04\\xb1q\\x91\\x01\\x00\\x00\\x00\"\\xb1p\\xa2\\xd0\\x15result_consumed_after\\x00\\x84type\\x81r\\x00\\x00\\x00(\\xb1p\\xa2\\xd0\\x16result_available_after\\x00\\x86fields\\x91\\x83num\\x00\\x00\\x00\\x04\\xb1q\\x91\\x01\\x00\\x00\\x00\"\\xb1p\\xa2\\xd0\\x15result_consumed_after\\x00\\x84type\\x81r\\x00\\x00'\n\nClient received message: \n\nClientResponse(fields=[1], metadata={'result_available_after': 0, 'fields': ['num']}, eof=False)\n\nClient received message: \n\nClientResponse(fields=[1], metadata={'result_available_after': 0, 'fields': ['num']}, eof=False)\n\nError handling with Reset...\n\nPipelining statement and params:\nThis will cause a syntax error\n{}\n\nPipelining PULL_ALL\n\nWriting message to transport\n'b'\\x00#\\xb2\\x10\\xd0\\x1eThis will cause a syntax error\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00''\n\nData received:\nb'\\x00\\x9e\\xb1\\x7f\\xa2\\x84code\\xd0%Neo.ClientError.Statement.SyntaxError\\x87message\\xd0eInvalid input \\'T\\': expected \u003cinit\u003e (line 1, column 1 (offset: 0))\\n\"This will cause a syntax error\"\\n ^\\x00\\x00\\x00\\x02\\xb0~\\x00\\x00'\n\nWriting message to transport\n'b'\\x00\\x02\\xb0\\x0f\\x00\\x00''\n\nData received:\nb'\\x00\\x03\\xb1p\\xa0\\x00\\x00'\n\nClient raised exception: \n\nServer failed. Reset with '15'\n\nAccessing basic result metadata...\n\nPipelining statement and params:\nCREATE ()\n{}\n\nPipelining PULL_ALL\n\nWriting message to transport\n'b'\\x00\\r\\xb2\\x10\\x89CREATE ()\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00''\n\nData received:\nb'\\x00$\\xb1p\\xa2\\xd0\\x16result_available_after\\x01\\x86fields\\x90\\x00\\x00\\x008\\xb1p\\xa3\\x85stats\\xa1\\x8dnodes-created\\x01\\xd0\\x15result_consumed_after\\x00\\x84type\\x81w\\x00\\x00'\n\nClient received message: \n\nClientResponse(fields=None, metadata={'stats': {'nodes-created': 1}, 'result_consumed_after': 0, 'type': 'w'}, eof=True)\n\nExplaining and profiling a query...\n\nPipelining statement and params:\nEXPLAIN RETURN 1 AS num\n{}\n\nPipelining PULL_ALL\n\nWriting message to transport\n'b'\\x00\\x1c\\xb2\\x10\\xd0\\x17EXPLAIN RETURN 1 AS num\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00''\n\nData received:\nb'\\x00(\\xb1p\\xa2\\xd0\\x16result_available_after\\x00\\x86fields\\x91\\x83num\\x00\\x00\\x01M\\xb1p\\xa3\\xd0\\x15result_consumed_after\\x00\\x84type\\x81r\\x84plan\\xa4\\x84args\\xa6\\x87runtime\\x8bINTERPRETED\\x8cplanner-impl\\x83IDP\\x8cruntime-impl\\x8bINTERPRETED\\x87version\\x8aCYPHER 3.3\\x8dEstimatedRows\\xc1?\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\\x87planner\\x84COST\\x88children\\x91\\xa4\\x84args\\xa2\\x8dEstimatedRows\\xc1?\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\\x8bExpressions\\xd0\\x14{num : {  AUTOINT0}}\\x88children\\x90\\x8bidentifiers\\x91\\x83num\\x8coperatorType\\x8aProjection\\x8bidentifiers\\x91\\x83num\\x8coperatorType\\x8eProduceResults\\x00\\x00'\n\nClient received message: \n\nClientResponse(fields=None, metadata={'result_consumed_after': 0, 'type': 'r', 'plan': {'args': {'runtime': 'INTERPRETED', 'planner-impl': 'IDP', 'runtime-impl': 'INTERPRETED', 'version': 'CYPHER 3.3', 'EstimatedRows': 1.0, 'planner': 'COST'}, 'children': [{'args': {'EstimatedRows': 1.0, 'Expressions': '{num : {  AUTOINT0}}'}, 'children': [], 'identifiers': ['num'], 'operatorType': 'Projection'}], 'identifiers': ['num'], 'operatorType': 'ProduceResults'}}, eof=True)\n\nAccessing notifications...\n\nPipelining statement and params:\nEXPLAIN MATCH (n), (m) RETURN n, m\n{}\n\nPipelining PULL_ALL\n\nWriting message to transport\n'b'\\x00\\'\\xb2\\x10\\xd0\"EXPLAIN MATCH (n), (m) RETURN n, m\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00''\n\nData received:\nb'\\x00(\\xb1p\\xa2\\xd0\\x16result_available_after\\x00\\x86fields\\x92\\x81n\\x81m\\x00\\x00\\x04X\\xb1p\\xa4\\xd0\\x15result_consumed_after\\x00\\x84type\\x81r\\x84plan\\xa4\\x84args\\xa6\\x87runtime\\x8bINTERPRETED\\x8cplanner-impl\\x83IDP\\x8cruntime-impl\\x8bINTERPRETED\\x87version\\x8aCYPHER 3.3\\x8dEstimatedRows\\xc1?\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\\x87planner\\x84COST\\x88children\\x91\\xa4\\x84args\\xa1\\x8dEstimatedRows\\xc1?\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\\x88children\\x92\\xa4\\x84args\\xa1\\x8dEstimatedRows\\xc1?\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\\x88children\\x90\\x8bidentifiers\\x91\\x81n\\x8coperatorType\\x8cAllNodesScan\\xa4\\x84args\\xa1\\x8dEstimatedRows\\xc1?\\xf0\\x00\\x00\\x00\\x00\\x00\\x00\\x88children\\x90\\x8bidentifiers\\x91\\x81m\\x8coperatorType\\x8cAllNodesScan\\x8bidentifiers\\x92\\x81m\\x81n\\x8coperatorType\\xd0\\x10CartesianProduct\\x8bidentifiers\\x92\\x81m\\x81n\\x8coperatorType\\x8eProduceResults\\x8dnotifications\\x91\\xa5\\x88severity\\x87WARNING\\x8bdescription\\xd1\\x01\\xa9If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (m))\\x84code\\xd08Neo.ClientNotification.Statement.CartesianProductWarning\\x88position\\xa3\\x86offset\\x08\\x86column\\t\\x84line\\x01\\x85title\\xd0DThis query builds a cartesian product between disconnected patterns.\\x00\\x00'\n\nClient received message: \n\nClientResponse(fields=None, metadata={'result_consumed_after': 0, 'type': 'r', 'plan': {'args': {'runtime': 'INTERPRETED', 'planner-impl': 'IDP', 'runtime-impl': 'INTERPRETED', 'version': 'CYPHER 3.3', 'EstimatedRows': 1.0, 'planner': 'COST'}, 'children': [{'args': {'EstimatedRows': 1.0}, 'children': [{'args': {'EstimatedRows': 1.0}, 'children': [], 'identifiers': ['n'], 'operatorType': 'AllNodesScan'}, {'args': {'EstimatedRows': 1.0}, 'children': [], 'identifiers': ['m'], 'operatorType': 'AllNodesScan'}], 'identifiers': ['m', 'n'], 'operatorType': 'CartesianProduct'}], 'identifiers': ['m', 'n'], 'operatorType': 'ProduceResults'}, 'notifications': [{'severity': 'WARNING', 'description': 'If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (m))', 'code': 'Neo.ClientNotification.Statement.CartesianProductWarning', 'position': {'offset': 8, 'column': 9, 'line': 1}, 'title': 'This query builds a cartesian product between disconnected patterns.'}]}, eof=True)\n\nGet somes nodes...\n\nPipelining statement and params:\nMATCH (n) RETURN n\n{}\n\nPipelining PULL_ALL\n\nWriting message to transport\n'b'\\x00\\x17\\xb2\\x10\\xd0\\x12MATCH (n) RETURN n\\xa0\\x00\\x00\\x00\\x02\\xb0?\\x00\\x00''\n\nData received:\nb'\\x00\u0026\\xb1p\\xa2\\xd0\\x16result_available_after\\x00\\x86fields\\x91\\x81n\\x00\\x00\\x00\\x08\\xb1q\\x91\\xb3N\\x00\\x90\\xa0\\x00\\x00\\x00\\x08\\xb1q\\x91\\xb3N\\x01\\x90\\xa0\\x00\\x00\\x00\\x08\\xb1q\\x91\\xb3N\\x02\\x90\\xa0\\x00\\x00\\x00\\x08\\xb1q\\x91\\xb3N\\x03\\x90\\xa0\\x00\\x00\\x00\\x08\\xb1q\\x91\\xb3N\\x04\\x90\\xa0\\x00\\x00\\x00\"\\xb1p\\xa2\\xd0\\x15result_consumed_after\\x00\\x84type\\x81r\\x00\\x00'\n\nClient received message: ClientResponse(fields=[Node(signature=78, nodeIdentity=0, labels=[], properties={})], metadata={'result_available_after': 0, 'fields': ['n']}, eof=False)\nClient received message: ClientResponse(fields=[Node(signature=78, nodeIdentity=1, labels=[], properties={})], metadata={'result_available_after': 0, 'fields': ['n']}, eof=False)\nClient received message: ClientResponse(fields=[Node(signature=78, nodeIdentity=2, labels=[], properties={})], metadata={'result_available_after': 0, 'fields': ['n']}, eof=False)\nClient received message: ClientResponse(fields=[Node(signature=78, nodeIdentity=3, labels=[], properties={})], metadata={'result_available_after': 0, 'fields': ['n']}, eof=False)\nClient received message: ClientResponse(fields=[Node(signature=78, nodeIdentity=4, labels=[], properties={})], metadata={'result_available_after': 0, 'fields': ['n']}, eof=False)\n\nReset session...\n\nWriting message to transport\n'b'\\x00\\x02\\xb0\\x0f\\x00\\x00''\n\nData received:\nb'\\x00\\x03\\xb1p\\xa0\\x00\\x00'\n\nClient received message: \n\nClientResponse(fields=['Successfully reset sever session'], metadata={}, eof=True)\n\nFinished in 0.018699501997616608\n```\n\n#### Pipelining\nAs you can see, `asyncbolt` using pipelining by default. Users can also choose to pipeline multiple message together.\nDone properly, this can result in huge performance gains.\n\n```python\n# Write several messages to the buffer\nclient_session.pipeline(\"RETURN 1 AS num\", {})\nclient_session.pipeline(\"RETURN 1 AS num\", {})\nclient_session.pipeline(\"RETURN 1 AS num\", {})\n# Write one more and send them to the server with `run`\nasync for msg in client_session.run(\"RETURN 1 AS num\", {}):\n    print(msg)\n```\n\n#### Reset\nReset the server to a clean state:\n```python\nawait client_session.reset()\n```\n\n## Server\nUnlike `asyncbolt.ClientSession`, the `asyncbolt.ServerSession` class can never be used out of the box. Users must\nimplement a subclass of `asyncbolt.ServerSession` with the method `run`, which can be a coroutine or a regular function.\nOptionally, inheriting classes can also implement the `asyncbolt.ServerSession.verify_auth_token` method. \n\n\n```python\nclass AwesomeServerSession(asyncbolt.ServerSession):\n    \"\"\"asyncbolt.ServerSession is a descendant of asyncio.Protocol/asyncbolt.BoltServerProtocol\"\"\"\n    async def run(self, statement, parameters):\n        # ...do something awesome here... \n        \n    def verify_auth_token(self, auth_token):\n        # ...do auth...\n```\n\nUsers will almost never need to inherit directly from `asyncbolt.BoltServerProtocol`, and therefore it will not be discussed here.\nIf you are interested, `asyncbolt.ServerSession` inherits directly from the protocol, using `asyncio` objects to implement\nthe Bolt session logic.\n\n## Bolt message transfer encoding parser\n\n`asyncbolt.BoltParser` is a stateless parser used for parsing incoming Bolt datastreams. The parser accepts a single\nargument `protocol`, which is a Python object that implements the methods `on_chunk`, and `on_message_complete`. A typical\nimplementation would use the `asyncbolt.ChunkedReadBuffer` to assemble the chunks:\n\n```python\nclass DummyProtocol:\n\ndef __init__(self):\n    self.read_buffer = asyncbolt.ChunkedReadBuffer()\n    self.parser = asyncbolt.BoltParser(self)\n\ndef data_received(self, data):\n    \"\"\"Or however you get data\"\"\"\n    self.parser.feed_data(data)\n\ndef on_chunk(self, chunk):\n    self.read_buffer.feed_data(chunk)\n    \ndef on_message_complete(self):\n    self.read_buffer.feed_eof()\n```\n\n## Bolt message serializers/deserializers\n\n`asyncbolt.serialize_message` and `asyncbolt.deserialize_message` are responsible for translating between Python\nobjects and C structs represented as bytes objects using the Bolt binary message serialization format.\n\n### Serialization\n`asyncbolt.serialize_message` has the following signature:\n\n```python\nserialize_message(signature, *, buf=None, params=None, max_chunk_size=8192)\n```\n\n* `signature` is a bolt message signature RECORD, SUCCESS, RUN etc. enumerated with `asyncbolt.Message`\n* `buf` is a Python object that implements the methods `write(data: bytes)` and `write_eof()`. Defaults to `asyncbolt.ChunkedWriteBuffer`\n* `params` a `tuple` of parameters that will be passed to the Bolt message\n* `max_chunk_size` is the maximum number of bytes sent in a single message. Passed to the default write buffer.\n\n### Deserialization\n`asyncbolt.deserialize_message` has the following signature:\n\n```python\ndeserialize_message(buf)\n```\n\n* `buf` is a Python object that implements the method `read(n: int)` where `n` is the number of bytes that will be\nreturned. Typically, an `asyncbolt.ChunkedReadBuffer` will be used\n\n## TODOs\n:zap:`asyncbolt`:zap: needs a lot of work. Contributions are welcome.\n\n* Tests, tests, tests (Neo4j, *buffers*, etc., etc.)\n* Improve buffer implementation/testing\n* ServerSession needs some work\n* Set up for upcoming versions of Bolt\n* Profiling and optimization (Cython/C extensions)\n* Add CI\n* Improve docstrings and generate API docs.\n* Improve everything\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavebshow%2Fasyncbolt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavebshow%2Fasyncbolt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavebshow%2Fasyncbolt/lists"}