sse-starlette


Namesse-starlette JSON
Version 2.0.0 PyPI version JSON
download
home_page
SummarySSE plugin for Starlette
upload_time2024-01-25 18:48:07
maintainer
docs_urlNone
author
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/)

[![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"}),
    )
```

### 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 pipenv: `pip install pipenv`
2. install dependencies using pipenv: `pipenv install --dev -e .`
3. To run tests, either:
   - `pipenv run pytest`
 
### Makefile
- make sure your virtualenv is active: `pipenv shell`
- check `Makefile` for available commands and development support, e.g. run the unit tests:
```python
make test
```

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": "",
    "name": "sse-starlette",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "",
    "author": "",
    "author_email": "sysid <sysid@gmx.de>",
    "download_url": "https://files.pythonhosted.org/packages/3c/54/9651ca960c0fbb997fb2a69dc01b29b182c643eb42a59b82623f7716054a/sse_starlette-2.0.0.tar.gz",
    "platform": null,
    "description": "# Server Sent Events for [Starlette](https://github.com/encode/starlette) and [FastAPI](https://fastapi.tiangolo.com/)\n\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\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\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 pipenv: `pip install pipenv`\n2. install dependencies using pipenv: `pipenv install --dev -e .`\n3. To run tests, either:\n   - `pipenv run pytest`\n \n### Makefile\n- make sure your virtualenv is active: `pipenv shell`\n- check `Makefile` for available commands and development support, e.g. run the unit tests:\n```python\nmake test\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.0.0",
    "project_urls": {
        "Source": "https://github.com/sysid/sse-starlette"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5004b1569f95ce91fbfe35e5e6b3b11e8373ffe4fa81e998818c8c995a873896",
                "md5": "f4b6f4d714493b5a2bd8ff7d86e6d860",
                "sha256": "c4dd134302cb9708d47cae23c365fe0a089aa2a875d2f887ac80f235a9ee5744"
            },
            "downloads": -1,
            "filename": "sse_starlette-2.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f4b6f4d714493b5a2bd8ff7d86e6d860",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 9032,
            "upload_time": "2024-01-25T18:48:00",
            "upload_time_iso_8601": "2024-01-25T18:48:00.488655Z",
            "url": "https://files.pythonhosted.org/packages/50/04/b1569f95ce91fbfe35e5e6b3b11e8373ffe4fa81e998818c8c995a873896/sse_starlette-2.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3c549651ca960c0fbb997fb2a69dc01b29b182c643eb42a59b82623f7716054a",
                "md5": "6aa9e5c64c3e428d606c3ac3eb768929",
                "sha256": "0c43cc43aca4884c88c8416b65777c4de874cc4773e6458d3579c0a353dc2fb7"
            },
            "downloads": -1,
            "filename": "sse_starlette-2.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "6aa9e5c64c3e428d606c3ac3eb768929",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 18235,
            "upload_time": "2024-01-25T18:48:07",
            "upload_time_iso_8601": "2024-01-25T18:48:07.273039Z",
            "url": "https://files.pythonhosted.org/packages/3c/54/9651ca960c0fbb997fb2a69dc01b29b182c643eb42a59b82623f7716054a/sse_starlette-2.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-25 18:48:07",
    "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.23571s