🍰 The little DI framework that tastes like a cake. 🍰
Documentation: https://fresh-bakery.readthedocs.io/en/latest/
Fresh Bakery
Fresh bakery is a lightweight [Dependency Injection][DI] framework/toolkit, which is ideal for building object dependencies in Python.
It is [fully] production-ready, and gives you the following:
- A lightweight, stupidly simple DI framework.
- Fully asynchronous, no synchronous mode.
- Any async backends compatible (
asyncio,trio). - Zero dependencies.
Mypycompatible (no probably need for# type: ignore).FastAPIfully compatible.Litestarcompatible.Pytestfully compatible (Fresh Bakery encourages the use ofpytest).- Ease of testing.
- Easily extended (contribution is welcome).
Requirements
Python 3.8+
Installation
$ pip3 install fresh-bakery
Examples
Quickstart
This 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:
from bakery import Bakery, Cake
def full_days_in(hours: int) -> float:
return hours / 24
def average(total: int, num: int) -> float:
return total / num
class WorkingBakery(Bakery):
average_hours: int = Cake(8)
week_hours: int = Cake(sum, [average_hours, average_hours, 7, 9, average_hours])
full_days: float = Cake(full_days_in, week_hours)
async def main() -> None:
async with WorkingBakery() as bakery:
assert bakery.week_hours == 40
assert bakery.full_days - 0.00001 < full_days_in(40)
assert int(bakery.average_hours) == 8
You can see it's as simple as it can be.
One more example
Let's suppose we have a thin wrapper around file object.
from typing import ClassVar, Final
from typing_extensions import Self
class FileWrapper:
file_opened: bool = False
write_lines: ClassVar[list[str]] = []
def __init__(self, filename: str) -> None:
self.filename: Final = filename
def write(self, line: str) -> int:
type(self).write_lines.append(line)
return len(line)
def __enter__(self) -> Self:
type(self).file_opened = True
return self
def __exit__(self, *_args: object) -> None:
type(self).file_opened = False
type(self).write_lines.clear()
This wrapper acts exactly like a file object: it can be opened, closed, and can write line to file.
Let's open file hello.txt, write 2 lines into it and close it. Let's do all this with the bakery syntax:
from bakery import Bakery, Cake
class FileBakery(Bakery):
_file_obj: FileWrapper = Cake(FileWrapper, "hello.txt")
file_obj: FileWrapper = Cake(_file_obj)
write_1_bytes: int = Cake(file_obj.write, "hello, ")
write_2_bytes: int = Cake(file_obj.write, "world")
async def main() -> None:
assert FileWrapper.file_opened is False
assert FileWrapper.write_lines == []
async with FileBakery() as bakery:
assert bakery.file_obj.filename == "hello.txt"
assert FileWrapper.file_opened is True
assert FileWrapper.write_lines == ["hello, ", "world"]
assert FileWrapper.file_opened is False
assert FileWrapper.write_lines == []
Maybe you noticed some strange things concerning FileBakery bakery:
_file_objandfile_objobjects. Do we need them both?- Unused
write_1_bytesandwrite_2_bytesobjects. Do we need them?
Let's try to fix both cases. First, let's figure out why do we need _file_obj and file_obj objects?
- The first
Cakefor_file_objinitiatesFileWrapperobject, i.e. calls__init__method; - the second
Cakeforfile_objcalls context-manager, i.e. calls__enter__method on enter and__exit__method on exit.
Actually, we can merge these two statements into single one:
# class FileBakery(Bakery):
file_obj: FileWrapper = Cake(Cake(FileWrapper, "hello.txt"))
So, 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:
# class FileBakery(Bakery):
strs_to_write: list[str] = Cake(["hello, ", "world"])
How to apply function to every string in this list? There are several ways to do it. One of them is built-in map function.
map_cake = Cake(map, file_obj.write, strs_to_write)
But map function returns iterator and we need to get elements from it. Built-in list function will do the job.
list_cake = Cake(list, map_cake)
In the same manner as we did for file_obj let's merge these two statements into one. The final FileBakery will look like this:
class FileBakeryMap(Bakery):
file_obj: FileWrapper = Cake(Cake(FileWrapper, "hello.txt"))
strs_to_write: list[str] = Cake(["hello, ", "world"])
_: list[int] = Cake(list, Cake(map, file_obj.write, strs_to_write))
The last thing nobody likes is hard-coded strings! In this case such strings are:
- the name of the file
hello.txt - list of strings to write:
hello,andworld
What if we've got another filename or other strings to write? Let's define filename and list of strings as FileBakery parameters:
from bakery import Bakery, Cake, __Cake__
class FileBakery(Bakery):
filename: str = __Cake__()
strs_to_write: list[str] = __Cake__()
file_obj: FileWrapper = Cake(Cake(FileWrapper, filename))
_: list[int] = Cake(list, Cake(map, file_obj.write, strs_to_write))
To define parameters you can use dunder-cake construction: __Cake__().
To pass arguments into FileBakery you can use native python syntax:
# async def main() -> None:
async with FileBakeryMapWithParams(
filename="hello.txt", strs_to_write=["hello, ", "world"]
) as bakery:
...
And the whole example will look like this:
from typing import ClassVar, Final
from typing_extensions import Self
from bakery import Bakery, Cake, __Cake__
# class FileWrapper: ...
class FileBakery(Bakery):
filename: str = __Cake__()
strs_to_write: list[str] = __Cake__()
file_obj: FileWrapper = Cake(Cake(FileWrapper, filename))
_: list[int] = Cake(list, Cake(map, file_obj.write, strs_to_write))
async def main() -> None:
assert FileWrapper.file_opened is False
assert FileWrapper.write_lines == []
async with FileBakeryMapWithParams(
filename="hello.txt", strs_to_write=["hello, ", "world"]
) as bakery:
assert bakery.file_obj.filename == "hello.txt"
assert FileWrapper.file_opened is True
assert FileWrapper.write_lines == ["hello, ", "world"]
assert FileWrapper.file_opened is False
assert FileWrapper.write_lines == []
More examples are presented in section bakery examples.
Dependencies
No dependencies ;)
Changelog
You can see the release history here: https://github.com/Mityuha/fresh-bakery/releases/
Fresh Bakery is MIT licensed code.
Metadata
Release files for fresh-bakery 0.4.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fresh_bakery-0.4.4.tar.gz | 17.7 kB | Without provenance | Details for fresh_bakery-0.4.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fresh_bakery-0.4.4-py3-none-any.whl | Python 3 | none | any | Details for fresh_bakery-0.4.4-py3-none-any.whl |
Total release size: 37.6 kB
Release files / fresh_bakery-0.4.4.tar.gz
| Download URL | fresh_bakery-0.4.4.tar.gz |
|---|---|
| Size | 17.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b151eb6ddbb86996fc86b6dc5d91d44604218ec1b6b620b4c4221be1dcad844b
|
|
BLAKE2b-256 checksum How to use checksums |
91eb11542a5119957566a2c6c9b90e34648b01c43c63502f160c08122dbf17e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.2 CPython/3.14.3 Linux/6.14.0-1017-azure
|
Release files / fresh_bakery-0.4.4-py3-none-any.whl
| Download URL | fresh_bakery-0.4.4-py3-none-any.whl |
|---|---|
| Size | 19.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
276b6655313690cd2b8376fc447849f27a730359fdaa5d4de54f21bc5667af07
|
|
BLAKE2b-256 checksum How to use checksums |
6dd918d314bbe194cab9bfb5f253ac36655dc44dbb4680001b23f0a32a9f167a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.2 CPython/3.14.3 Linux/6.14.0-1017-azure
|
Security for fresh-bakery 0.4.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.