Intro Example
flexdi offers the FlexGraph, used to manage dependencies and invoke callables.
The graph is a representation of what dependencies exist, and what providers can be
used to fulfil them.
When determining dependencies for a callable, flexdi will examine the type
annotations of the arguments to populate the graph with dependencies that can
satisfy the callable. A callable can be anything from a class, to functions,
class methods, generators, etc.
flexdi allows binding helper functions to the graph as a providers of types,
determined by their return annotations.
Bindings can themselves be injected with dependencies,
determined by their parameter annotations.
A Binding can be a simple function or a generator with custom teardown logic.
from typing import Iterator
from sqlalchemy import Engine, create_engine, text
from sqlalchemy.orm import Session
from flexdi import FlexGraph
# The FlexGraph keeps track of what dependencies different
# providers require, and will later be used to resolve them.
graph = FlexGraph()
# Let's add a binding for an Engine.
# The binding will be used for anything that requires an Engine.
# FlexGraph uses the return type annotation to create bindings.
@graph.bind
def provide_engine() -> Engine:
return create_engine("sqlite:///mydb.db")
# Generator responses can also be used. e.g.
# - A function returning Iterator[T] binds to T
# - A function returning Generator[T, U, V] binds to T
# - A function returning AsyncIterator[T] binds to T
# - A function returning AsyncGenerator[T, U] binds to T
@graph.bind
def provide_connection(engine: Engine) -> Iterator[Session]:
with Session(engine) as session:
yield session
# An entrypoint is a convenience method for a creating no argument
# version of a function or coroutine. You should typically only
# have one entrypoint used in your applications.
@graph.entrypoint
def main(conn: Session) -> None:
statement = text("SELECT name FROM sqlite_master;")
for [table_name] in conn.execute(statement):
print(table_name)
# Notice that we call main with no arguments!
if __name__ == "__main__":
main()
The manual effort of linking the dependencies together is removed for the developer. They are free to focus on creating the simple components which perform the actual interesting aspects of their application.