{"id":13468707,"url":"https://github.com/nggit/tremolo","last_synced_at":"2026-03-06T15:32:19.692Z","repository":{"id":113589606,"uuid":"596137881","full_name":"nggit/tremolo","owner":"nggit","description":"A Swiss Army knife, crash-free ASGI server and web framework for handling large file uploads. Zero-dependency.","archived":false,"fork":false,"pushed_at":"2026-02-25T07:43:24.000Z","size":697,"stargazers_count":125,"open_issues_count":2,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-25T09:50:42.387Z","etag":null,"topics":["asgi","asgi-server","asyncio","http-server","microframework","minimalism","python","python3","uvloop","web-framework","websocket","websocket-server","zero-dependency"],"latest_commit_sha":null,"homepage":"https://nggit.github.io/tremolo-docs/","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/nggit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-02-01T14:48:19.000Z","updated_at":"2026-02-25T04:02:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"f48e011c-6a01-4ad7-b13b-3920d81238c4","html_url":"https://github.com/nggit/tremolo","commit_stats":{"total_commits":55,"total_committers":2,"mean_commits":27.5,"dds":"0.018181818181818188","last_synced_commit":"b2d3ca608a619230d470e049e64578b2574d51a7"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/nggit/tremolo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nggit%2Ftremolo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nggit%2Ftremolo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nggit%2Ftremolo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nggit%2Ftremolo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nggit","download_url":"https://codeload.github.com/nggit/tremolo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nggit%2Ftremolo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30183472,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T14:42:24.748Z","status":"ssl_error","status_checked_at":"2026-03-06T14:42:14.925Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["asgi","asgi-server","asyncio","http-server","microframework","minimalism","python","python3","uvloop","web-framework","websocket","websocket-server","zero-dependency"],"created_at":"2024-07-31T15:01:17.164Z","updated_at":"2026-03-06T15:32:19.673Z","avatar_url":"https://github.com/nggit.png","language":"Python","readme":"# Tremolo\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=nggit_tremolo\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=nggit_tremolo)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=nggit_tremolo\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=nggit_tremolo)\n\nTremolo is a [stream-oriented](https://nggit.github.io/tremolo-docs/yield.html), asynchronous, programmable HTTP server written in pure Python. It can also serve as an [ASGI server](#asgi-server).\n\nTremolo provides a common routing functionality to some unique features such as download/upload speed limiters, etc. While maintaining its simplicity and performance.\n\nBeing built with a stream in mind, Tremolo tends to use `yield` instead of `return` in route handlers.\n\n```python\n@app.route('/hello')\nasync def hello_world(**server):\n    yield b'Hello '\n    yield b'world!'\n```\n\nYou can take advantage of this to serve/generate big files efficiently:\n\n```python\n@app.route('/my/url/speedtest.bin')\nasync def my_big_data(request, response):\n    buffer_size = 16384\n\n    response.set_content_type('application/octet-stream')\n\n    with open('/dev/random', 'rb') as f:\n        chunk = True\n\n        while chunk:\n            chunk = f.read(buffer_size)\n            yield chunk\n```\n\nAnd other use cases…\n\n## Features\nTremolo is only suitable for those who value [minimalism](https://en.wikipedia.org/wiki/Minimalism_%28computing%29) and stability over features.\n\nWith only **3k** lines of code, with **no dependencies** other than the [Python Standard Library](https://docs.python.org/3/library/index.html), it gives you:\n\n* HTTP/1.x with [WebSocket support](https://nggit.github.io/tremolo-docs/websocket.html)\n* Keep-Alive connections with [configurable limit](https://nggit.github.io/tremolo-docs/configuration.html#keepalive_connections)\n* Stream chunked uploads\n* [Stream multipart uploads](https://nggit.github.io/tremolo-docs/body.html#multipart)\n* Download/upload speed throttling\n* [Resumable downloads](https://nggit.github.io/tremolo-docs/resumable-downloads.html)\n* Framework features; routing, middleware, etc.\n* ASGI server\n* PyPy compatible\n\n## Installation\n```\npython3 -m pip install --upgrade tremolo\n```\n\n## Example\nHere is a complete *hello world* example in case you missed the usual `return`.\n\n```python\nfrom tremolo import Application\n\napp = Application()\n\n@app.route('/hello')\nasync def hello_world(**server):\n    return 'Hello world!', 'latin-1'\n\n\nif __name__ == '__main__':\n    app.run('0.0.0.0', 8000, debug=True)\n```\n\nWell, `latin-1` on the right side is not required. The default is `utf-8`.\n\nYou can save it as `hello.py` and just run it with `python3 hello.py`.\nAnd your first *hello world* page with Tremolo will be at http://localhost:8000/hello.\n\n## ASGI Server\nTremolo is an HTTP Server framework. You can build abstractions on top of it, say an ASGI server.\n\nIn fact, Tremolo already has ASGI server (plus WebSocket) implementation.\nSo you can immediately use existing [ASGI applications / frameworks](https://asgi.readthedocs.io/en/latest/implementations.html#application-frameworks), on top of Tremolo (ASGI server).\n\nFor example, If a minimal ASGI application with the name `example.py`:\n\n```python\nasync def app(scope, receive, send):\n    assert scope['type'] == 'http'\n\n    await send({\n        'type': 'http.response.start',\n        'status': 200,\n        'headers': [\n            (b'content-type', b'text/plain')\n        ]\n    })\n    await send({\n        'type': 'http.response.body',\n        'body': b'Hello, World!'\n    })\n```\n\nThen you can run as follows:\n\n```\npython3 -m tremolo --debug --bind 127.0.0.1:8000 example:app\n```\n\nTo see more available options:\n\n```\npython3 -m tremolo --help\n```\n\nIt's also possible to run the ASGI server programmatically ([example with uvloop](https://github.com/nggit/tremolo/blob/main/example_uvloop.py)):\n\n```\npython3 example_uvloop.py\n```\n\n## Experimental Features\nExperimental features can be enabled with `experimental=True` or `--experimental`.\nSince they require user awareness, they are not enabled by default.\n\nFor example, even in ASGI server mode, Tremolo gives apps direct access to the server objects.\nWhich means that even if you use an app/framework like Starlette/FastAPI,\nyou can still use Tremolo's `request` and `response` objects for more optimized [streaming features](https://nggit.github.io/tremolo-docs/body.html#multipart).\n```python\nfrom starlette.applications import Starlette\nfrom starlette.routing import Route\n\n\nasync def homepage(request):\n    # Tremolo's `request` and `response` objects\n    req = request.state.server['request']\n    res = request.state.server['response']\n\n    async for data in req.stream():\n        await res.write(data)\n\n    await res.end()\n\n\nroutes = [\n    Route('/', homepage, methods=['GET', 'POST']),\n]\n\napp = Starlette(routes=routes)\n```\n\n## Testing\nJust run `python3 alltests.py` for all tests. Or individual *test_\\*.py* in the [tests/](https://github.com/nggit/tremolo/tree/main/tests) folder, for example `python3 tests/test_cli.py`.\n\nIf you also want measurements with [coverage](https://coverage.readthedocs.io/):\n\n```\ncoverage run alltests.py\ncoverage combine\ncoverage report\ncoverage html # to generate html reports\n```\n\n## Benchmarking\nThe first thing to note is that Tremolo is a pure Python server framework.\n\nAs a pure Python server framework, it is hard to find a comparison.\nBecause most servers/frameworks today are full of steroids like `httptools`, `uvloop`, Rust, etc.\n\nYou can try comparing with [Uvicorn](https://www.uvicorn.org/) with the following option (disabling steroids to be fair):\n\n```\nuvicorn --loop asyncio --http h11 --log-level error example:app\n```\n\nvs\n\n```\npython3 -m tremolo --log-level ERROR example:app\n```\n\nYou will find that Tremolo is reasonably fast.\n\nIf it's not, it could be due to [--upload-rate](https://nggit.github.io/tremolo-docs/configuration.html#upload_rate) or [--download-rate](https://nggit.github.io/tremolo-docs/configuration.html#download_rate) limits, which take effect when the payload is slightly larger.\nDespite causing benchmarks to show poor results, it prevents any single client from monopolizing bandwidth, ensuring responsiveness under heavy load.\n\nHowever, it should be noted that bottlenecks often occur on the application side.\nWhich means that in real-world usage, throughput reflects more on the application than the server.\n\n## Misc.\nTremolo utilizes `SO_REUSEPORT` (Linux 3.9+) to load balance worker processes.\n\n```python\napp.run('0.0.0.0', 8000, worker_num=2)\n```\n\nTremolo can also listen to multiple ports in case you are using an external load balancer like Nginx / HAProxy.\n\n```python\napp.listen(8001)\napp.listen(8002)\n\napp.run('0.0.0.0', 8000)\n```\n\nYou can even get higher concurrency with [PyPy](https://www.pypy.org/) or [uvloop](https://magic.io/blog/uvloop-blazing-fast-python-networking/):\n\n```\npython3 -m tremolo --loop uvloop --log-level ERROR example:app\n```\n\nSee: [--loop](https://nggit.github.io/tremolo-docs/configuration.html#loop)\n\n## License\nMIT License\n","funding_links":[],"categories":["Python","Members"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnggit%2Ftremolo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnggit%2Ftremolo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnggit%2Ftremolo/lists"}