# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['esdb', 'esdb.generated', 'esdb.streams', 'esdb.subscriptions']

package_data = \
{'': ['*']}

install_requires = \
['grpcio>=1.50.0,<2.0.0', 'protobuf>=4.21.8,<5.0.0']

setup_kwargs = {
    'name': 'esdb',
    'version': '0.3.1',
    'description': 'gRPC client for EventStore DB',
    'long_description': '# esdb-py\n\n[![PyPI version](https://badge.fury.io/py/esdb.svg)](https://pypi.org/project/esdb/)\n[![codecov](https://codecov.io/gh/andriykohut/esdb-py/branch/main/graph/badge.svg?token=YVDPTDBPFB)](https://codecov.io/gh/andriykohut/esdb-py)\n\n## EventStoreDB Python gRPC client\n\n> NOTE: This project is still work in progress\n\n<!-- TOC -->\n* [Completed features](#completed-features)\n* [Installation](#installation)\n* [Development](#development)\n* [Usage](#usage)\n  * [Connection string](#connection-string)\n  * [Discovery and node preferences](#discovery-and-node-preferences)\n  * [Connection configuration](#connection-configuration)\n  * [Append, Read, Catch-up subscriptions](#append-read-catch-up-subscriptions)\n  * [Batch append](#batch-append)\n  * [Catch-up subscription to all events with filtering](#catch-up-subscription-to-all-events-with-filtering)\n  * [Persistent subscriptions](#persistent-subscriptions)\n<!-- TOC -->\n\n## Completed features\n\n* [x] secure connection\n* [x] basic auth\n* [x] connection string parsing\n* [x] streams\n  * [x] append\n  * [x] batch append (v21.10+)\n  * [x] delete\n  * [x] read stream\n  * [x] read all with stream/event type filters (v21.10+)\n  * [x] catch-up subscriptions\n  * [x] tombstone\n  * [x] filtering\n* [x] persistent subscriptions\n  * [x] create\n  * [x] read stream\n  * [x] read all with filter (v21.10+)\n  * [x] update\n  * [x] delete\n  * [x] list\n  * [x] info\n  * [ ] reply parked events\n* [ ] CRUD for projections\n* [ ] users\n\n## Installation\n\nUsing pip:\n\n```sh\npip install esdb\n```\n\nUsing poetry:\n\n```sh\npoetry add esdb\n```\n\n## Development\n\n1. Install [poetry](https://python-poetry.org/docs/#installation)\n2. Create virtualenv (i.e. using pyenv):\n\n    ```sh\n    pyenv install 3.10.5\n    pyenv virtualenv 3.10.5 esdb-py\n    pyenv local esdb-py\n    ```\n\n3. Install deps with `poetry install`\n4. Start eventstore in docker: `make run-esdb`\n5. Run the tests: `pytest tests`\n\n## Usage\n\nHave a look at [tests](https://github.com/andriykohut/esdb-py/tree/main/tests) for more examples.\n\n### Connection string examples\n\nDNS discovery with credentials, discovery configuration, node preference and ca file path\n```\nesdb+discover://admin:changeit@localhost:2111?discoveryInterval=0&maxDiscoverAttempts=3&tlsCafile=certs/ca/ca.crt&nodePreference=follower\n```\n\nSingle-node insecure connection\n```\nesdb://localhost:2111?tls=false\n```\n\nSupported parameters:\n - `discoveryInterval`\n - `gossipTimeout`\n - `maxDiscoverAttempts`\n - `nodePreference`\n - `keepAliveInterval`\n - `keepAliveTimeout`\n - `tls`\n - `tlsCafile`\n - `tlsVerifyCert`\n - `defaultDeadline`\n\n\nConnection string can be generated [here](https://developers.eventstore.com/clients/grpc/#connection-details).\n\n### Discovery and node preferences\n\n```py\nfrom esdb import ESClient\n\nclient = ESClient("esdb+discover://admin:changeit@localhost:2111?nodePreference=follower")\n\n```\n\n### Connection configuration\n\n```py\nfrom esdb import ESClient\n\n# Connect without TLS\nclient = ESClient("esdb://localhost:2111?tls=false")\n\n# Secure connection with basic auth and keepalive\nclient = ESClient("esdb://admin:changeit@localhost:2111?tlsCafile=certs/ca/ca.crt&keepAliveInterval=5&keepAliveTimeout=5")\n```\n\n### Append, Read, Catch-up subscriptions\n\n```py\nimport asyncio\nimport datetime\nimport uuid\n\nfrom esdb import ESClient\n\n\nclient = ESClient("esdb+discover://admin:changeit@localhost:2111")\nstream = f"test-{str(uuid.uuid4())}"\n\n\nasync def streams():\n    async with client.connect() as conn:\n        # Appending to stream\n        for i in range(10):\n            append_result = await conn.streams.append(\n                stream=stream,\n                event_type="test_event",\n                data={"i": i, "ts": datetime.datetime.utcnow().isoformat()},\n            )\n\n        # Read up to 10 events\n        async for result in conn.streams.read(stream=stream, count=10):\n            print(result.data)\n\n        # Read up to 10 events, backwards\n        async for result in conn.streams.read(stream=stream, count=10, backwards=True):\n            print(result.data)\n\n        # Read up to 10 events, starting from 5th event\n        async for result in conn.streams.read(stream=stream, count=10, revision=5):\n            print(result.data)\n\n        # Read up to 10 events backwards, starting from 5th event\n        async for result in conn.streams.read(stream=stream, count=10, backwards=True, revision=5):\n            print(result.data)\n\n        # Create a catch-up subscription to a stream\n        async for result in conn.streams.read(stream=stream, subscribe=True):\n            print(result.data)\n\n\nasyncio.run(streams())\n```\n\n### Batch append\n\n```py\nimport asyncio\nimport uuid\n\nfrom esdb import ESClient\nfrom esdb.streams import Message\n\n\nasync def batch_append():\n    # Append multiple events in as a single batch\n    # Batch append is not supported on EventStore < v21.10\n    stream = str(uuid.uuid4())\n    messages: list[Message] = [\n        Message(event_type="one", data={"item": 1}),\n        Message(event_type="one", data={"item": 2}),\n        Message(event_type="one", data={"item": 3}),\n        Message(event_type="two", data={"item": 1}),\n        Message(event_type="two", data={"item": 2}),\n        Message(event_type="two", data={"item": 3}),\n    ]\n    async with ESClient("esdb+discover://admin:changeit@localhost:2111").connect() as conn:\n        response = await conn.streams.batch_append(stream=stream, messages=messages)\n        assert response.current_revision == 5\n        events = [e async for e in conn.streams.read(stream=stream, count=50)]\n        assert len(events) == 6\n\n\nasyncio.run(batch_append())\n```\n\n### Catch-up subscription to all events with filtering\n\n```py\nimport uuid\nimport asyncio\n\nfrom esdb import ESClient\nfrom esdb.shared import Filter\n\n\nasync def filters():\n    async with ESClient("esdb+discover://admin:changeit@localhost:2111").connect() as conn:\n        # Append 10 events with the same prefix to random streams\n        for i in range(10):\n            await conn.streams.append(stream=str(uuid.uuid4()), event_type=f"prefix-{i}", data=b"")\n        # subscribe to events from all streams, filtering by event type\n        async for event in conn.streams.read_all(\n                subscribe=True,  # subscribe will wait for events, use count=<n> to read <n> events and stop\n                filter_by=Filter(\n                    kind=Filter.Kind.EVENT_TYPE,\n                    regex="^prefix-",\n                    # Checkpoint only required when subscribe=True, it\'s not needed when using count=<int>\n                    checkpoint_interval_multiplier=1000,\n                ),\n        ):\n            print(event)\n\n\nasyncio.run(filters())\n```\n\n### Persistent subscriptions\n\n```python\nimport asyncio\nfrom esdb import ESClient\nfrom esdb.shared import Filter\nfrom esdb.subscriptions import SubscriptionSettings, NackAction\n\nclient = ESClient("esdb+discover://admin:changeit@localhost:2111")\n\nstream = "stream-foo"\ngroup = "group-bar"\n\n\nasync def persistent():\n    async with client.connect() as conn:\n        # emit some events to the same stream\n        for i in range(50):\n            await conn.streams.append(stream, "foobar", {"i": i})\n\n        # create a stream subscription\n        await conn.subscriptions.create_stream_subscription(\n            stream=stream,\n            group_name=group,\n            settings=SubscriptionSettings(\n                max_subscriber_count=50,\n                read_batch_size=5,\n                live_buffer_size=10,\n                history_buffer_size=10,\n                consumer_strategy=SubscriptionSettings.ConsumerStrategy.ROUND_ROBIN,\n                checkpoint_ms=10000,\n            ),\n        )\n\n        # create subscription to all events with filtering\n        # Only supported on EventStore v21.10+\n        await conn.subscriptions.create_all_subscription(\n            group_name="subscription_group",\n            filter_by=Filter(kind=Filter.Kind.EVENT_TYPE, regex="^some_type$", checkpoint_interval_multiplier=200),\n            settings=SubscriptionSettings(\n                read_batch_size=50,\n                live_buffer_size=100,\n                history_buffer_size=100,\n                max_retry_count=2,\n                checkpoint_ms=20000,\n            ),\n        )\n\n    # read from a subscription\n    async with client.connect() as conn:\n        sub = conn.subscriptions.subscribe(stream=stream, group_name=group, buffer_size=5)\n        async for event in sub:\n            try:\n                # do work with event\n                print(event)\n                await sub.ack([event])\n            except Exception as err:\n                await sub.nack([event], NackAction.RETRY, reason=str(err))\n\n        # get subscription info\n        info = await conn.subscriptions.get_info(group, stream)\n        assert info.group_name == group\n\n        # delete subscription\n        await conn.subscriptions.delete(group, stream)\n        \n        # list subscriptions\n        subs = await conn.subscriptions.list()\n        for sub in subs:\n            print(sub.total_items)\n\n\nasyncio.run(persistent())\n```\n',
    'author': 'Andrii Kohut',
    'author_email': 'kogut.andriy@gmail.com',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'https://github.com/andriykohut/esdb-py',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.9,<4.0',
}


setup(**setup_kwargs)
