### Example of Installing Configurations Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Demonstrates how to install a base configuration and then override specific bindings in a test configuration. The `allow_override` parameter is used to permit rebinding. ```python def base_config(binder): binder.bind(Database, ProductionDB()) binder.bind(Logger, RealLogger()) def test_config(binder): binder.install(base_config) binder.bind(Logger, MockLogger(), allow_override=True) inject.configure(test_config, allow_override=True) ``` -------------------------------- ### Basic Dependency Injection Setup Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Configure the injector with basic bindings for services like Database and Cache. This is a fundamental setup for most applications. ```python def config(binder): binder.bind(Database, Database('postgresql://localhost/mydb')) binder.bind(Cache, RedisCache('redis://localhost')) inject.configure(config) ``` -------------------------------- ### Minimal Configuration Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/examples.md Demonstrates the most basic setup for python-inject by binding a single instance and retrieving it. ```python import inject class Cache: def set(self, key, value): pass def get(self, key): pass def configure_app(): def config(binder): binder.bind(Cache, Cache()) inject.configure(config) configure_app() # Use injection cache = inject.instance(Cache) cache.set('key', 'value') ``` -------------------------------- ### Setup and Teardown for Testing Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/README.md Configure mock dependencies for testing and clear the injector after tests. ```python def setUp(self): inject.configure(lambda binder: ( binder.bind(Database, TestDatabase()), binder.bind(Cache, MockCache()), ), clear=True) def tearDown(self): inject.clear() ``` -------------------------------- ### Binder.install() Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Installs another configuration function into the current binder, merging its bindings. ```APIDOC ## Class: Binder ### Method: install(self, config: BinderCallable) -> Binder ### Description Installs another configuration function, merging its bindings. ### Parameters - **config** (`Callable[[Binder], Optional[Binder]]`) - Configuration function to install. ### Returns `Binder` - Self for chaining. ### Example ```python def base_config(binder): binder.bind(Database, ProductionDB()) binder.bind(Logger, RealLogger()) def test_config(binder): binder.install(base_config) binder.bind(Logger, MockLogger(), allow_override=True) inject.configure(test_config, allow_override=True) ``` ``` -------------------------------- ### Unittest Setup for Dependency Injection Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Set up and tear down the injector for unit tests using `setUp` and `tearDown` methods. This ensures a clean injection environment for each test. ```python def setUp(self): inject.configure(lambda b: b.bind(Cache, MockCache()), clear=True) def tearDown(self): inject.clear() ``` -------------------------------- ### Install Configuration Function in Binder Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Installs another configuration function, merging its bindings into the current binder. Useful for organizing configurations. ```python def install(self, config: BinderCallable) -> Binder ``` -------------------------------- ### Compose Multiple Configurations Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Demonstrates combining different configuration modules using binder.install(). This allows for modular and reusable configuration setups. ```python def logging_config(binder): binder.bind(Logger, RealLogger()) def cache_config(binder): binder.bind(Cache, RedisCache()) def database_config(binder): binder.bind(Database, PostgresDB()) def production_config(binder): binder.install(logging_config) binder.install(cache_config) binder.install(database_config) # Additional production bindings def test_config(binder): binder.install(logging_config) # Reuse logging binder.bind(Cache, MockCache()) # Override binder.bind(Database, TestDB()) # Override ``` -------------------------------- ### Provider Binding Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/types.md Shows how to bind a provider using `bind_to_provider`. This example uses `threading.local` to manage a thread-specific user and demonstrates binding a function that retrieves the current user. ```python import threading _local = threading.local() def get_current_user(): return getattr(_local, 'user', None) def config(binder): # Provider called for each injection binder.bind_to_provider(CurrentUser, get_current_user) ``` -------------------------------- ### Install python-inject Source: https://github.com/ivankorobkov/python-inject/blob/master/README.md Install the latest version of the python-inject library using pip. ```bash pip install inject ``` -------------------------------- ### Step-by-step Injector Example Source: https://github.com/ivankorobkov/python-inject/blob/master/README.md Demonstrates core python-inject features including instance retrieval, parameter injection, attribute injection, configuration, and usage. ```python # Import the inject module. import inject # `inject.instance` requests dependencies from the injector. def foo(bar): cache = inject.instance(Cache) cache.save('bar', bar) # `inject.params` injects dependencies as keyword arguments or positional argument. # Also you can use @inject.autoparams in Python 3.5, see the example above. @inject.params(cache=Cache, user=CurrentUser) def baz(foo, cache=None, user=None): cache.save('foo', foo, user) # this can be called in different ways: # with injected arguments baz('foo') # with positional arguments baz('foo', my_cache) # with keyword arguments baz('foo', my_cache, user=current_user) # `inject.param` is deprecated, use `inject.params` instead. @inject.param('cache', Cache) def bar(foo, cache=None): cache.save('foo', foo) # `inject.attr` creates properties (descriptors) which request dependencies on access. class User(object): cache = inject.attr(Cache) def __init__(self, id): self.id = id def save(self): self.cache.save('users', self) @classmethod def load(cls, id): return cls.cache.load('users', id) # Create an optional configuration. def my_config(binder): binder.bind(Cache, RedisCache('localhost:1234')) # Configure a shared injector. inject.configure(my_config) # Instantiate User as a normal class. Its `cache` dependency is injected when accessed. user = User(10) user.save() # Call the functions, the dependencies are automatically injected. foo('Hello') bar('world') ``` -------------------------------- ### Basic Dependency Injection Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/README.md Demonstrates defining services, configuring bindings, and using the @inject.autoparams decorator for dependency injection in Python. ```python import inject # 1. Define services class Cache: def set(self, key, value): pass class Database: def query(self, sql): pass # 2. Configure bindings def config(binder): binder.bind(Cache, Cache()) binder.bind(Database, Database()) inject.configure(config) # 3. Use injection @inject.autoparams def fetch_user(user_id: int, db: Database, cache: Cache): cached = cache.get(f'user:{user_id}') if cached: return cached user = db.query(f"SELECT * FROM users WHERE id = {user_id}") cache.set(f'user:{user_id}', user) return user # 4. Call without arguments user = fetch_user(42) ``` -------------------------------- ### Example BinderCallable Configurations Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/types.md Illustrates two ways to define configuration functions: one returning None implicitly and another using chaining for multiple bindings. ```python def my_config(binder: inject.Binder) -> None: binder.bind(Cache, RedisCache()) binder.bind(Database, PostgresDB()) # Return None implicitly def my_config_chained(binder: inject.Binder) -> inject.Binder: return binder.bind(Cache, RedisCache()) \ .bind(Database, PostgresDB()) inject.configure(my_config) ``` -------------------------------- ### Basic unittest Setup for Python-Inject Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Demonstrates how to set up and tear down python-inject configurations within a unittest.TestCase. Ensure to clear the injection context after each test. ```python import unittest import inject class MyTest(unittest.TestCase): def setUp(self): inject.configure(self.test_config, clear=True) def tearDown(self): inject.clear() def test_something(self): assert inject.instance(Service) is not None @staticmethod def test_config(binder): binder.bind(Cache, MockCache()) binder.bind(Database, TestDB()) ``` -------------------------------- ### Instance Binding Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/binding-types.md Demonstrates binding a class to a pre-created instance. Both calls to inject.instance return the exact same object. ```python import inject class RedisCache: def __init__(self, url: str): self.url = url self.client = redis.from_url(url) def config(binder): cache = RedisCache('redis://localhost:6379') binder.bind(RedisCache, cache) inject.configure(config) # Both calls return the exact same instance cache1 = inject.instance(RedisCache) cache2 = inject.instance(RedisCache) assert cache1 is cache2 # Same object in memory ``` -------------------------------- ### Basic Test Setup with Unittest Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/examples.md Sets up a basic test case using Python's unittest framework. It configures the injector with mock dependencies before each test and clears it afterward. ```python import unittest import inject class UserServiceTest(unittest.TestCase): def setUp(self): def test_config(binder): binder.bind(Database, MockDatabase()) binder.bind(Cache, MockCache()) binder.bind(Email, MockEmail()) inject.configure(test_config, clear=True) def tearDown(self): inject.clear() def test_create_user(self): service = UserService() user = service.create_user('alice@example.com') self.assertEqual(user.email, 'alice@example.com') ``` -------------------------------- ### Binding Storage Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/architecture.md Demonstrates how different types of bindings (instance, constructor, provider, string key, tuple key) are stored as zero-argument callables in the _bindings dictionary. ```python _bindings: dict[Binding, Callable[[], Any]] = { Cache: lambda: redis_instance, # Instance binding Database: _ConstructorBinding(lambda: Database(...)), # Constructor Logger: lambda: ThreadLocal.logger, # Provider 'api_key': lambda: 'secret', # String key ('cache', 'redis'): lambda: redis_inst, # Tuple key } ``` -------------------------------- ### Constructor Binding Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/binding-types.md Shows how a class is bound to a constructor that creates a singleton on first injection. The instance is cached and reused. ```python import inject class DatabaseConnection: def __init__(self, url: str): self.url = url print(f"Connecting to {url}") self.conn = psycopg2.connect(url) def config(binder): def create_db(): return DatabaseConnection('postgresql://localhost/mydb') binder.bind_to_constructor(DatabaseConnection, create_db) inject.configure(config) # First call: creates instance, prints "Connecting to..." db1 = inject.instance(DatabaseConnection) # Second call: returns cached instance db2 = inject.instance(DatabaseConnection) assert db1 is db2 # Same instance ``` -------------------------------- ### Example of Binding Instances Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Shows how to bind different types of keys, including classes, string literals, and integers, to specific instances. This is useful for providing concrete implementations or configuration values. ```python def config(binder): redis = RedisCache('localhost:6379') binder.bind(Cache, redis) binder.bind('db_host', 'localhost') binder.bind('db_port', 5432) ``` -------------------------------- ### Example: Thread-Local Current User Binding Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/binding-types.md Demonstrates binding a thread-local object using a provider. Each thread can have its own instance of CurrentUser. ```python import threading import inject _local = threading.local() class CurrentUser: def __init__(self, user_id: int): self.user_id = user_id def get_current_user(): return getattr(_local, 'user', None) def config(binder): binder.bind_to_provider(CurrentUser, get_current_user) inject.configure(config) def set_user(user_id: int): _local.user = CurrentUser(user_id) # Different threads have different current users def worker(user_id: int): set_user(user_id) user = inject.instance(CurrentUser) print(f"Thread sees user {user.user_id}") import threading threads = [ threading.Thread(target=worker, args=(1,)), threading.Thread(target=worker, args=(2,)), ] for t in threads: t.start() for t in threads: t.join() ``` -------------------------------- ### Constructor Binding Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/types.md Demonstrates how to use `bind_to_constructor` with both simple callables and context managers. Ensure the necessary classes like Cache, RedisCache, DatabaseConnection, and Database are defined elsewhere. ```python def config(binder): # Simple constructor binder.bind_to_constructor( Cache, lambda: RedisCache('localhost:6379') ) # Context manager constructor @contextlib.contextmanager def get_db_connection(): conn = DatabaseConnection() yield conn conn.close() binder.bind_to_constructor(Database, get_db_connection) ``` -------------------------------- ### Example of Forward Reference Binding Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/architecture.md Demonstrates how to bind a string reference to an instance using the binder. This implicitly also binds the ForwardRef version. ```python def config(binder): binder.bind('MyClass', my_instance) # Also binds: ForwardRef('MyClass') -> my_instance ``` -------------------------------- ### Documentation Structure Overview Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/index.md Visualizes the main documentation entry points and their relationships, showing how different guides and references connect. ```text README (entry point) ├── Quick Reference Summary (condensed API) ├── Configuration Guide (setup) │ ├── Binding Types (detailed) │ └── Examples (patterns) ├── Decorators (usage) │ └── Examples (patterns) ├── Best Practices (guidelines) │ └── Examples (patterns) ├── Architecture (implementation) │ └── Injector Lifecycle (state) ├── Errors (troubleshooting) ├── Types (reference) └── API Reference (complete) ``` -------------------------------- ### Example of Binding Various Types Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/types.md Demonstrates how to bind different types of keys, including classes, strings, and tuples, to instances or configurations within the injector. This is useful for setting up dependency injection configurations. ```python def config(binder): # Class bindings binder.bind(Cache, RedisCache()) binder.bind(Database, PostgresDB()) # String bindings binder.bind('db_host', 'localhost') binder.bind('db_port', 5432) # Tuple bindings binder.bind(('cache', 'redis'), redis_client) binder.bind(('service', 'auth'), AuthService()) ``` -------------------------------- ### Debug Configuration with Print Statements Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Use print statements within the configuration function to trace the creation and binding of dependencies during injector setup. ```python def config(binder): print("Starting configuration...") cache = RedisCache() print(f"Created Cache: {cache}") binder.bind(Cache, cache) print("Bound Cache") db = Database() print(f"Created Database: {db}") binder.bind(Database, db) print("Bound Database") inject.configure(config) # Test injection cache = inject.instance(Cache) print(f"Retrieved Cache: {cache}") ``` -------------------------------- ### Binder Class Methods Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Methods for configuring dependency injection bindings. Use `install` to include other configurations, `bind` to map a class to a specific instance, `bind_to_constructor` for singleton instances, and `bind_to_provider` for instances created per injection. ```python class Binder: def __init__(self, allow_override: bool = False) -> None def install(self, config: BinderCallable) -> Binder """Install another configuration, return self for chaining""" def bind(self, cls: Binding, instance: T) -> Binder """Bind class to instance, return self for chaining""" def bind_to_constructor(self, cls: Binding, constructor: Constructor) -> Binder """Bind class to constructor (singleton), return self for chaining""" def bind_to_provider(self, cls: Binding, provider: Provider) -> Binder """Bind class to provider (called per injection), return self for chaining""" ``` -------------------------------- ### Runtime Binding Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Demonstrates how Python Inject automatically instantiates unbound classes when bind_in_runtime is True (default). ```python def config(binder): binder.bind(Config, load_config()) inject.configure(config) # Runtime binding: User class is auto-instantiated as singleton # because it has no dependencies or all dependencies can be resolved user = inject.instance(User) ``` -------------------------------- ### Configuration Override and Composition Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Use 'allow_override=True' to permit rebinding of existing configurations and 'install()' to merge configurations from other modules. This pattern is useful for composing configurations, such as using a base configuration and overriding specific bindings for testing. ```python def base_config(binder): binder.bind(Database, PostgresDB()) binder.bind(Logger, RealLogger()) def test_config(binder): binder.install(base_config) binder.bind(Logger, MockLogger()) # Override inject.configure(test_config, allow_override=True, clear=True) ``` -------------------------------- ### Full Quick Code Example for Python Inject Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/00-START-HERE.md Demonstrates defining services, configuring bindings, and using the @autoparams decorator to inject dependencies into a function. Call the function without explicit dependency arguments. ```python import inject # 1. Define services class Cache: def set(self, k, v): pass class Database: def query(self, sql): pass # 2. Configure def config(binder): binder.bind(Cache, Cache()) binder.bind(Database, Database()) inject.configure(config) # 3. Use injection @inject.autoparams def fetch_user(user_id: int, db: Database, cache: Cache): cached = cache.get(f'user:{user_id}') if cached: return cached user = db.query(f"SELECT * FROM users WHERE id={user_id}") cache.set(f'user:{user_id}', user) return user # 4. Call without arguments user = fetch_user(42) # cache and db are automatically injected ``` -------------------------------- ### Context Manager Support with @inject.params Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/decorators.md Illustrates how @inject.params can be used with context managers to inject resources that require setup and teardown, such as database connections. ```python @contextlib.contextmanager def get_connection(): conn = DbConnection() yield conn conn.close() def config(binder): binder.bind_to_provider(Connection, get_connection) @inject.params(conn=Connection) def query(conn=None): # conn.__enter__() has been called result = conn.execute("SELECT 1") # conn.__exit__() will be called when function exits return result ``` -------------------------------- ### Getting the Injector Instance Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Shows how to safely retrieve the injector instance, which might return None if not configured, or strictly retrieve it, raising an error if not configured. ```python # Safe: might return None injector = inject.get_injector() if injector: instance = injector.get_instance(Cache) # Assuming Cache is a configured type # Strict: raises if not configured injector = inject.get_injector_or_die() instance = injector.get_instance(Cache) # Assuming Cache is a configured type ``` -------------------------------- ### Catch InjectorException During Configuration Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/errors.md Demonstrates how to catch InjectorException when the inject module's configuration fails. This is useful for handling setup errors gracefully. ```python import inject try: inject.configure(config) except inject.InjectorException as e: print(f"Configuration failed: {e}") ``` -------------------------------- ### Application Shutdown and Restart Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Manage application lifecycle by defining methods to start, stop, and restart the injector, ensuring proper cleanup and re-configuration. ```python class Application: def start(self): inject.configure(self.config, clear=True) def stop(self): inject.clear() def restart(self): self.stop() self.start() ``` -------------------------------- ### Runtime Binding Chain Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/binding-types.md Illustrates a chain of runtime-bound dependencies where the entire tree is created automatically. This includes classes that depend on other runtime-bound or explicitly bound classes. ```python import inject class Config: """Must be explicitly bound""" def __init__(self, filepath: str): self.data = json.load(open(filepath)) class Logger: """Runtime bound, no-arg constructor""" def log(self, msg): print(msg) class Database: """Runtime bound, but depends on Config""" config = inject.attr(Config) def connect(self): self.db = psycopg2.connect(self.config.data['db_url']) class UserService: """Runtime bound, depends on Database""" db = inject.attr(Database) logger = inject.attr(Logger) def find_user(self, user_id: int): self.logger.log(f"Finding user {user_id}") return self.db.query(f"SELECT * FROM users WHERE id = {user_id}") def config(binder): binder.bind(Config, Config('config.json')) inject.configure(config) # Entire dependency tree is created automatically service = inject.instance(UserService) user = service.find_user(42) ``` -------------------------------- ### Runtime Binding Example in Python Inject Source: https://github.com/ivankorobkov/python-inject/blob/master/README.md Illustrates runtime binding where singletons are automatically created on injection. Explicit bindings are required for `Config`, while `Cache` and `Db` are implicitly handled by runtime binding. ```python class Config(object): pass class Cache(object): config = inject.attr(Config) class Db(object): config = inject.attr(Config) class User(object): cache = inject.attr(Cache) db = inject.attr(Db) @classmethod def load(cls, user_id): return cls.cache.load('users', user_id) or cls.db.load('users', user_id) inject.configure(lambda binder: binder.bind(Config, load_config_file())) user = User.load(10) ``` -------------------------------- ### Basic Usage of @inject.params Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/decorators.md Demonstrates how to use @inject.params to explicitly map parameter names to binding keys for dependency injection. This example shows injecting 'Cache' and 'Database' into function parameters. ```python import inject @inject.params(cache=Cache, db=Database) def create_user(name: str, cache=None, db=None): user = User(name) db.save(user) cache.invalidate('users') return user # cache and db are injected create_user('Alice') ``` -------------------------------- ### Setup Fresh Injector for Each Test Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Ensure test isolation by configuring a fresh injector with specific test configurations before each test case and clearing it afterwards. ```python class TestSuite(unittest.TestCase): def setUp(self): """Fresh injector for each test""" inject.configure(self.test_config, clear=True) def tearDown(self): """Cleanup""" inject.clear() @staticmethod def test_config(binder): binder.bind(Database, TestDatabase()) binder.bind(Cache, MockCache()) ``` -------------------------------- ### Check if Injector is Configured Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Checks if the global injector has been configured. Use this to conditionally configure the injector, for example, at the start of your application or test suite. ```python if not inject.is_configured(): inject.configure(my_config) ``` -------------------------------- ### Application Startup Configuration Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/examples.md Shows how to configure multiple dependencies, including database connections and services, during application startup. ```python import inject from myapp.database import Database from myapp.cache import RedisCache from myapp.services import UserService, AuthService def create_app(): def config(binder): binder.bind(Database, Database('postgresql://localhost/mydb')) binder.bind(Cache, RedisCache('redis://localhost')) # Services depend on Database and Cache via @autoparams binder.bind(UserService, UserService()) binder.bind(AuthService, AuthService()) inject.configure(config) return create_flask_app() if __name__ == '__main__': app = create_app() app.run() ``` -------------------------------- ### Get Configured Injector or Raise Exception Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Use `inject.get_injector_or_die()` to get the configured injector. This function raises an `InjectorException` if no injector has been set up. ```python injector = inject.get_injector_or_die() cache = injector.get_instance(Cache) ``` -------------------------------- ### Binding Configuration Values with String Keys Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Demonstrates using simple strings as keys for binding configuration values like hostnames, ports, or API keys. Ensure necessary environment variables are set if used. ```python import inject import os def config(binder): binder.bind('db_host', 'localhost') binder.bind('db_port', 5432) binder.bind('api_key', os.environ['API_KEY']) ``` -------------------------------- ### Pytest Fixture for Dependency Injection Setup Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Use a pytest fixture with `autouse=True` to automatically configure and clear the injector for all tests in a module. This simplifies test setup by ensuring a consistent injection environment. ```python @pytest.fixture(autouse=True) def setup(): inject.configure(lambda b: b.bind(Cache, MockCache()), clear=True) yield inject.clear() ``` -------------------------------- ### Get Injected Instance Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Retrieves an injected instance of a specified binding from the global injector. ```python instance(cls: Binding) -> Injectable ``` -------------------------------- ### Configuration Composition with Override Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Demonstrates how to use allow_override=True to compose configurations and override existing bindings. ```python def base_config(binder): binder.bind(Cache, RedisCache('localhost')) binder.bind(Database, PostgresDB()) def test_config(binder): binder.install(base_config) binder.bind(Cache, MockCache()) # Override base config inject.configure(test_config, allow_override=True) ``` -------------------------------- ### Injector Get Instance Method Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Retrieves an injected instance of a specified binding from this Injector instance. ```python def get_instance(self, cls: Binding) -> Injectable ``` -------------------------------- ### Unconfigured Injector State Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Before any configuration, the injector is unconfigured. Attempting to get an instance will raise an InjectorException. ```python _INJECTOR = None # Before any configuration assert inject.is_configured() == False assert inject.get_injector() is None ``` ```python try: cache = inject.instance(Cache) except inject.InjectorException as e: # "No injector is configured" ``` -------------------------------- ### Mixed Lazy and Eager Initialization Approach Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Illustrates a mixed approach where some components (like configuration) are eagerly initialized, while others (like database and services) are lazily initialized using constructor bindings. This optimizes startup time and resource usage. ```python def config(binder): # Config is eager (needed to boot) config_obj = load_config_file() binder.bind(Config, config_obj) # Database is lazy (may not be needed) binder.bind_to_constructor( Database, lambda: Database(config_obj.db_url) ) # Services are lazy too binder.bind_to_constructor( UserService, lambda: UserService() ) inject.configure(config) ``` -------------------------------- ### Get Current Injector Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Retrieves the current global injector instance. Returns None if the injector is not configured. ```python get_injector() -> Optional[Injector] ``` -------------------------------- ### Clear Injector Between Tests Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/best-practices.md Ensure tests are isolated by clearing the injector configuration in `setUp` and `tearDown` methods. ```python class TestSuite(unittest.TestCase): def setUp(self): inject.configure(self.test_config, clear=True) def tearDown(self): inject.clear() ``` -------------------------------- ### Get Configured Injector Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Retrieve the currently configured injector using `inject.get_injector()`. If no injector is configured, it returns None. ```python injector = inject.get_injector() if injector: instance = injector.get_instance(Cache) ``` -------------------------------- ### Thread-Local Dependency Provider Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/README.md Use a provider function to inject thread-local dependencies, ensuring each thread gets its own instance. ```python _local = threading.local() def get_current_user(): return getattr(_local, 'user', None) def config(binder): binder.bind_to_provider(CurrentUser, get_current_user) ``` -------------------------------- ### Composable Configurations in Python Inject Source: https://github.com/ivankorobkov/python-inject/blob/master/README.md Demonstrates how to create reusable configurations and override existing dependencies. Use `binder.install` to include another configuration and `binder.bind` to override specific bindings. `inject.configure` is used to apply the final configuration. ```python def base_config(binder): # ... more dependencies registered here binder.bind(Validator, RealValidator()) binder.bind(Cache, RedisCache('localhost:1234')) def tests_config(binder): # reuse existing configuration binder.install(base_config) # override only certain dependencies binder.bind(Validator, TestValidator()) binder.bind(Cache, MockCache()) inject.configure(tests_config, allow_override=True, clear=True) ``` -------------------------------- ### Binder.__init__() Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Initializes a new `Binder` instance, optionally allowing the override of existing bindings. ```APIDOC ## Class: Binder ### Method: __init__(self, allow_override: bool = False) -> None ### Description Initializes a new `Binder` instance. ### Parameters - **allow_override** (bool) - Default: False - Allow binding the same key multiple times. ``` -------------------------------- ### Get Current Injector or Raise Error Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Retrieves the current global injector instance. Raises an error if the injector is not configured. ```python get_injector_or_die() -> Injector ``` -------------------------------- ### Provider Binding in Python Inject Source: https://github.com/ivankorobkov/python-inject/blob/master/README.md Binds a type to a provider function. The provider function is called on each injection to get the instance. ```python def get_my_thread_local_cache(): pass def config(binder): # Executes the provider on each injection. binder.bind_to_provider(Cache, get_my_thread_local_cache) ``` -------------------------------- ### Explicit Binding Required Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Shows how an InjectorException is raised when attempting to inject an unbound class with bind_in_runtime set to False. ```python inject.configure(config, bind_in_runtime=False) try: user = inject.instance(User) # Raises InjectorException except inject.InjectorException as e: print("User is not explicitly bound") ``` -------------------------------- ### Eager Initialization of Instance Bindings Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Shows how instance bindings are initialized immediately when `inject.configure` is called. This ensures that the bound instances are ready as soon as configuration is complete. ```python def config(binder): print("Creating Database...") db = Database() print("Database created") binder.bind(Database, db) inject.configure(config) # Output: "Creating Database..." then "Database created" ``` -------------------------------- ### Configure with Environment Variables Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Binds dependencies by reading configuration values from environment variables. Provides default values if variables are not set. ```python import os def config(binder): # Read from environment db_url = os.environ.get('DATABASE_URL', 'postgresql://localhost/mydb') cache_url = os.environ.get('REDIS_URL', 'redis://localhost:6379') # Bind to read values binder.bind(Database, PostgresDB(db_url)) binder.bind(Cache, RedisCache(cache_url)) binder.bind('debug', os.environ.get('DEBUG', '').lower() == 'true') inject.configure(config) ``` -------------------------------- ### Duplicate Binding Error Example Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Illustrates the InjectorException raised when attempting to bind the same key twice with allow_override set to False (default). ```python def config(binder): binder.bind(Cache, RedisCache()) binder.bind(Cache, MemcacheCache()) # InjectorException: Duplicate binding inject.configure(config) # Fails ``` -------------------------------- ### Configure Bindings for Services Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/00-START-HERE.md Sets up the dependency injection container by binding interfaces to their concrete implementations or instances. Use this to define how services are provided. ```python def config(binder): binder.bind(Database, PostgresDB()) binder.bind(Cache, RedisCache()) inject.configure(config) ``` -------------------------------- ### Importing and Using Inject APIs Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/reference-summary.md Import all necessary components directly from the 'inject' module for configuration and instance retrieval. ```python import inject # All of these work: inject.configure(config) inject.instance(SomeClass) inject.autoparams inject.params inject.attr inject.Binder inject.Injector inject.InjectorException inject.ConstructorTypeError ``` -------------------------------- ### Namespaced Configuration with Tuple Keys Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Shows how to use tuples as keys to create nested or namespaced configuration values, useful for organizing related settings like cache or service configurations. ```python import inject def config(binder): binder.bind(('cache', 'redis'), RedisCache()) binder.bind(('cache', 'default'), InMemoryCache()) binder.bind(('service', 'auth'), AuthService()) binder.bind(('service', 'users'), UserService()) ``` -------------------------------- ### Constructor Binding with Arguments using Closures Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/binding-types.md Demonstrates how to provide arguments to constructors when using bind_to_constructor by utilizing lambda functions or closures to capture configuration values. ```python def config(binder): # Wrong: These don't work # binder.bind_to_constructor(Service, Service('arg')) # Calls Service immediately # binder.bind_to_constructor(Service, Service) # No-arg constructor only # Correct: Use lambda or closure binder.bind_to_constructor( Service, lambda: Service('custom_arg') ) # Correct: Closure with external config config_value = 'important_setting' binder.bind_to_constructor( Service, lambda: Service(config_value) ) ``` -------------------------------- ### Non-Invasive Dependency Injection Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/README.md Demonstrates how python-inject can inject dependencies without altering class constructors. Use inject.instance() to get an injected instance of a class. ```python class MyService: def __init__(self, dependency): self.dep = dependency # Still works normally service = MyService(my_dependency) # But can also inject service = inject.instance(MyService) ``` -------------------------------- ### Configure Injector Once at Startup Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/best-practices.md Configure the injector once during application startup. This should be done before any worker threads are spawned to ensure thread safety. ```python # app.py def create_app(): inject.configure(app_config) return Flask(__name__) if __name__ == '__main__': app = create_app() app.run() ``` -------------------------------- ### Catch InjectorException When Getting Instance Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/errors.md Shows how to catch InjectorException when attempting to retrieve an instance from the injector fails. This is useful for handling dependency injection errors. ```python try: cache = inject.instance(Cache) except inject.InjectorException as e: print(f"Could not inject Cache: {e}") ``` -------------------------------- ### Safe Configuration with Once Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Illustrates using once=True to ensure configuration only occurs if the injector has not been configured yet, suitable for frameworks that reload modules. ```python # app.py inject.configure(my_config, once=True) # Only runs first time # Can be imported multiple times, configuration only happens once ``` -------------------------------- ### Injector.__init__() Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Initializes the Injector. It can be configured with a binder, and settings can be adjusted for runtime binding and overriding. ```APIDOC ## Injector.__init__() ### Description Initializes the Injector with optional configuration, runtime binding, and override settings. ### Method __init__ ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **config** (Optional[Callable[[Binder], Optional[Binder]]]) - Optional - Configuration function. - **bind_in_runtime** (bool) - Optional - Enable automatic runtime binding. Defaults to True. - **allow_override** (bool) - Optional - Allow overriding bindings. Defaults to False. ``` -------------------------------- ### Python Inject Binder Initialization Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Initializes a Binder instance. Use `allow_override` to permit rebinding the same key. ```python def __init__(self, allow_override: bool = False) -> None ``` -------------------------------- ### Example Usage of Generic Type Variable T Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/types.md Demonstrates how the generic type variable T is used with inject.instance to infer and return the correct type of the requested instance. ```python @dataclass class User: id: int name: str # T is bound to User at call site user = inject.instance(User) # Return type is User ``` -------------------------------- ### Lazy Configuration with Constructor Binding Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Defers the creation of a service until it is first requested. This is useful for expensive-to-initialize dependencies. ```python def config(binder): def lazy_service(): # Only created if actually used import expensive_module return expensive_module.ExpensiveService() binder.bind_to_constructor(ExpensiveService, lazy_service) ``` -------------------------------- ### Avoid Binding All Classes Eagerly Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/best-practices.md Prefer runtime binding for simple classes to avoid over-configuration and reduce noise in your configuration setup. Only bind explicitly when necessary. ```python # WRONG: Over-configuration def config(binder): binder.bind(UserService, UserService()) binder.bind(ProductService, ProductService()) binder.bind(OrderService, OrderService()) binder.bind(Logger, Logger()) binder.bind(Cache, InMemoryCache()) # BETTER: Use runtime binding def config(binder): binder.bind(Database, Database()) # Let simple classes use runtime binding ``` -------------------------------- ### Context Managers with Injector Source: https://github.com/ivankorobkov/python-inject/blob/master/README.md Demonstrates how to use context managers with the python-inject framework, including async context managers and binding to providers. ```python @contextlib.contextmanager def get_file_sync(): obj = MockFile() yield obj obj.destroy() @contextlib.asynccontextmanager async def get_conn_async(): obj = MockConnection() yield obj obj.destroy() def config(binder): binder.bind_to_provider(MockFile, get_file_sync) binder.bind(int, 100) binder.bind_to_provider(str, lambda: "Hello") binder.bind_to_provider(MockConnection, get_conn_sync) inject.configure(config) @inject.autoparams() def example(conn: MockConnection, file: MockFile): # Connection and file will be automatically destroyed on exit. pass ``` -------------------------------- ### Example: Request-Scoped User Binding in Flask Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/binding-types.md Shows how to bind a request-scoped object in a Flask application using a provider. The user instance is specific to the current request. ```python import flask import inject _request_context = {} def get_request_user(): return _request_context.get(flask.request.endpoint, {}).get('user') def config(binder): binder.bind_to_provider(User, get_request_user) @app.before_request def load_user(): _request_context[flask.request.endpoint] = { 'user': User.from_token(flask.request.headers.get('Authorization')) } @app.route('/profile') @inject.autoparams def profile(user: User): return f"Hello {user.name}" ``` -------------------------------- ### Safe Configuration with try-except Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/errors.md Use a try-except block to catch InjectorException during configuration. This prevents application startup failure if the configuration function encounters an issue. ```python import inject def create_app(): try: inject.configure(config_func) except inject.InjectorException as e: logger.error(f"Failed to configure injector: {e}") raise ``` -------------------------------- ### Avoid Configuring Injector from Worker Threads Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/best-practices.md Never call `inject.configure()` from worker threads as it is not thread-safe. Configuration must happen in the main thread before workers start. ```python # WRONG def worker(): inject.configure(config) # Thread-unsafe! # RIGHT inject.configure(config) # Main thread, before workers start for _ in range(10): threading.Thread(target=worker).start() ``` -------------------------------- ### Group Related Bindings by Feature Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/best-practices.md Organize configuration bindings by feature or layer to improve readability and maintainability. Use `binder.install` to include configurations from other functions. ```python def database_config(binder): binder.bind(Database, PostgresDB()) binder.bind(Migration, Migration()) def service_config(binder): binder.bind(UserService, UserService()) binder.bind(OrderService, OrderService()) def app_config(binder): binder.install(database_config) binder.install(service_config) ``` -------------------------------- ### Detecting Duplicate Configuration Bindings Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/injector-lifecycle.md Demonstrates how the injector raises an exception when duplicate bindings for the same key are attempted without allowing overrides. This helps catch configuration errors early. ```python def config(binder): binder.bind(Cache, RedisCache()) binder.bind(Cache, MemcacheCache()) # ERROR: duplicate try: inject.configure(config) except inject.InjectorException as e: # "Duplicate binding, key=" ``` -------------------------------- ### Pytest Fixture for Python-Inject Setup Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Shows how to use a pytest fixture to automatically configure and clear python-inject for tests. The `autouse=True` flag ensures the fixture runs for every test. ```python import pytest import inject @pytest.fixture(autouse=True) def inject_setup(): def test_config(binder): binder.bind(Cache, MockCache()) binder.bind(Database, TestDB()) inject.configure(test_config, clear=True) yield inject.clear() def test_something(inject_setup): assert inject.instance(Service) is not None ``` -------------------------------- ### Sync Context Manager for Database Connection Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/binding-types.md Demonstrates binding a sync context manager to a constructor. The `get_database` function yields a `DatabaseConnection` instance, which is automatically managed (entered and exited) when injected into `query_users`. ```Python import contextlib import inject @contextlib.contextmanager def get_database(): db = DatabaseConnection() try: yield db finally: db.close() def config(binder): binder.bind_to_constructor(Database, get_database) inject.configure(config) @inject.autoparams def query_users(db: Database): # __enter__() has been called users = db.query("SELECT * FROM users") # __exit__() will be called when function exits return users ``` -------------------------------- ### Partial Mocking with Override Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/examples.md Shows how to partially mock dependencies by reusing existing configurations and overriding specific bindings. This allows testing with a real database while mocking only the email service. ```python import inject def test_with_real_database(): def test_config(binder): binder.install(production_config) # Reuse prod config binder.bind(Email, MockEmail(), allow_override=True) # Override only Email inject.configure(test_config, allow_override=True, clear=True) # Test with real database but mock email service = UserService() user = service.create_user('test@example.com') inject.clear() ``` -------------------------------- ### _ConstructorBinding Implementation with Double-Checked Locking Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/architecture.md Details the implementation of _ConstructorBinding, showcasing the double-checked locking pattern for thread-safe singleton instance creation. ```python class _ConstructorBinding: _instance: Optional[T] _created: bool def __call__(self) -> T: # Double-checked locking for thread safety if self._created and self._instance is not None: return self._instance with _BINDING_LOCK: if self._created and self._instance is not None: return self._instance self._instance = self._constructor() self._created = True return self._instance ``` -------------------------------- ### Python Inject Module Structure and API Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/README.md Shows how to import and use core components of the inject library, including configuration, injection, and decorators. ```python import inject # Configuration inject.configure(config) inject.clear() # Injection inject.instance(SomeClass) # Decorators @inject.autoparams @inject.params(...) @inject.attr() # Classes inject.Injector inject.Binder # Exceptions inject.InjectorException inject.ConstructorTypeError ``` -------------------------------- ### instance() Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Injects and returns an instance of a class or binding key from the configured injector. Can resolve dependencies based on type or a hashable key. ```APIDOC ## instance() ### Description Injects and returns an instance of a class or binding key from the configured injector. Can resolve dependencies based on type or a hashable key. ### Method `instance` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters Table | Parameter | Type | Description | |---|---|---| | cls | `Union[type, Hashable]` | The class or binding key to retrieve | ### Returns `Injectable` — The bound or runtime-created instance. ### Raises - `InjectorException`: If injector is not configured - `InjectorException`: If no binding exists and runtime binding is disabled - `ConstructorTypeError`: If constructor fails with TypeError ### Example ```python import inject def config(binder): binder.bind(Cache, RedisCache()) inject.configure(config) # Retrieve instance cache = inject.instance(Cache) cache.set('key', 'value') # Works with hashable keys inject.instance('db_host') # returns 'localhost' if bound ``` ``` -------------------------------- ### Flask Integration with Python Inject Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/examples.md Demonstrates how to configure and use python-inject within a Flask application. This setup allows for automatic injection of dependencies like Database and UserService into Flask routes and services. ```python import flask import inject app = flask.Flask(__name__) class UserService: @inject.autoparams def find_user(self, user_id: int, db: Database): return db.find(user_id) def configure_flask(app): def config(binder): binder.bind(Database, Database()) binder.bind(UserService, UserService()) inject.configure(config) @app.route('/users/') @inject.autoparams def get_user(user_id, service: UserService): user = service.find_user(user_id) return flask.jsonify(user) if __name__ == '__main__': configure_flask(app) app.run() ``` -------------------------------- ### Global Injector Configuration and Usage Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/README.md Configure the global injector once at startup and access instances from anywhere. ```python inject.configure(config) # Configure once at startup inject.instance(Cache) # Use from anywhere ``` -------------------------------- ### Type-Safe Configuration with Enum Keys Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/configuration.md Illustrates using Python enums as keys for configuration bindings, providing type safety and clarity for settings related to different states or environments. ```python from enum import Enum import inject class Environment(Enum): DEVELOPMENT = 'development' PRODUCTION = 'production' def config(binder): binder.bind(Environment.PRODUCTION, ProductionConfig()) binder.bind(Environment.DEVELOPMENT, DevelopmentConfig()) ``` -------------------------------- ### Get Instance from Injector Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/api-reference.md Retrieves an instance for a given class or binding key. Supports direct class retrieval and retrieval using any hashable key. Ensure bindings are configured or runtime binding is enabled. ```python @overload def get_instance(self, cls: Binding) -> T: ... @overload def get_instance(self, cls: Hashable) -> Injectable: ... def get_instance(self, cls: Binding) -> Injectable: ... ``` ```python def config(binder): binder.bind(Cache, RedisCache()) injector = inject.configure(config) # Direct retrieval cache = injector.get_instance(Cache) # Works with any hashable key db_url = injector.get_instance('database_url') ``` -------------------------------- ### Documentation File Structure Source: https://github.com/ivankorobkov/python-inject/blob/master/_autodocs/DOCUMENTATION-MANIFEST.md Overview of the directory structure for the Python Inject documentation, showing the purpose of each file. ```text output/ ├── README.md (Entry point) ├── index.md (Navigation) ├── api-reference.md (Complete API) ├── reference-summary.md (Quick reference) ├── types.md (Type definitions) ├── errors.md (Error reference) ├── configuration.md (Configuration guide) ├── binding-types.md (Binding type guide) ├── decorators.md (Decorator reference) ├── examples.md (Usage examples) ├── architecture.md (Implementation) ├── injector-lifecycle.md (Lifecycle management) ├── best-practices.md (Guidelines) └── DOCUMENTATION-MANIFEST.md (This file) ```