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

field-initialization.mddocs/

Field Initialization Utilities

Convenience decorators for automatically copying constructor arguments to instance fields, reducing boilerplate code in class initializers by eliminating manual field assignment.

Capabilities

Internal Field Copying

Automatically copies constructor arguments to private instance fields with underscore prefix.

def copy_args_to_internal_fields(fn):
    """
    Decorator that copies __init__ arguments to internal (private) fields.
    
    Applies to __init__ methods only. Each argument 'arg_name' becomes '_arg_name' field.
    Cannot be used with *args parameters.
    
    Parameters:
    - fn: __init__ method to decorate
    
    Returns:
    Decorated __init__ method that copies args to _field_name attributes
    """

Public Field Copying

Automatically copies constructor arguments to public instance fields with same names.

def copy_args_to_public_fields(fn):
    """
    Decorator that copies __init__ arguments to public fields.
    
    Applies to __init__ methods only. Each argument 'arg_name' becomes 'arg_name' field.
    Cannot be used with *args parameters.
    
    Parameters:
    - fn: __init__ method to decorate
    
    Returns:
    Decorated __init__ method that copies args to field_name attributes
    """

Usage Examples

Internal Fields with Dependency Injection

import pinject

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

class UserRepository(object):
    @pinject.copy_args_to_internal_fields
    def __init__(self, database_config, table_name="users"):
        # Arguments automatically copied to _database_config and _table_name
        pass
    
    def get_connection_info(self):
        return f"Connecting to {self._database_config.host}:{self._database_config.port}"
    
    def get_table(self):
        return self._table_name

obj_graph = pinject.new_object_graph()
repo = obj_graph.provide(UserRepository)  # uses default table_name="users"

print(repo.get_connection_info())  # "Connecting to localhost:5432"
print(repo.get_table())           # "users"
print(hasattr(repo, 'database_config'))  # False (not public)
print(hasattr(repo, '_database_config')) # True (internal field)

Public Fields with Manual Logic

import pinject

class Logger(object):
    def __init__(self):
        self.level = "INFO"

class FileService(object):
    @pinject.copy_args_to_public_fields
    def __init__(self, logger, base_path="/tmp"):
        # Arguments automatically copied to .logger and .base_path
        # Additional initialization logic can follow
        self.files_processed = 0
        print(f"FileService initialized with path {self.base_path}")
    
    def process_file(self, filename):
        self.logger.level = "DEBUG"
        full_path = f"{self.base_path}/{filename}"
        self.files_processed += 1
        return full_path

obj_graph = pinject.new_object_graph()
service = obj_graph.provide(FileService)  # uses default base_path="/tmp"

print(service.logger.level)        # "INFO" (initially)
path = service.process_file("test.txt")
print(path)                        # "/tmp/test.txt"
print(service.logger.level)        # "DEBUG" (modified)
print(service.files_processed)     # 1

Combined with Other Decorators

import pinject

class DatabaseConnection(object):
    def __init__(self):
        self.url = "postgresql://localhost"

class CacheService(object):
    def __init__(self):
        self.data = {}

class BusinessService(object):
    @pinject.inject(['database_connection'])  # Only inject database_connection
    @pinject.copy_args_to_internal_fields     # Copy all args to internal fields
    def __init__(self, database_connection, cache_service, api_key):
        # database_connection injected and copied to _database_connection
        # cache_service passed directly and copied to _cache_service  
        # api_key passed directly and copied to _api_key
        self.initialized = True
    
    def get_data(self):
        return {
            'db_url': self._database_connection.url,
            'cache_size': len(self._cache_service.data),
            'api_key_length': len(self._api_key)
        }

cache = CacheService()
cache.data['key1'] = 'value1'

obj_graph = pinject.new_object_graph()
service = obj_graph.provide(
    BusinessService, 
    cache_service=cache, 
    api_key="secret123"
)

data = service.get_data()
print(data['db_url'])         # "postgresql://localhost"
print(data['cache_size'])     # 1
print(data['api_key_length']) # 9

Error Handling

import pinject

class InvalidDecoratorUsage(object):
    # ERROR: copy_args decorators only work on __init__ methods
    @pinject.copy_args_to_public_fields
    def setup(self, config):
        pass

class InvalidArgsUsage(object):
    # ERROR: copy_args decorators don't work with *args
    @pinject.copy_args_to_internal_fields
    def __init__(self, service, *additional_args):
        pass

# These will raise errors:
# - pinject.DecoratorAppliedToNonInitError for the first case
# - pinject.PargsDisallowedWhenCopyingArgsError for the second case

Comparison: Manual vs Automatic Field Copying

import pinject

# Manual approach (verbose)
class ManualService(object):
    def __init__(self, database_service, cache_service, config):
        self._database_service = database_service
        self._cache_service = cache_service
        self._config = config
        # Additional initialization logic...

# Automatic approach (concise)
class AutoService(object):
    @pinject.copy_args_to_internal_fields
    def __init__(self, database_service, cache_service, config):
        # Fields automatically created: _database_service, _cache_service, _config
        # Focus on additional initialization logic only...
        pass

# Both approaches result in identical field structure
obj_graph = pinject.new_object_graph()
manual = obj_graph.provide(ManualService)
auto = obj_graph.provide(AutoService)

print(hasattr(manual, '_database_service'))  # True
print(hasattr(auto, '_database_service'))    # True

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