sse-starlette


Namesse-starlette JSON
Version 2.1.3 PyPI version JSON
download
home_pageNone
SummarySSE plugin for Starlette
upload_time2024-08-01 08:52:50
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseBSD-3-Clause
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Server Sent Events for [Starlette](https://github.com/encode/starlette) and [FastAPI](https://fastapi.tiangolo.com/)

[![Downloads](https://static.pepy.tech/badge/sse-starlette/week)](https://pepy.tech/project/sse-starlette)
[![PyPI Version][pypi-image]][pypi-url]
[![Build Status][build-image]][build-url]
[![Code Coverage][coverage-image]][coverage-url]

> Implements the [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) specification.

Background: https://sysid.github.io/server-sent-events/

Installation:

```shell
pip install sse-starlette
```

Usage:

```python
import asyncio
import uvicorn
from starlette.applications import Starlette
from starlette.routing import Route
from sse_starlette.sse import EventSourceResponse

async def numbers(minimum, maximum):
    for i in range(minimum, maximum + 1):
        await asyncio.sleep(0.9)
        yield dict(data=i)

async def sse(request):
    generator = numbers(1, 5)
    return EventSourceResponse(generator)

routes = [
    Route("/", endpoint=sse)
]

app = Starlette(debug=True, routes=routes)

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000, log_level='info')
```

Output:
![output](output.png)

**Caveat:** SSE streaming does not work in combination with [GZipMiddleware](https://github.com/encode/starlette/issues/20#issuecomment-704106436).

Be aware that for proper server shutdown your application must stop all
running tasks (generators). Otherwise you might experience the following warnings
at shutdown: `Waiting for background tasks to complete. (CTRL+C to force quit)`.

Client disconnects need to be handled in your Request handler (see example.py):
```python
async def endless(req: Request):
    async def event_publisher():
        i = 0
        try:
          while True:
              i += 1
              yield dict(data=i)
              await asyncio.sleep(0.2)
        except asyncio.CancelledError as e:
          _log.info(f"Disconnected from client (via refresh/close) {req.client}")
          # Do any other cleanup, if any
          raise e
    return EventSourceResponse(event_publisher())
```

## Special use cases
### Customize Ping
By default, the server sends a ping every 15 seconds. You can customize this by:
1. setting the `ping` parameter
2. by changing the `ping` event to a comment event so that it is not visible to the client
```python
@router.get("")
async def handle():
    generator = numbers(1, 100)
    return EventSourceResponse(
        generator,
        headers={"Server": "nini"},
        ping=5,
        ping_message_factory=lambda: ServerSentEvent(**{"comment": "You can't see\r\nthis ping"}),
    )
```
### SSE Send Timeout
To avoid 'hanging' connections in case HTTP connection from a certain client was kept open, but the client
stopped reading from the connection you can specifiy a send timeout (see
[#89](https://github.com/sysid/sse-starlette/issues/89)).
```python
EventSourceResponse(..., send_timeout=5)  # terminate hanging send call after 5s
```

### Fan out Proxies
Fan out proxies usually rely on response being cacheable. To support that, you can set the value of `Cache-Control`.
For example:
```python
return EventSourceResponse(
        generator(), headers={"Cache-Control": "public, max-age=29"}
    )
```
### Error Handling
See example: `examples/error_handling.py`

### Sending Responses without Async Generators
Async generators can expose tricky error and cleanup behavior especially when they are interrupted.

[Background: Cleanup in async generators](https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#cleanup-in-generators-and-async-generators).

Example [`no_async_generators.py`](https://github.com/sysid/sse-starlette/pull/56#issue-1704495339) shows an alternative implementation
that does not rely on async generators but instead uses memory channels (`examples/no_async_generators.py`).


## Development, Contributing
1. install pdm: `pip install pdm`
2. install dependencies using pipenv: `pdm install -d`.
3. To run tests:

### Makefile
- make sure your virtualenv is active
- check `Makefile` for available commands and development support, e.g. run the unit tests:
```shell
make test
make tox
```

For integration testing you can use the provided examples in `tests` and `examples`.

If you are using Postman, please see: https://github.com/sysid/sse-starlette/issues/47#issuecomment-1445953826


<!-- Badges -->

[pypi-image]: https://badge.fury.io/py/sse-starlette.svg
[pypi-url]: https://pypi.org/project/sse-starlette/
[build-image]: https://github.com/sysid/sse-starlette/actions/workflows/build.yml/badge.svg
[build-url]: https://github.com/sysid/sse-starlette/actions/workflows/build.yml
[coverage-image]: https://codecov.io/gh/sysid/sse-starlette/branch/master/graph/badge.svg
[coverage-url]: https://codecov.io/gh/sysid/sse-starlette

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "sse-starlette",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": null,
    "author": null,
    "author_email": "sysid <sysid@gmx.de>",
    "download_url": "https://files.pythonhosted.org/packages/72/fc/56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496/sse_starlette-2.1.3.tar.gz",
    "platform": null,
    "description": "# Server Sent Events for [Starlette](https://github.com/encode/starlette) and [FastAPI](https://fastapi.tiangolo.com/)\n\n[![Downloads](https://static.pepy.tech/badge/sse-starlette/week)](https://pepy.tech/project/sse-starlette)\n[![PyPI Version][pypi-image]][pypi-url]\n[![Build Status][build-image]][build-url]\n[![Code Coverage][coverage-image]][coverage-url]\n\n> Implements the [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) specification.\n\nBackground: https://sysid.github.io/server-sent-events/\n\nInstallation:\n\n```shell\npip install sse-starlette\n```\n\nUsage:\n\n```python\nimport asyncio\nimport uvicorn\nfrom starlette.applications import Starlette\nfrom starlette.routing import Route\nfrom sse_starlette.sse import EventSourceResponse\n\nasync def numbers(minimum, maximum):\n    for i in range(minimum, maximum + 1):\n        await asyncio.sleep(0.9)\n        yield dict(data=i)\n\nasync def sse(request):\n    generator = numbers(1, 5)\n    return EventSourceResponse(generator)\n\nroutes = [\n    Route(\"/\", endpoint=sse)\n]\n\napp = Starlette(debug=True, routes=routes)\n\nif __name__ == \"__main__\":\n    uvicorn.run(app, host=\"0.0.0.0\", port=8000, log_level='info')\n```\n\nOutput:\n![output](output.png)\n\n**Caveat:** SSE streaming does not work in combination with [GZipMiddleware](https://github.com/encode/starlette/issues/20#issuecomment-704106436).\n\nBe aware that for proper server shutdown your application must stop all\nrunning tasks (generators). Otherwise you might experience the following warnings\nat shutdown: `Waiting for background tasks to complete. (CTRL+C to force quit)`.\n\nClient disconnects need to be handled in your Request handler (see example.py):\n```python\nasync def endless(req: Request):\n    async def event_publisher():\n        i = 0\n        try:\n          while True:\n              i += 1\n              yield dict(data=i)\n              await asyncio.sleep(0.2)\n        except asyncio.CancelledError as e:\n          _log.info(f\"Disconnected from client (via refresh/close) {req.client}\")\n          # Do any other cleanup, if any\n          raise e\n    return EventSourceResponse(event_publisher())\n```\n\n## Special use cases\n### Customize Ping\nBy default, the server sends a ping every 15 seconds. You can customize this by:\n1. setting the `ping` parameter\n2. by changing the `ping` event to a comment event so that it is not visible to the client\n```python\n@router.get(\"\")\nasync def handle():\n    generator = numbers(1, 100)\n    return EventSourceResponse(\n        generator,\n        headers={\"Server\": \"nini\"},\n        ping=5,\n        ping_message_factory=lambda: ServerSentEvent(**{\"comment\": \"You can't see\\r\\nthis ping\"}),\n    )\n```\n### SSE Send Timeout\nTo avoid 'hanging' connections in case HTTP connection from a certain client was kept open, but the client\nstopped reading from the connection you can specifiy a send timeout (see\n[#89](https://github.com/sysid/sse-starlette/issues/89)).\n```python\nEventSourceResponse(..., send_timeout=5)  # terminate hanging send call after 5s\n```\n\n### Fan out Proxies\nFan out proxies usually rely on response being cacheable. To support that, you can set the value of `Cache-Control`.\nFor example:\n```python\nreturn EventSourceResponse(\n        generator(), headers={\"Cache-Control\": \"public, max-age=29\"}\n    )\n```\n### Error Handling\nSee example: `examples/error_handling.py`\n\n### Sending Responses without Async Generators\nAsync generators can expose tricky error and cleanup behavior especially when they are interrupted.\n\n[Background: Cleanup in async generators](https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#cleanup-in-generators-and-async-generators).\n\nExample [`no_async_generators.py`](https://github.com/sysid/sse-starlette/pull/56#issue-1704495339) shows an alternative implementation\nthat does not rely on async generators but instead uses memory channels (`examples/no_async_generators.py`).\n\n\n## Development, Contributing\n1. install pdm: `pip install pdm`\n2. install dependencies using pipenv: `pdm install -d`.\n3. To run tests:\n\n### Makefile\n- make sure your virtualenv is active\n- check `Makefile` for available commands and development support, e.g. run the unit tests:\n```shell\nmake test\nmake tox\n```\n\nFor integration testing you can use the provided examples in `tests` and `examples`.\n\nIf you are using Postman, please see: https://github.com/sysid/sse-starlette/issues/47#issuecomment-1445953826\n\n\n<!-- Badges -->\n\n[pypi-image]: https://badge.fury.io/py/sse-starlette.svg\n[pypi-url]: https://pypi.org/project/sse-starlette/\n[build-image]: https://github.com/sysid/sse-starlette/actions/workflows/build.yml/badge.svg\n[build-url]: https://github.com/sysid/sse-starlette/actions/workflows/build.yml\n[coverage-image]: https://codecov.io/gh/sysid/sse-starlette/branch/master/graph/badge.svg\n[coverage-url]: https://codecov.io/gh/sysid/sse-starlette\n",
    "bugtrack_url": null,
    "license": "BSD-3-Clause",
    "summary": "SSE plugin for Starlette",
    "version": "2.1.3",
    "project_urls": {
        "Source": "https://github.com/sysid/sse-starlette"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "52aa36b271bc4fa1d2796311ee7c7283a3a1c348bad426d37293609ca4300eef",
                "md5": "98c91b2a06d24a27123f22d72916a6f0",
                "sha256": "8ec846438b4665b9e8c560fcdea6bc8081a3abf7942faa95e5a744999d219772"
            },
            "downloads": -1,
            "filename": "sse_starlette-2.1.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "98c91b2a06d24a27123f22d72916a6f0",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 9383,
            "upload_time": "2024-08-01T08:52:48",
            "upload_time_iso_8601": "2024-08-01T08:52:48.659065Z",
            "url": "https://files.pythonhosted.org/packages/52/aa/36b271bc4fa1d2796311ee7c7283a3a1c348bad426d37293609ca4300eef/sse_starlette-2.1.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "72fc56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496",
                "md5": "7ed3e07a7580f56d0683ea2c20be3e8b",
                "sha256": "9cd27eb35319e1414e3d2558ee7414487f9529ce3b3cf9b21434fd110e017169"
            },
            "downloads": -1,
            "filename": "sse_starlette-2.1.3.tar.gz",
            "has_sig": false,
            "md5_digest": "7ed3e07a7580f56d0683ea2c20be3e8b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 19678,
            "upload_time": "2024-08-01T08:52:50",
            "upload_time_iso_8601": "2024-08-01T08:52:50.248882Z",
            "url": "https://files.pythonhosted.org/packages/72/fc/56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496/sse_starlette-2.1.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-01 08:52:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "sysid",
    "github_project": "sse-starlette",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "sse-starlette"
}
        
Elapsed time: 0.53433s