{"id":15965439,"url":"https://github.com/deknowny/middletools","last_synced_at":"2025-06-16T20:35:30.394Z","repository":{"id":57441585,"uuid":"388584049","full_name":"deknowny/middletools","owner":"deknowny","description":"This python library allows you integrate `async-await` middleware-based system to your project","archived":false,"fork":false,"pushed_at":"2021-08-02T19:04:01.000Z","size":48,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-14T13:46:04.258Z","etag":null,"topics":["base","create","library","middlewares","own","python","tools","utils"],"latest_commit_sha":null,"homepage":"","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/deknowny.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-07-22T20:06:37.000Z","updated_at":"2022-12-03T15:40:56.000Z","dependencies_parsed_at":"2022-09-06T02:41:48.013Z","dependency_job_id":null,"html_url":"https://github.com/deknowny/middletools","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deknowny%2Fmiddletools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deknowny%2Fmiddletools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deknowny%2Fmiddletools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deknowny%2Fmiddletools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deknowny","download_url":"https://codeload.github.com/deknowny/middletools/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247183958,"owners_count":20897682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["base","create","library","middlewares","own","python","tools","utils"],"created_at":"2024-10-07T17:41:32.166Z","updated_at":"2025-04-04T13:26:32.378Z","avatar_url":"https://github.com/deknowny.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Middletools\n[![Coverage Status](https://coveralls.io/repos/github/deknowny/middletools/badge.svg)](https://coveralls.io/github/deknowny/middletools)\n![Supported python version](https://img.shields.io/pypi/pyversions/middletools)\n![PyPI package version](https://img.shields.io/pypi/v/middletools)\n![Downloads](https://img.shields.io/pypi/dm/middletools)\n\n\nThis is a python library that allows you to integrate middlewares-based system to your project. It contains base tools for creating and running middlewares with `async-await` style\n\n```python\nimport asyncio\n\nimport middletools\n\n\nasync def middleware1(request, call_next):\n    print(f\"Run middleware1 (request: {request})\")\n    response = await call_next()\n    print(f\"End middleware1 (response: {response})\")\n    return response\n\n\nasync def middleware2(request, call_next):\n    print(f\"Run middleware2 (request: {request})\")\n    response = await call_next()\n    print(f\"End middleware2 (response: {response})\")\n    return response\n\n\nasync def main():\n    read_afterwords = await middletools.read_forewords(\n        middleware1, middleware2,\n        inbox_value=123  # Pass `request` value\n    )\n    print(\"Some other code...\")\n    await read_afterwords(456)  # Pass `response` value\n\n\nasyncio.run(main())\n```\nOutput is\n```\nRun middleware1 (request: 123)\nRun middleware2 (request: 123)\nSome other code...\nEnd middleware2 (response: 456)\nEnd middleware1 (response: 456)\n```\n\n\n## Installation\n### PyPI\n```shell\npython -m pip install middletools\n```\n### GitHub\n```shell\npython -m pip install https://github.com/deknowny/middlewares/archive/main.zip\n```\n\n## Usage\nThe main idea is give an ability just passing the middlewares and `inbox`/`outbox` payload values in a few methods instead of running and saving middlewares state by hand\n\nStandard case: a function runs RESTful API routers and requires a middleware that checks\na header in client's request\n\n***\nThere are 2 endpoints for an abstract `GET` and `POST` methods\n```python\n# Some abstract router\n@router.get(\"/\")\nasync def route_get(request):\n    return 200, {\"response\": \"some content\"}\n\n\n@router.post(\"/\")\nasync def route_post(request):\n    return 201, {\"response\": \"ok\"}\n\n```\n\nIn the core of web framework you used a function like this that just call all routers\n\n```python\nclass Router:\n    ...\n    ...\n    \n    async def call_routers(self, request):\n        for router in self.routers:\n            ... # Pass request to routers and check it matched\n```\n\n`middlewares` library allows you easy integrate middleware system to your `call_routers`\n***\n### Create middleware function\n```python\nimport middletools\n\n...\n...\n\n# Adding a middleware handler to an abstract \n@router.add_middleware\nasync def my_middleware(\n    request: SomeRequest, call_next: middletools.types.CallNext\n) -\u003e SomeResponse:\n    # Just check if header exists, id not set the default value\n    if \"X-Required-Header\" not in request.headers:\n        request.header[\"X-Required-Header\"] = \"default\"\n    response = await call_next()\n    return response\n```\nHere we add a header to client request if clint didn't do it. Then `await call_next()` give control to other middlewares or to our `call_routers` handler and response from this is the value `call_next()` returns\n***\n`call_routers` should looks like this \n```python\nimport typing\n\nimport middletools\n\n\nclass Router:\n    # You can use generics to describe middleware hand;er\n    middlewares: typing.List[\n        middletools.types.MiddlewareHandler[\n            SomeRequest, SomeResponse\n        ]\n    ]\n    ...\n    ...\n\n    async def call_routers(self, request):\n        read_afterwords = await middletools.read_forewords(\n            *self.middlewares, inbox_value=request\n        )\n        for router in self.routers:\n            ... # Pass request to routers and check it matched\n            response = ...\n            await read_afterwords(response)\n            break\n        \n```\n`middlewares.read_forewords` run middlewares until every one of them give control with `await call_next()`.\nWhen we do all our stuff and get the router response we can call `await read_afterwords(response)` and run all middlewares completely.\n\n### Notes\nIf a middleware doesn't call `call_next()` it raises `middlewares.CallNextNotUsedError`. It means that the middleware forcibly decline middlewares handlers and response should be sent immediately without routers running. `call_routers` should looks like this:\n```python\nimport middletools\n\n\nasync def call_routers(self, request):\n    try:\n        read_afterwords = await middletools.read_forewords(\n            *self.middlewares, inbox_value=request\n        )\n        for router in self.routers:\n            ... # Pass request to routers and check it matched\n            response = ...\n            await read_afterwords(response)\n            return response\n    except middletools.CallNextNotUsedError:\n        return SomeBadResponseBecauseNotRouted(400, \"Require a header!\")\n    \n```\n***\nIf a middleware doesn't return anything, middlewares dispatching declined forcibly too but after routers handled. (Return nothing means there isn't any `return` or `return None` used). It raises `middlewares.NothingReturnedError`\n```python\nimport middletools\n\n\nasync def call_routers(self, request):\n    try:\n        read_afterwords = await middletools.read_forewords(\n            *self.middlewares, inbox_value=request\n        )\n        for router in self.routers:\n            ... # Pass request to routers and check it matched\n            response = ...\n            await read_afterwords(response)\n            return response\n    except middletools.CallNextNotUsedError:\n        return SomeBadResponseBecauseNotRouted(400, \"Require a header!\")\n    except middletools.NothingReturnedError:\n        return SomeBadResponseBecauseMiddlewareDntReturnResponse(\n            500, \"Oops, internal server error\"\n        )\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeknowny%2Fmiddletools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeknowny%2Fmiddletools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeknowny%2Fmiddletools/lists"}