Skip to main content

Bake your dependencies stupidly simple!

Project description

fresh-bakery

🍰 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.
  • Mypy compatible (no probably need for # type: ignore).
  • FastAPI fully compatible.
  • Litestar compatible.
  • Pytest fully compatible (Fresh Bakery encourages the use of pytest).
  • 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:

  1. _file_obj and file_obj objects. Do we need them both?
  2. Unused write_1_bytes and write_2_bytes objects. 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 Cake for _file_obj initiates FileWrapper object, i.e. calls __init__ method;
  • the second Cake for file_obj calls 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, and world

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fresh_bakery-0.4.4.tar.gz (17.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fresh_bakery-0.4.4-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

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

Hashes for fresh_bakery-0.4.4.tar.gz
Algorithm Hash digest
SHA256 b151eb6ddbb86996fc86b6dc5d91d44604218ec1b6b620b4c4221be1dcad844b
MD5 9c764709a38a9f512d967659fcba821e
BLAKE2b-256 91eb11542a5119957566a2c6c9b90e34648b01c43c63502f160c08122dbf17e3

See more details on using hashes here.

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

Hashes for fresh_bakery-0.4.4-py3-none-any.whl
Algorithm Hash digest
SHA256 276b6655313690cd2b8376fc447849f27a730359fdaa5d4de54f21bc5667af07
MD5 2b34682c93098642919a1750fac78582
BLAKE2b-256 6dd918d314bbe194cab9bfb5f253ac36655dc44dbb4680001b23f0a32a9f167a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page