{"id":13458929,"url":"https://github.com/Mityuha/fresh-bakery","last_synced_at":"2025-03-24T16:31:09.522Z","repository":{"id":58166337,"uuid":"529840352","full_name":"Mityuha/fresh-bakery","owner":"Mityuha","description":"Bake dependency injections asynchronously and stupidly simple ","archived":false,"fork":false,"pushed_at":"2024-10-24T12:21:50.000Z","size":307,"stargazers_count":21,"open_issues_count":8,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-10-25T12:43:27.021Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://fresh-bakery.readthedocs.io/en/latest/","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/Mityuha.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-28T11:20:41.000Z","updated_at":"2024-10-24T12:19:08.000Z","dependencies_parsed_at":"2023-10-10T21:15:01.678Z","dependency_job_id":"4cd99885-00a1-4fe1-b952-85d7da610509","html_url":"https://github.com/Mityuha/fresh-bakery","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mityuha%2Ffresh-bakery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mityuha%2Ffresh-bakery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mityuha%2Ffresh-bakery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mityuha%2Ffresh-bakery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Mityuha","download_url":"https://codeload.github.com/Mityuha/fresh-bakery/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245308490,"owners_count":20594260,"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":[],"created_at":"2024-07-31T09:00:59.883Z","updated_at":"2025-03-24T16:31:09.191Z","avatar_url":"https://github.com/Mityuha.png","language":"Python","funding_links":[],"categories":["Software"],"sub_categories":["DI Frameworks / Containers"],"readme":"\r\n\u003cp align=\"center\"\u003e\r\n  \u003ca href=\"https://fresh-bakery.readthedocs.io/en/latest/\"\u003e\u003cimg width=\"300px\" src=\"https://github.com/Mityuha/fresh-bakery/assets/17745407/9ad83683-03dc-43af-b66f-f8a010bde264\" alt='fresh-bakery'\u003e\u003c/a\u003e\r\n\u003c/p\u003e\r\n\u003cp align=\"center\"\u003e\r\n    \u003cem\u003e🍰 The little DI framework that tastes like a cake. 🍰\u003c/em\u003e\r\n\u003c/p\u003e\r\n\r\n---\r\n\r\n**Documentation**: [https://fresh-bakery.readthedocs.io/en/latest/](https://fresh-bakery.readthedocs.io/en/latest/)\r\n\r\n---\r\n\r\n# Fresh Bakery\r\n\r\nFresh bakery is a lightweight [Dependency Injection][DI] framework/toolkit,\r\nwhich is ideal for building object dependencies in Python.\r\n\r\nIt is [fully] production-ready, and gives you the following:\r\n\r\n* A lightweight, stupidly simple DI framework.\r\n* Fully asynchronous, no synchronous mode.\r\n* Any async backends compatible (`asyncio`, `trio`).\r\n* Zero dependencies.\r\n* `Mypy` compatible (no probably need for `# type: ignore`).\r\n* `FastAPI` fully compatible.\r\n* `Litestar` compatible.\r\n* `Pytest` fully compatible (Fresh Bakery encourages the use of `pytest`).\r\n* Ease of testing.\r\n* Easily extended (contribution is welcome).\r\n\r\n## Requirements\r\n\r\nPython 3.8+\r\n\r\n## Installation\r\n\r\n```shell\r\n$ pip3 install fresh-bakery\r\n```\r\n\r\n## Examples\r\n\r\n### Quickstart\r\nThis example is intended to show the nature of Dependency Injection and the ease of use the library. Many of us work 8 hours per day on average, 5 days a week, i.e. ~ 40 hours per week. Let's describe it using DI and bakery:\r\n```python\r\nfrom bakery import Bakery, Cake\r\n\r\n\r\ndef full_days_in(hours: int) -\u003e float:\r\n    return hours / 24\r\n\r\n\r\ndef average(total: int, num: int) -\u003e float:\r\n    return total / num\r\n\r\n\r\nclass WorkingBakery(Bakery):\r\n    average_hours: int = Cake(8)\r\n    week_hours: int = Cake(sum, [average_hours, average_hours, 7, 9, average_hours])\r\n    full_days: float = Cake(full_days_in, week_hours)\r\n\r\n\r\nasync def main() -\u003e None:\r\n    async with WorkingBakery() as bakery:\r\n        assert bakery.week_hours == 40\r\n        assert bakery.full_days - 0.00001 \u003c full_days_in(40)\r\n        assert int(bakery.average_hours) == 8\r\n```\r\nYou can see it's as simple as it can be.\r\n\r\n### One more example\r\nLet's suppose we have a thin wrapper around file object.\r\n```python\r\nfrom typing import ClassVar, Final\r\n\r\nfrom typing_extensions import Self\r\n\r\n\r\nclass FileWrapper:\r\n    file_opened: bool = False\r\n    write_lines: ClassVar[list[str]] = []\r\n\r\n    def __init__(self, filename: str) -\u003e None:\r\n        self.filename: Final = filename\r\n\r\n    def write(self, line: str) -\u003e int:\r\n        type(self).write_lines.append(line)\r\n        return len(line)\r\n\r\n    def __enter__(self) -\u003e Self:\r\n        type(self).file_opened = True\r\n        return self\r\n\r\n    def __exit__(self, *_args: object) -\u003e None:\r\n        type(self).file_opened = False\r\n        type(self).write_lines.clear()\r\n```\r\nThis wrapper acts exactly like a file object: it can be opened, closed, and can write line to file.\r\nLet's open file `hello.txt`, write 2 lines into it and close it. Let's do all this with the bakery syntax:\r\n```python\r\nfrom bakery import Bakery, Cake\r\n\r\n\r\nclass FileBakery(Bakery):\r\n    _file_obj: FileWrapper = Cake(FileWrapper, \"hello.txt\")\r\n    file_obj: FileWrapper = Cake(_file_obj)\r\n    write_1_bytes: int = Cake(file_obj.write, \"hello, \")\r\n    write_2_bytes: int = Cake(file_obj.write, \"world\")\r\n\r\n\r\nasync def main() -\u003e None:\r\n    assert FileWrapper.file_opened is False\r\n    assert FileWrapper.write_lines == []\r\n    async with FileBakery() as bakery:\r\n        assert bakery.file_obj.filename == \"hello.txt\"\r\n        assert FileWrapper.file_opened is True\r\n        assert FileWrapper.write_lines == [\"hello, \", \"world\"]\r\n\r\n    assert FileWrapper.file_opened is False\r\n    assert FileWrapper.write_lines == []\r\n```\r\nMaybe you noticed some strange things concerning `FileBakery` bakery:\r\n1. `_file_obj` and `file_obj` objects. Do we need them both?\r\n2. Unused `write_1_bytes` and `write_2_bytes` objects. Do we need them?\r\n\r\nLet's try to fix both cases. First, let's figure out why do we need `_file_obj` and `file_obj` objects?\r\n- The first `Cake` for `_file_obj` initiates `FileWrapper` object, i.e. calls `__init__` method;\r\n- the second `Cake` for `file_obj` calls context-manager, i.e. calls `__enter__` method on enter and `__exit__` method on exit.\r\n\r\nActually, we can merge these two statements into single one:\r\n```python\r\n# class FileBakery(Bakery):\r\n    file_obj: FileWrapper = Cake(Cake(FileWrapper, \"hello.txt\"))\r\n```\r\nSo, what about unused arguments? OK, let's re-write this gist a little bit. First, let's declare the list of strings we want to write:\r\n```python\r\n# class FileBakery(Bakery):\r\n    strs_to_write: list[str] = Cake([\"hello, \", \"world\"])\r\n```\r\nHow to apply function to every string in this list? There are several ways to do it. One of them is built-in [`map`](https://docs.python.org/3/library/functions.html#map) function.\r\n```python\r\nmap_cake = Cake(map, file_obj.write, strs_to_write)\r\n```\r\nBut `map` function returns iterator and we need to get elements from it. Built-in [`list`](https://docs.python.org/3/library/functions.html#func-list) function will do the job.\r\n```python\r\nlist_cake = Cake(list, map_cake)\r\n```\r\nIn the same manner as we did for `file_obj` let's merge these two statements into one. The final `FileBakery` will look like this:\r\n```python\r\nclass FileBakeryMap(Bakery):\r\n    file_obj: FileWrapper = Cake(Cake(FileWrapper, \"hello.txt\"))\r\n    strs_to_write: list[str] = Cake([\"hello, \", \"world\"])\r\n    _: list[int] = Cake(list, Cake(map, file_obj.write, strs_to_write))\r\n```\r\nThe last thing nobody likes is hard-coded strings! In this case such strings are:\r\n- the name of the file `hello.txt`\r\n- list of strings to write: `hello, ` and `world`\r\n\r\nWhat if we've got another filename or other strings to write? Let's define filename and list of strings as `FileBakery` parameters:\r\n```python\r\nfrom bakery import Bakery, Cake, __Cake__\r\n\r\n\r\nclass FileBakery(Bakery):\r\n    filename: str = __Cake__()\r\n    strs_to_write: list[str] = __Cake__()\r\n    file_obj: FileWrapper = Cake(Cake(FileWrapper, filename))\r\n    _: list[int] = Cake(list, Cake(map, file_obj.write, strs_to_write))\r\n```\r\nTo define parameters you can use dunder-cake construction: `__Cake__()`.   \r\nTo pass arguments into `FileBakery` you can use native python syntax:\r\n```python\r\n# async def main() -\u003e None:\r\n    async with FileBakeryMapWithParams(\r\n        filename=\"hello.txt\", strs_to_write=[\"hello, \", \"world\"]\r\n    ) as bakery:\r\n        ...\r\n```\r\nAnd the whole example will look like this:\r\n```python\r\nfrom typing import ClassVar, Final\r\n\r\nfrom typing_extensions import Self\r\n\r\nfrom bakery import Bakery, Cake, __Cake__\r\n\r\n\r\n# class FileWrapper: ...\r\n\r\n\r\nclass FileBakery(Bakery):\r\n    filename: str = __Cake__()\r\n    strs_to_write: list[str] = __Cake__()\r\n    file_obj: FileWrapper = Cake(Cake(FileWrapper, filename))\r\n    _: list[int] = Cake(list, Cake(map, file_obj.write, strs_to_write))\r\n\r\n\r\nasync def main() -\u003e None:\r\n    assert FileWrapper.file_opened is False\r\n    assert FileWrapper.write_lines == []\r\n    async with FileBakeryMapWithParams(\r\n        filename=\"hello.txt\", strs_to_write=[\"hello, \", \"world\"]\r\n    ) as bakery:\r\n        assert bakery.file_obj.filename == \"hello.txt\"\r\n        assert FileWrapper.file_opened is True\r\n        assert FileWrapper.write_lines == [\"hello, \", \"world\"]\r\n\r\n    assert FileWrapper.file_opened is False\r\n    assert FileWrapper.write_lines == []\r\n```\r\nMore examples are presented in section [bakery examples](https://fresh-bakery.readthedocs.io/en/latest/bakery_examples/).\r\n\r\n## Dependencies\r\n\r\nNo dependencies ;)\r\n\r\n## Changelog\r\nYou can see the release history here: https://github.com/Mityuha/fresh-bakery/releases/\r\n\r\n---\r\n\r\n\u003cp align=\"center\"\u003e\u003ci\u003eFresh Bakery is \u003ca href=\"https://github.com/Mityuha/fresh-bakery/blob/main/LICENSE\"\u003eMIT licensed\u003c/a\u003e code.\u003c/p\u003e\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMityuha%2Ffresh-bakery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMityuha%2Ffresh-bakery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMityuha%2Ffresh-bakery/lists"}