ssec


Namessec JSON
Version 4.2.2 PyPI version JSON
download
home_pageNone
SummarySynchronous and asynchronous library for Server Sent Event (SSE).
upload_time2025-03-20 14:40:50
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseNone
keywords server sent event (sse) sse-client
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <p align="center">
    <img src="https://github.com/ReMi-HSBI/ssec/blob/main/docs/src/static/images/ssec_logo.svg?raw=true" alt="drawing" width="33%"/>
</p>

# ssec

[![ruff](https://img.shields.io/badge/ruff-⚡-261230.svg?style=flat-square)](https://docs.astral.sh/ruff)
[![mypy](https://img.shields.io/badge/mypy-📝-2a6db2.svg?style=flat-square)](https://mypy-lang.org)
[![gitmoji](https://img.shields.io/badge/gitmoji-😜%20😍-FFDD67.svg?style=flat-square)](https://gitmoji.dev)

## Description

Python package for synchronous and asynchronous streaming of
[Server-Sent Events (SSE)](https://html.spec.whatwg.org/multipage/server-sent-events.html).
This library works with [httpx](https://github.com/encode/httpx)
to support synchronous as well as asynchronous workflows but is also
usable with other http frameworks ([see below](#aiohttp)).

## Installation

```sh
pip install ssec
```

## Example

`sync`

```python
import logging
import ssec

def main() -> None:
    logging.basicConfig(level=logging.INFO)
    for event in ssec.sse(
        "https://stream.wikimedia.org/v2/stream/recentchange"
    ):
        print(event)

main()
```

`async`

```python
import asyncio
import logging
import ssec

async def main() -> None:
    logging.basicConfig(level=logging.INFO)
    async for event in ssec.sse_async(
        "https://stream.wikimedia.org/v2/stream/recentchange"
    ):
        print(event)

asyncio.run(main())
```

## Note

Although there are already some libraries on the subject
([aiohttp-sse-client](https://github.com/rtfol/aiohttp-sse-client),
[aiosseclient](https://github.com/ebraminio/aiosseclient)), these are
unfortunately not entirely correct. In example, both mentioned libraries
asynchronously iterate over the stream content via
`async for line in response.content`[^1][^2].
This internally calls [aiohttp](https://docs.aiohttp.org/en/stable)'s
[`readuntil`](https://docs.aiohttp.org/en/stable/streams.html#aiohttp.StreamReader.readuntil)
method with the default seperator `\n`, but the official specification
says:

> Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE
> FEED (CRLF) character pair, a single U+000A LINE FEED (LF) character,
> or a single +000D CARRIAGE RETURN (CR) character.

Another point is the error handling, which is often not sufficient to analyze
the error or is entirely skipped.

### aiohttp

Although this library works with `httpx`, it is also possible to use it
with other http frameworks like `aiohttp` as long as they provide a
method to iterate over a byte-stream. Unfortunately, it is not possible
to handle reconnection then, so you will have to implement that by
yourself. An example could look like this:

```python
import asyncio
import logging

import aiohttp
import ssec

async def main() -> None:
    logging.basicConfig(level=logging.INFO)

    chunk_size = 1024
    connect_attempt = 1
    max_connect_attempts = 5
    config = ssec.SSEConfig(reconnect_timeout=3)
    async with aiohttp.ClientSession() as session:
        while True:
            headers = {
                "Accept": "text/event-stream",
                "Cache-Control": "no-store",
            }
            if config.last_event_id:
                headers["Last-Event-ID"] = config.last_event_id
            try:
                async with session.get(
                    "https://stream.wikimedia.org/v2/stream/recentchange",
                ) as response:
                    streamer = response.content.iter_chunked(chunk_size)
                    async for event in ssec.stream_async(streamer, config=config):
                        print(event)
            except aiohttp.ClientError:
                if connect_attempt >= max_connect_attempts:
                    logging.exception("Failed to connect!")
                    raise

                waiting_period = config.reconnect_timeout

                message = (
                    f"Failed to connect. "
                    f"Reconnect in {waiting_period} seconds "
                    f"[attempt {connect_attempt}/{max_connect_attempts}]."
                )
                logging.info(message)

                connect_attempt += 1
                await asyncio.sleep(waiting_period)

asyncio.run(main())
```

[^1]: [Code Reference](https://github.com/rtfol/aiohttp-sse-client/blob/e311075ac8b9b75d8b09512f8638f1dd03e2ef2b/aiohttp_sse_client/client.py#L157)  
[^2]: [Code Reference](https://github.com/ebraminio/aiosseclient/blob/375d597bcc3a7bf871b65913b366d515b300dc93/aiosseclient.py#L131)

## Miscellaneous

### Installation (Developer)

```sh
pip install ssec[dev]
```

### Documentation

Build the documentation by running the following command in the root
directory of the project:

```sh
sphinx-build -b html docs/src docs/build
```

> The command requires the [developers edition](#installation-developer)
> of `ssec`.

The documentation is then accessible via `docs/build/index.html`.

### Set up Visual Studio Code for Development

To edit the code base with [Visual Studio Code](https://code.visualstudio.com),
you should install the `@recommended` extensions. Other necessary
settings are already included in the `.vscode` directory and should be
enabled by default.

## Contributing

Contributing to `ssec` is highly appreciated, but comes with some
requirements:

1. **Type Hints**

    Write modern python code using
    [type annotations](https://peps.python.org/pep-0484/)
    to enable static analysis and potential runtime type checking.

2. **Documentation**

    Write quality documentation using
    [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html)
    docstring conventions.

3. **Linting**

   Lint your code with [ruff](https://github.com/charliermarsh/ruff) and
   [mypy](http://mypy-lang.org).

4. **Style**

    Format your code using [ruff](https://github.com/charliermarsh/ruff).

5. **Testing**

    Write tests for your code using [pytest](https://pypi.org/project/pytest).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "ssec",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": "\"J. Baudisch\" <justin.baudisch@hsbi.de>",
    "keywords": "Server Sent Event (SSE), SSE-Client",
    "author": null,
    "author_email": "\"J. Baudisch\" <justin.baudisch@hsbi.de>",
    "download_url": "https://files.pythonhosted.org/packages/a8/a4/8d48a287f768952ab8a846a9cbebb61de920273ff073f1268f8d7466e5b5/ssec-4.2.2.tar.gz",
    "platform": null,
    "description": "<p align=\"center\">\r\n    <img src=\"https://github.com/ReMi-HSBI/ssec/blob/main/docs/src/static/images/ssec_logo.svg?raw=true\" alt=\"drawing\" width=\"33%\"/>\r\n</p>\r\n\r\n# ssec\r\n\r\n[![ruff](https://img.shields.io/badge/ruff-\u26a1-261230.svg?style=flat-square)](https://docs.astral.sh/ruff)\r\n[![mypy](https://img.shields.io/badge/mypy-\ud83d\udcdd-2a6db2.svg?style=flat-square)](https://mypy-lang.org)\r\n[![gitmoji](https://img.shields.io/badge/gitmoji-\ud83d\ude1c%20\ud83d\ude0d-FFDD67.svg?style=flat-square)](https://gitmoji.dev)\r\n\r\n## Description\r\n\r\nPython package for synchronous and asynchronous streaming of\r\n[Server-Sent Events (SSE)](https://html.spec.whatwg.org/multipage/server-sent-events.html).\r\nThis library works with [httpx](https://github.com/encode/httpx)\r\nto support synchronous as well as asynchronous workflows but is also\r\nusable with other http frameworks ([see below](#aiohttp)).\r\n\r\n## Installation\r\n\r\n```sh\r\npip install ssec\r\n```\r\n\r\n## Example\r\n\r\n`sync`\r\n\r\n```python\r\nimport logging\r\nimport ssec\r\n\r\ndef main() -> None:\r\n    logging.basicConfig(level=logging.INFO)\r\n    for event in ssec.sse(\r\n        \"https://stream.wikimedia.org/v2/stream/recentchange\"\r\n    ):\r\n        print(event)\r\n\r\nmain()\r\n```\r\n\r\n`async`\r\n\r\n```python\r\nimport asyncio\r\nimport logging\r\nimport ssec\r\n\r\nasync def main() -> None:\r\n    logging.basicConfig(level=logging.INFO)\r\n    async for event in ssec.sse_async(\r\n        \"https://stream.wikimedia.org/v2/stream/recentchange\"\r\n    ):\r\n        print(event)\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n## Note\r\n\r\nAlthough there are already some libraries on the subject\r\n([aiohttp-sse-client](https://github.com/rtfol/aiohttp-sse-client),\r\n[aiosseclient](https://github.com/ebraminio/aiosseclient)), these are\r\nunfortunately not entirely correct. In example, both mentioned libraries\r\nasynchronously iterate over the stream content via\r\n`async for line in response.content`[^1][^2].\r\nThis internally calls [aiohttp](https://docs.aiohttp.org/en/stable)'s\r\n[`readuntil`](https://docs.aiohttp.org/en/stable/streams.html#aiohttp.StreamReader.readuntil)\r\nmethod with the default seperator `\\n`, but the official specification\r\nsays:\r\n\r\n> Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE\r\n> FEED (CRLF) character pair, a single U+000A LINE FEED (LF) character,\r\n> or a single +000D CARRIAGE RETURN (CR) character.\r\n\r\nAnother point is the error handling, which is often not sufficient to analyze\r\nthe error or is entirely skipped.\r\n\r\n### aiohttp\r\n\r\nAlthough this library works with `httpx`, it is also possible to use it\r\nwith other http frameworks like `aiohttp` as long as they provide a\r\nmethod to iterate over a byte-stream. Unfortunately, it is not possible\r\nto handle reconnection then, so you will have to implement that by\r\nyourself. An example could look like this:\r\n\r\n```python\r\nimport asyncio\r\nimport logging\r\n\r\nimport aiohttp\r\nimport ssec\r\n\r\nasync def main() -> None:\r\n    logging.basicConfig(level=logging.INFO)\r\n\r\n    chunk_size = 1024\r\n    connect_attempt = 1\r\n    max_connect_attempts = 5\r\n    config = ssec.SSEConfig(reconnect_timeout=3)\r\n    async with aiohttp.ClientSession() as session:\r\n        while True:\r\n            headers = {\r\n                \"Accept\": \"text/event-stream\",\r\n                \"Cache-Control\": \"no-store\",\r\n            }\r\n            if config.last_event_id:\r\n                headers[\"Last-Event-ID\"] = config.last_event_id\r\n            try:\r\n                async with session.get(\r\n                    \"https://stream.wikimedia.org/v2/stream/recentchange\",\r\n                ) as response:\r\n                    streamer = response.content.iter_chunked(chunk_size)\r\n                    async for event in ssec.stream_async(streamer, config=config):\r\n                        print(event)\r\n            except aiohttp.ClientError:\r\n                if connect_attempt >= max_connect_attempts:\r\n                    logging.exception(\"Failed to connect!\")\r\n                    raise\r\n\r\n                waiting_period = config.reconnect_timeout\r\n\r\n                message = (\r\n                    f\"Failed to connect. \"\r\n                    f\"Reconnect in {waiting_period} seconds \"\r\n                    f\"[attempt {connect_attempt}/{max_connect_attempts}].\"\r\n                )\r\n                logging.info(message)\r\n\r\n                connect_attempt += 1\r\n                await asyncio.sleep(waiting_period)\r\n\r\nasyncio.run(main())\r\n```\r\n\r\n[^1]: [Code Reference](https://github.com/rtfol/aiohttp-sse-client/blob/e311075ac8b9b75d8b09512f8638f1dd03e2ef2b/aiohttp_sse_client/client.py#L157)  \r\n[^2]: [Code Reference](https://github.com/ebraminio/aiosseclient/blob/375d597bcc3a7bf871b65913b366d515b300dc93/aiosseclient.py#L131)\r\n\r\n## Miscellaneous\r\n\r\n### Installation (Developer)\r\n\r\n```sh\r\npip install ssec[dev]\r\n```\r\n\r\n### Documentation\r\n\r\nBuild the documentation by running the following command in the root\r\ndirectory of the project:\r\n\r\n```sh\r\nsphinx-build -b html docs/src docs/build\r\n```\r\n\r\n> The command requires the [developers edition](#installation-developer)\r\n> of `ssec`.\r\n\r\nThe documentation is then accessible via `docs/build/index.html`.\r\n\r\n### Set up Visual Studio Code for Development\r\n\r\nTo edit the code base with [Visual Studio Code](https://code.visualstudio.com),\r\nyou should install the `@recommended` extensions. Other necessary\r\nsettings are already included in the `.vscode` directory and should be\r\nenabled by default.\r\n\r\n## Contributing\r\n\r\nContributing to `ssec` is highly appreciated, but comes with some\r\nrequirements:\r\n\r\n1. **Type Hints**\r\n\r\n    Write modern python code using\r\n    [type annotations](https://peps.python.org/pep-0484/)\r\n    to enable static analysis and potential runtime type checking.\r\n\r\n2. **Documentation**\r\n\r\n    Write quality documentation using\r\n    [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html)\r\n    docstring conventions.\r\n\r\n3. **Linting**\r\n\r\n   Lint your code with [ruff](https://github.com/charliermarsh/ruff) and\r\n   [mypy](http://mypy-lang.org).\r\n\r\n4. **Style**\r\n\r\n    Format your code using [ruff](https://github.com/charliermarsh/ruff).\r\n\r\n5. **Testing**\r\n\r\n    Write tests for your code using [pytest](https://pypi.org/project/pytest).\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Synchronous and asynchronous library for Server Sent Event (SSE).",
    "version": "4.2.2",
    "project_urls": {
        "Repository": "https://github.com/ReMi-HSBI/ssec"
    },
    "split_keywords": [
        "server sent event (sse)",
        " sse-client"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "17631537b7c8e1fc445490c40bbc26bf56020eeb4394340adab264ad6be5d8c6",
                "md5": "babc86850e5b76aa2d4b3faf33b4cfca",
                "sha256": "369a185b5de23d98e25c76d717b3be9ec16acb03c6be80476ee592409b462714"
            },
            "downloads": -1,
            "filename": "ssec-4.2.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "babc86850e5b76aa2d4b3faf33b4cfca",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 10531,
            "upload_time": "2025-03-20T14:40:47",
            "upload_time_iso_8601": "2025-03-20T14:40:47.739995Z",
            "url": "https://files.pythonhosted.org/packages/17/63/1537b7c8e1fc445490c40bbc26bf56020eeb4394340adab264ad6be5d8c6/ssec-4.2.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "a8a48d48a287f768952ab8a846a9cbebb61de920273ff073f1268f8d7466e5b5",
                "md5": "a36778da86a786e41630d12eb8806060",
                "sha256": "19b0005216c33a51b85065fd5e807156c7f18b93eb6198ef0b5915365b1323e3"
            },
            "downloads": -1,
            "filename": "ssec-4.2.2.tar.gz",
            "has_sig": false,
            "md5_digest": "a36778da86a786e41630d12eb8806060",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 11630,
            "upload_time": "2025-03-20T14:40:50",
            "upload_time_iso_8601": "2025-03-20T14:40:50.788705Z",
            "url": "https://files.pythonhosted.org/packages/a8/a4/8d48a287f768952ab8a846a9cbebb61de920273ff073f1268f8d7466e5b5/ssec-4.2.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-03-20 14:40:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ReMi-HSBI",
    "github_project": "ssec",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "ssec"
}
        
Elapsed time: 1.40062s