CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pinject

A pythonic dependency injection library that assembles objects into graphs in an easy, maintainable way

Pending
Overview
Eval results
Files

binding-specs.mddocs/

Binding Specifications

Custom binding configuration through BindingSpec classes that define explicit relationships between interfaces and implementations, enabling complex dependency scenarios beyond automatic resolution.

Capabilities

BindingSpec Base Class

Abstract base class for creating custom binding specifications that define how dependencies should be resolved.

class BindingSpec(object):
    def configure(self, bind):
        """
        Configures bindings for the object graph.

        Parameters:
        - bind: binding configuration object for creating explicit bindings

        Called by the object graph during initialization to register custom bindings.
        """

Binding Configuration Methods

Methods available within BindingSpec.configure() for defining explicit bindings.

class Binder(object):
    def __call__(self, binding_key):
        """
        Creates a binding for the specified key.

        Parameters:
        - binding_key: key to bind (typically a string matching parameter name)

        Returns:
        Binding configuration object with to_class(), to_instance(), and to_provider() methods
        """
    
    def to_class(self, cls, in_scope=None):
        """
        Binds to a specific class.

        Parameters:
        - cls: class to instantiate for this binding
        - in_scope: scope ID for instances (SINGLETON, PROTOTYPE, or custom)
        """
    
    def to_instance(self, instance):
        """
        Binds to a specific instance.

        Parameters:
        - instance: pre-created instance to use for this binding
        """
    
    def to_provider(self, provider_fn, in_scope=None):
        """
        Binds to a provider function.

        Parameters:
        - provider_fn: function that returns instances for this binding
        - in_scope: scope ID for instances
        """

Usage Examples

Basic Binding Specification

import pinject

class DatabaseInterface(object):
    pass

class PostgreSQLDatabase(DatabaseInterface):
    def __init__(self):
        self.connection = "postgresql://localhost"

class MySQLDatabase(DatabaseInterface):
    def __init__(self):
        self.connection = "mysql://localhost"

class DatabaseBindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        # Bind interface to specific implementation
        bind('database_interface').to_class(PostgreSQLDatabase)

class UserService(object):
    def __init__(self, database_interface):
        self.db = database_interface

obj_graph = pinject.new_object_graph(binding_specs=[DatabaseBindingSpec()])
user_service = obj_graph.provide(UserService)
print(user_service.db.connection)  # "postgresql://localhost"

Instance Bindings

import pinject

class Configuration(object):
    def __init__(self, env, debug):
        self.environment = env
        self.debug_mode = debug

class ConfigBindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        # Bind to pre-created instances
        prod_config = Configuration('production', False)
        bind('configuration').to_instance(prod_config)

class AppService(object):
    def __init__(self, configuration):
        self.config = configuration

obj_graph = pinject.new_object_graph(binding_specs=[ConfigBindingSpec()])
app = obj_graph.provide(AppService)
print(app.config.environment)  # "production"

Provider Function Bindings

import pinject
import logging

class LoggingBindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        # Bind to provider functions
        bind('logger').to_provider(self._create_logger, in_scope=pinject.SINGLETON)
        bind('file_handler').to_provider(lambda: logging.FileHandler('app.log'))
    
    def _create_logger(self, file_handler):
        logger = logging.getLogger('myapp')
        logger.addHandler(file_handler)
        logger.setLevel(logging.INFO)
        return logger

class AppService(object):
    def __init__(self, logger):
        self.logger = logger

obj_graph = pinject.new_object_graph(binding_specs=[LoggingBindingSpec()])
app = obj_graph.provide(AppService)
app.logger.info("Application started")

Scoped Bindings

import pinject

class DatabaseConnection(object):
    def __init__(self):
        self.connection_id = id(self)

class ConnectionBindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        # Singleton: same instance shared across the application
        bind('shared_connection').to_class(DatabaseConnection, in_scope=pinject.SINGLETON)
        
        # Prototype: new instance every time
        bind('temp_connection').to_class(DatabaseConnection, in_scope=pinject.PROTOTYPE)

class ServiceA(object):
    def __init__(self, shared_connection, temp_connection):
        self.shared = shared_connection
        self.temp = temp_connection

class ServiceB(object):
    def __init__(self, shared_connection, temp_connection):
        self.shared = shared_connection
        self.temp = temp_connection

obj_graph = pinject.new_object_graph(binding_specs=[ConnectionBindingSpec()])
service_a = obj_graph.provide(ServiceA)
service_b = obj_graph.provide(ServiceB)

# Shared connections are the same instance
print(service_a.shared.connection_id == service_b.shared.connection_id)  # True

# Temp connections are different instances  
print(service_a.temp.connection_id == service_b.temp.connection_id)  # False

Provider Methods with Dependencies

import pinject

class DatabaseConfig(object):
    def __init__(self):
        self.host = "localhost"
        self.port = 5432

class DatabaseConnection(object):
    def __init__(self, host, port):
        self.host = host
        self.port = port

class DatabaseBindingSpec(pinject.BindingSpec):
    def configure(self, bind):
        bind('database_config').to_class(DatabaseConfig)
        bind('database_connection').to_provider(self._create_connection)
    
    def _create_connection(self, database_config):
        # Provider methods can have their own dependencies injected
        return DatabaseConnection(database_config.host, database_config.port)

obj_graph = pinject.new_object_graph(binding_specs=[DatabaseBindingSpec()])
connection = obj_graph.provide(DatabaseConnection)

Install with Tessl CLI

npx tessl i tessl/pypi-pinject

docs

binding-specs.md

decorators.md

error-handling.md

field-initialization.md

index.md

object-graph.md

scoping.md

tile.json