Bake your dependencies stupidly simple!
Project description
🍰 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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fresh_bakery-0.4.4.tar.gz.
File metadata
- Download URL: fresh_bakery-0.4.4.tar.gz
- Upload date:
- Size: 17.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.14.3 Linux/6.14.0-1017-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b151eb6ddbb86996fc86b6dc5d91d44604218ec1b6b620b4c4221be1dcad844b
|
|
| MD5 |
9c764709a38a9f512d967659fcba821e
|
|
| BLAKE2b-256 |
91eb11542a5119957566a2c6c9b90e34648b01c43c63502f160c08122dbf17e3
|
File details
Details for the file fresh_bakery-0.4.4-py3-none-any.whl.
File metadata
- Download URL: fresh_bakery-0.4.4-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.14.3 Linux/6.14.0-1017-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
276b6655313690cd2b8376fc447849f27a730359fdaa5d4de54f21bc5667af07
|
|
| MD5 |
2b34682c93098642919a1750fac78582
|
|
| BLAKE2b-256 |
6dd918d314bbe194cab9bfb5f253ac36655dc44dbb4680001b23f0a32a9f167a
|