### Install Pyinj using uv or pip Source: https://github.com/qriusglobal/pyinj/blob/main/docs/getting-started.md Instructions for installing the Pyinj library using either the `uv` package manager or `pip`. ```bash uv add pyinj # or pip install pyinj ``` -------------------------------- ### Register and Get Dependency with Pyinj Source: https://github.com/qriusglobal/pyinj/blob/main/docs/getting-started.md Demonstrates how to use Pyinj to register a dependency (Logger) with a specific scope (SINGLETON) and then retrieve an instance of it from the container. ```python from pyinj import Container, Token, Scope container = Container() LOGGER = Token[Logger]("logger") container.register(LOGGER, ConsoleLogger, Scope.SINGLETON) logger = container.get(LOGGER) logger.info("Hello") ``` -------------------------------- ### PyInj Development Setup and Commands Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Outlines the steps to set up the PyInj development environment, including cloning the repository, installing dependencies, running tests, and performing code quality checks. ```Bash # Clone repository git clone cd pyinj # Install dependencies uv sync # Run tests pytest # Type checking basedpyright src/ # Format code ruff format . # Run all quality checks ruff check . && basedpyright src/ && pytest ``` -------------------------------- ### Dispose Resources with Pyinj Source: https://github.com/qriusglobal/pyinj/blob/main/docs/getting-started.md Shows the asynchronous method `dispose()` for cleaning up resources managed by the Pyinj container. ```python await container.dispose() ``` -------------------------------- ### Install Dependencies and Activate Virtual Environment Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Installs development dependencies using UV and activates the project's virtual environment. Ensures all necessary packages are available for development. ```Bash # Install development dependencies uv sync --dev # Activate virtual environment source .venv/bin/activate # On Windows: .venv\Scripts\activate ``` -------------------------------- ### Install PyInj using UV or Pip Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Instructions for installing the PyInj library using either the UV package installer (recommended) or the standard pip package manager. ```Bash # Install with UV (recommended) uv add pyinj # Or with pip pip install pyinj ``` -------------------------------- ### Basic PyInj Container Setup and Resolution Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Demonstrates the fundamental usage of PyInj by creating a container, defining a token for a 'Database' type, registering a provider function, and resolving the dependency. It also shows how to properly dispose of resources. ```Python from pyinj import Container, Token, Scope # Create container container = Container() # Define token DB_TOKEN = Token[Database]("database") # Register provider container.register(DB_TOKEN, create_database, Scope.SINGLETON) # Resolve dependency db = container.get(DB_TOKEN) # Cleanup await container.dispose() ``` -------------------------------- ### Python Dependency Injection Composition Root Example Source: https://github.com/qriusglobal/pyinj/blob/main/docs/PEP-DI-001-dependency-injection-specification.rst Illustrates how to build an application by explicitly assembling dependencies, showing the creation of HTTP client, payment service, and order service. ```Python def build_app() -> App: http_client = HttpxAdapter(httpx.AsyncClient()) payment_service = StripePayments(http_client) order_service = OrderService(payment_service) return App(order_service) ``` -------------------------------- ### Install Pre-commit Hooks Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Installs pre-commit hooks to automatically format code and run linters before committing changes. This helps maintain code quality and consistency. ```Bash pre-commit install ``` -------------------------------- ### Basic PyInj Container Setup and Usage Source: https://github.com/qriusglobal/pyinj/blob/main/docs/index.md Demonstrates the fundamental usage of PyInj by creating a container, registering a dependency (Database) with a specific token and scope, retrieving the dependency, and disposing of the container. This showcases the core DI pattern. ```python from pyinj import Container, Token, Scope container = Container() DB_TOKEN = Token[Database]("database") container.register(DB_TOKEN, create_database, Scope.SINGLETON) db = container.get(DB_TOKEN) await container.dispose() ``` -------------------------------- ### PyInj Error Handling Examples Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Illustrates common error scenarios in PyInj, such as circular dependencies, missing providers, and type validation failures, with example error messages. ```Python # Circular dependency detection # Container Error: Cannot resolve token 'service_a': # Resolution chain: service_a -> service_b -> service_a # Cause: Circular dependency detected # Missing provider # Container Error: Cannot resolve token 'missing_service': # Resolution chain: root # Cause: No provider registered for token 'missing_service' # Type validation failure # Container Error: Provider for token 'logger' returned , expected ``` -------------------------------- ### Install PyInj using uv or pip Source: https://github.com/qriusglobal/pyinj/blob/main/docs/index.md Installs the PyInj library using either the recommended 'uv' package manager or the standard 'pip'. This is the first step to using PyInj in your Python project. ```bash # uv (recommended) uv add pyinj # or pip pip install pyinj ``` -------------------------------- ### PyInj Performance Benchmarking Example Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Illustrates the performance characteristics of PyInj, specifically mentioning O(1) type lookups, cached injection metadata, and memory efficiency. ```Python # Benchmark: 1000 services registered # Resolution time: ~0.0001ms (O(1) guaranteed) # Memory overhead: ~500 bytes per service ``` -------------------------------- ### GitHub Actions CI Workflow for Pyinj Source: https://github.com/qriusglobal/pyinj/blob/main/CLAUDE.md This snippet shows a GitHub Actions workflow configuration for Continuous Integration (CI) of the Pyinj project. It utilizes `astral-sh/setup-uv@v4` to set up uv, installs dependencies using `uv pip`, and runs tests with `uv run pytest`. ```YAML # .github/workflows/ci.yml name: CI on: push: branches: [ main ] pull_request: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup uv uses: astral-sh/setup-uv@v4 - name: Install dependencies run: uv pip install --system - name: Run tests run: uv run pytest ``` -------------------------------- ### Register Provider with Container Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Demonstrates registering a provider for a specific token with a given scope in the PyInj container. Includes type hints and a docstring with an example. ```Python def register(self, token: Token[T], provider: Provider[T], scope: Scope) -> None: """Register a provider for the given token. Args: token: The token to register the provider for provider: Factory function or callable that creates instances scope: Lifecycle scope (SINGLETON, TRANSIENT, etc.) Raises: ValueError: If token is already registered Example: >>> container = Container() >>> token = Token[str]("greeting") >>> container.register(token, lambda: "Hello", Scope.SINGLETON) """ ``` -------------------------------- ### Method Chaining for Container Setup Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Shows Polars-inspired fluent interface for setting up a dependency injection container. Allows chaining registration of services and building the container with specified settings. ```python container = ( Container() .register(Database, create_db, scope=Scope.SINGLETON) .register(Cache, create_cache, scope=Scope.REQUEST) .register(EmailService, EmailService) .with_settings(Settings) .build() ) ``` -------------------------------- ### FastAPI Integration with PyInj Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Provides an example of integrating PyInj with FastAPI for dependency injection. It shows how to use `Depends` to retrieve services from the PyInj container. ```Python from fastapi import FastAPI, Depends from pyinj import Container, Token, Scope # Assuming MyService, service_token are defined app = FastAPI() container = Container() # Register a dummy service for the example class MyService: async def create_user(self): return "User created" service_token = Token[MyService]('my_service') container.register(service_token, MyService, Scope.SINGLETON) def get_service(container: Container = Depends(lambda: container)) -> MyService: return container.get(service_token) @app.post("/users") async def create_user(service: MyService = Depends(get_service)): return await service.create_user() ``` -------------------------------- ### Testing with Mocks using pyinj Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Illustrates how to use pyinj for testing by creating a test container and registering mock objects for dependencies. This example shows setting up a pytest fixture for the container and performing assertions on mocked API calls. ```python import pytest from di import Container, Token @pytest.fixture async def test_container(): container = Container() # Register mocks mock_http = AsyncMock(spec=HttpClient) container.register(HTTP_CLIENT, lambda: mock_http) yield container await container.aclose() async def test_api_call(test_container): service = await test_container.aget(API_SERVICE) result = await service.fetch_data() assert result == expected_data ``` -------------------------------- ### Test Basic Dependency Registration Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Provides an example of testing basic dependency registration and resolution within the PyInj container using pytest. It covers registering and retrieving a transient dependency. ```Python # tests/test_container.py class TestBasicRegistration: """Test basic dependency registration and resolution.""" def test_register_and_get_transient(self): """Test registering and resolving transient dependencies.""" # Arrange container = Container() token = Token[str]("test") # Act container.register(token, lambda: "value", Scope.TRANSIENT) result = container.get(token) # Assert assert result == "value" ``` -------------------------------- ### Format Code with Ruff Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Automatically formats the code using Ruff, ensuring adherence to the project's style guide. This command can be run manually or via the 'make format' command. ```Bash # Format code automatically make format # or manually: ruff format . ``` -------------------------------- ### YAML Pre-commit Configuration for Ruff, Pyright, MyPy, Bandit, and Black Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Configuration for pre-commit hooks to enforce code quality and style. Includes setup for Ruff for linting and formatting, Pyright and MyPy for type checking, Bandit for security analysis, and Black for code formatting. ```yaml # .pre-commit-config.yaml repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.8.0 hooks: - id: ruff args: [--fix] - id: ruff-format - repo: https://github.com/microsoft/pyright rev: v1.1.380 hooks: - id: pyright additional_dependencies: ['basedpyright>=1.21.0'] args: [--warnings] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.11.2 hooks: - id: mypy args: [--strict, --show-error-codes] additional_dependencies: [types-all] - repo: https://github.com/PyCQA/bandit rev: 1.7.10 hooks: - id: bandit args: [-r, src/, --severity-level, medium] - repo: https://github.com/psf/black rev: 24.10.0 hooks: - id: black language_version: python3.12 ``` -------------------------------- ### Mocking ServiceM8 HTTP Client for Tests Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Provides an example of injecting a mock HTTP client into ServiceM8Client for unit testing purposes. The mock client simulates HTTP requests. ```python # Example: Injecting a mock HTTP client for a unit test class MockHttpClient: async def request(self, *args, **kwargs): # return a mock response ... async with ServiceM8Client.with_dependencies( auth=my_auth, http_client=MockHttpClient() ) as client: # Your test logic here... ``` -------------------------------- ### GitHub Actions Workflow for Quality Gates Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst A GitHub Actions workflow designed to automate quality checks for the project on every push and pull request. It includes steps for setting up Python, installing dependencies, performing type checking with Pyright and MyPy, security audits with Bandit and safety, checking test coverage, and analyzing code complexity. ```yaml # .github/workflows/quality.yml name: Quality Gates on: [push, pull_request] jobs: quality: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.12', '3.13'] steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | pip install -e ".[dev,test,security]" - name: Type checking (Pyright) run: | basedpyright src/ tests/ --warnings - name: Type checking (MyPy) run: | mypy src/ tests/ --strict - name: Security audit run: | bandit -r src/ --severity-level medium safety check pip-audit - name: Test coverage run: | pytest --cov=src --cov-report=xml --cov-fail-under=90 - name: Complexity analysis run: | radon cc src/ -a -nc radon mi src/ -nc ``` -------------------------------- ### Batch Operations with itertools in Python Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Provides an example of efficient batch processing using Python's itertools module, specifically chain, islice, tee, and groupby. It shows how to group tokens by scope and resolve them efficiently, handling singletons and transients separately. ```python from itertools import chain, islice, tee, groupby from typing import List, Dict, Any # Assuming Token and Scope are defined elsewhere class Token: def __init__(self, scope): self.scope = scope class Scope: SINGLETON = 'singleton' class Container: def _batch_singletons(self, group): # Placeholder for actual singleton batch resolution return {} def get(self, token: Token) -> Any: # Placeholder for actual token resolution return None def batch_resolve(self, tokens: List[Token]) -> Dict[Token, Any]: """Resolve multiple dependencies efficiently.""" # Group by scope for optimal resolution by_scope = groupby(tokens, key=lambda t: t.scope) results = {} for scope, group in by_scope: if scope == Scope.SINGLETON: # Resolve singletons in parallel results.update(self._batch_singletons(group)) else: # Chain transient resolutions for token in group: results[token] = self.get(token) return results ``` -------------------------------- ### Compatibility Layer for Legacy DI Frameworks Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Provides a compatibility layer within the `di` package to facilitate gradual migration from legacy dependency injection frameworks. It includes an `LegacyAdapter` class with `bind` and `get` methods that map to the new `di.Container`'s registration and retrieval mechanisms. ```python # di/compat.py """Compatibility layer for legacy DI frameworks.""" from typing import Any, Type from di import Container, Token class LegacyAdapter: """Adapter for legacy DI patterns.""" def __init__(self, container: Container): self._container = container def bind(self, interface: Type, to: Type) -> None: """Legacy bind method.""" token = Token[interface](str(interface)) self._container.register(token, to) def get(self, interface: Type) -> Any: """Legacy get method.""" token = Token[interface](str(interface)) return self._container.get(token) ``` -------------------------------- ### FastAPI Integration with pyinj Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Demonstrates how to integrate the pyinj dependency injection container with a FastAPI application. It shows the setup of tokens, registration of services, and how to use the `@inject` decorator with `Depends` for dependency resolution in API endpoints. ```python from fastapi import FastAPI, Depends from di import Container, Token, inject # Define tokens DB_POOL = Token[AsyncPGPool]("db_pool") USER_SERVICE = Token[UserService]("user_service") # Setup container container = Container() container.register_async(DB_POOL, create_db_pool) container.register(USER_SERVICE, lambda: UserService( db=container.get(DB_POOL) )) # FastAPI app app = FastAPI() @app.get("/users/{user_id}") @inject async def get_user( user_id: int, service: UserService = Depends(USER_SERVICE) ): return await service.get_user(user_id) ``` -------------------------------- ### Pydantic-Style Validation Error Example Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Provides an example of a ValidationError message for Pydantic-style validation, indicating an invalid provider type and offering a fix with an example. ```python # If validation fails: """ ValidationError: Invalid provider for Token('database', Database) Expected: Callable[[], Database] Got: Fix: Provider must be a callable that returns a Database instance Example: container.register(token, lambda: Database()) """ ``` -------------------------------- ### Serve Documentation Locally Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Builds and serves the project's documentation locally, typically at http://localhost:8000, allowing for easy previewing. Accessible via 'make docs-serve'. ```Bash make docs-serve ``` -------------------------------- ### Build and Publish Pyinj to PyPI using uv Source: https://github.com/qriusglobal/pyinj/blob/main/CLAUDE.md This snippet details the command-line steps to build and publish the Pyinj package to PyPI using the uv build and publish commands. It includes steps for setting the version, building artifacts, and publishing with an API token. ```Shell rm -rf dist uv build uv publish --token "$PYPI_API_TOKEN" ``` -------------------------------- ### PyInj Release Process Overview Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Details the steps involved in releasing a new version of PyInj, including versioning conventions, build process using `uv`, publishing to PyPI, and CI/CD integration. ```Bash # Versioning: follow SemVer. Use pre-release tags (a, b, rc) while in beta; e.g. `1.0.1b1`. # Classifiers: set an appropriate development status (e.g., "Development Status :: 4 - Beta"). # Build locally with uv: # rm -rf dist # uv build # Publish to PyPI with uv: # uv publish --token "$PYPI_API_TOKEN" # CI/CD: # GitHub Actions runs tests with uv on PRs/commits. # Releases are built/published via .github/workflows/publish.yml using uv. # Yanking incorrect releases: # PyPI does not support API/CLI yanking; use the project release UI to “Yank this release”. # You cannot overwrite or reuse a version once uploaded. ``` -------------------------------- ### Migrating from dependency-injector to PyInj Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Compares the syntax for registering dependencies between `dependency-injector` and PyInj, showing how to migrate singleton and configuration providers. ```Python # Before (dependency-injector) # from dependency_injector import containers, providers # class Container(containers.DeclarativeContainer): # config = providers.Configuration() # logger = providers.Singleton(Logger) # After (PyInj) from pyinj import Container, Token, Scope # Assuming Logger class is defined # container = Container() # logger_token = Token[Logger]("logger") # container.register(logger_token, Logger, Scope.SINGLETON) ``` -------------------------------- ### Python Async Composition Root with AsyncExitStack Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Demonstrates setting up the Composition Root in Python using `contextlib.AsyncExitStack` to manage asynchronous resources and dependencies for an application. It shows how to enter resources like HTTP clients and service clients into the stack and wire them together to create the main application object. ```python from contextlib import asynccontextmanager, AsyncExitStack import httpx class App: def __init__(self, job_service): self.job_service = job_service async def run(self): # main application logic ... @asynccontextmanager async def build_app(): async with AsyncExitStack() as stack: # 1. Enter resources into the stack http_client = await stack.enter_async_context(httpx.AsyncClient()) s8_client = await stack.enter_async_context( ServiceM8Client.with_dependencies(auth=..., http_client=http_client) ) # 2. Wire dependencies job_service = JobService(s8_client) # 3. Yield the final application object yield App(job_service=job_service) # 4. On exit, stack.aclose() is called implicitly, cleaning up resources async def main(): async with build_app() as app: await app.run() ``` -------------------------------- ### Python SDK Pattern DI Container Initialization Source: https://github.com/qriusglobal/pyinj/blob/main/docs/PEP-DI-001-dependency-injection-specification.rst Illustrates the SDK pattern for dependency injection, showing how to define a Token for an HTTP client factory and initialize a DI container. ```Python # _internal/di_container.py HTTP_CLIENT_FACTORY: Token[Callable[[], HttpClient]] = Token("http_client_factory") container = Container() ``` -------------------------------- ### Async Providers and Cleanup Source: https://github.com/qriusglobal/pyinj/blob/main/docs/usage.md Illustrates how to manage asynchronous providers and ensure proper cleanup using `aget` and `dispose`. This is crucial for resources like database connections that require asynchronous initialization and closing. ```Python class DatabaseConnection: async def aclose(self) -> None: ... DB = Token[DatabaseConnection]("db") container.register(DB, DatabaseConnection, Scope.SINGLETON) conn = await container.aget(DB) await container.dispose() ``` -------------------------------- ### Build Documentation Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Builds the project's documentation using MkDocs. This command can be run manually or via 'make docs'. ```Bash # Build documentation make docs # or manually: mkdocs build ``` -------------------------------- ### Python Pyinj Package Typing Marker Source: https://github.com/qriusglobal/pyinj/blob/main/CLAUDE.md This snippet indicates the presence and location of the `py.typed` file within the Pyinj project structure. This file is crucial for enabling static type checking for the package when it's installed. ```Text src/pyinj/py.typed ``` -------------------------------- ### Clone PyInj Repository Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Clones the PyInj repository from GitHub to your local machine. This is the first step in setting up the development environment. ```Bash git clone https://github.com/YOUR_USERNAME/pyinj.git cd pyinj ``` -------------------------------- ### Core Interfaces (Protocols) in Python Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Defines core service contracts using Python's typing.Protocol for loose coupling and testability. Includes examples for HttpClient, Clock, and CircuitBreaker interfaces, with runtime_checkable decorator for validation. ```python from typing import Any, Protocol, runtime_checkable, Awaitable, Callable, TypeVar from datetime import datetime # Dummy types for demonstration class ResponseLike: pass T = TypeVar('T') @runtime_checkable class HttpClient(Protocol): async def request(self, method: str, url: str, **kwargs: Any) -> ResponseLike: ... @runtime_checkable class Clock(Protocol): def now(self) -> datetime: ... def monotonic(self) -> float: ... @runtime_checkable class CircuitBreaker(Protocol): async def call(self, func: Callable[[], Awaitable[T]]) -> T: ... def is_open(self) -> bool: ... def record_success(self) -> None: ... def record_failure(self) -> None: ... ``` -------------------------------- ### GitHub Actions Publish Workflow for Pyinj Source: https://github.com/qriusglobal/pyinj/blob/main/CLAUDE.md This snippet outlines a GitHub Actions workflow for publishing the Pyinj package to PyPI. It includes steps to clear the dist directory, build the package using `uv build`, and publish it using `uv publish` with an API token. It also mentions the alternative of using Trusted Publishing (OIDC). ```YAML # .github/workflows/publish.yml name: Publish on: push: tags: - "v*.*.*" jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup uv uses: astral-sh/setup-uv@v4 - name: Build and publish env: PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }} run: | rm -rf dist uv build uv publish --token "$PYPI_API_TOKEN" # Alternative: Use Trusted Publishing (OIDC) # - name: Publish with Trusted Publishing # uses: pypa/gh-action-pypi-publish@release/v1 # with: # repository-url: https://upload.pypi.org/legacy/ # # Use the OIDC token provided by GitHub Actions # # --trusted-publishing flag is implicitly handled by the action ``` -------------------------------- ### Python Dependency Injection Container Initialization Source: https://github.com/qriusglobal/pyinj/blob/main/docs/PEP-DI-001-dependency-injection-specification.rst Demonstrates the initialization of a dependency injection container and the registration of a database dependency with singleton scope. ```Python from pyinj import Container, Token, Scope # Create container container = Container() # Define token DB_TOKEN = Token[Database]("database") # Register provider container.register(DB_TOKEN, create_database, Scope.SINGLETON) # Resolve dependency db = container.get(DB_TOKEN) # Cleanup await container.aclose() ``` -------------------------------- ### Run All Quality Checks Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Executes formatting, linting, testing, and documentation building in a single command. This is a comprehensive check for code quality and project integrity. Triggered by 'make all'. ```Bash make all ``` -------------------------------- ### Scoped Container for Request-Level Dependencies in Python Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst This Python example demonstrates using a scoped container for managing request-specific dependencies. It shows how to create a new container instance using `di.core.scoped` and register/resolve dependencies within that scope to prevent state leakage. ```python from di.core import Container, scoped from di import Token # Define placeholder tokens and services USER_TOKEN: Token[str] = Token("user") MY_SERVICE_TOKEN: Token[str] = Token("my_service") class MyService: async def do_work(self): print("MyService doing work...") async def handle_request(): root = Container() # Simulate current user current_user = "test_user" # Simulate a service provider root.register(MY_SERVICE_TOKEN, lambda: MyService()) async with scoped(root) as request_container: # Register request-specific items, e.g., a user object request_container.register(USER_TOKEN, lambda: current_user) # Resolve dependencies using the scoped container service = await request_container.aget(MY_SERVICE_TOKEN) await service.do_work() # Example of running the handler import asyncio asyncio.run(handle_request()) ``` -------------------------------- ### Execute Project Build, Test, and Security Commands Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst This section provides essential bash commands for managing the pyinj project. It covers building the project, uploading distributions, performing static typing checks, auditing for security vulnerabilities, running tests, and executing performance benchmarks. ```bash uv build # or python -m build ``` ```bash uvx twine upload dist/* ``` ```bash uv run basedpyright # or uv run mypy ``` ```bash uv run bandit -r src/ # and uv run safety check ``` ```bash uv run pytest -q # or uv run pytest -q --anyio-mode=auto ``` ```bash uv run pytest benchmarks/ --benchmark-only ``` -------------------------------- ### Basic ServiceM8 Client Usage Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Shows basic usage of the ServiceM8Client as an async context manager, handling resource creation and cleanup automatically. Fetches a list of jobs. ```python from servicem8py import ServiceM8Client async with ServiceM8Client(auth=my_auth) as client: jobs = await client.job.list() print(f"Found {len(jobs)} jobs.") ``` -------------------------------- ### Python Pyinj Project Configuration (pyproject.toml) Source: https://github.com/qriusglobal/pyinj/blob/main/CLAUDE.md This snippet represents the structure of a `pyproject.toml` file for the Pyinj project. It includes project metadata such as name, version, description, authors, repository URLs, and classifiers, which are essential for packaging and distribution. ```TOML [project] name = "pyinj" version = "0.1.0" description = "A type-safe DI container for Python 3.13+" authors = [ { name="QriusGlobal", email="contact@qrius.global" }, ] readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.13" classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.13", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ # Add dependencies here, e.g., "fastcore" ] [project.urls] Homepage = "https://github.com/qriusglobal/pyinj" Repository = "https://github.com/qriusglobal/pyinj" ``` -------------------------------- ### Python SDK Pattern Client Initialization with DI Source: https://github.com/qriusglobal/pyinj/blob/main/docs/PEP-DI-001-dependency-injection-specification.rst Demonstrates how a Client class in the SDK pattern utilizes dependency injection by obtaining an HttpClient from the container during initialization. ```Python class Client: def __init__(self, *, http: HttpClient | None = None): self._http = http or container.get(HTTP_CLIENT_FACTORY)() ``` -------------------------------- ### CLI Application Integration with PyInj using Click Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Shows how to integrate PyInj with Click for command-line applications, including setting up the container in the context object. ```Python import click from pyinj import Container, Token, Scope # Assuming service_token and process are defined @click.command() @click.pass_context def cli(ctx): ctx.obj = Container() # Register services... # Example: ctx.obj.register(service_token, ServiceImplementation, Scope.TRANSIENT) pass @cli.command() @click.pass_context def process(ctx): container = ctx.obj # Assuming service_token is defined elsewhere # service = container.get(service_token) # service.process() pass # Placeholder for actual usage # To run: cli() ``` -------------------------------- ### Migrating from injector to PyInj Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Shows the differences in binding dependencies and decorating functions between the `injector` library and PyInj. ```Python # Before (injector) # from injector import Injector, inject, singleton # injector = Injector() # injector.binder.bind(Logger, to=ConsoleLogger, scope=singleton) # @inject # def my_function(logger: Logger) -> None: ... # After (PyInj) from pyinj import Container, Token, Scope # Assuming Logger and ConsoleLogger classes are defined # container = Container() # container.register(Token[Logger]("logger"), ConsoleLogger, Scope.SINGLETON) # @container.inject # def my_function(logger: Logger) -> None: ... ``` -------------------------------- ### Python Pyinj Initialization File Source: https://github.com/qriusglobal/pyinj/blob/main/CLAUDE.md This snippet shows the `__init__.py` file for the Pyinj package, specifically defining the `__version__` attribute. This is a common practice in Python packages to expose the package's version. ```Python # src/pyinj/__init__.py __version__ = "0.1.0" # Other imports and definitions... ``` -------------------------------- ### Lazy Initialization in Python Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Demonstrates lazy initialization using a LazyProvider class. This defers the creation of an instance until it's first accessed, improving startup performance by delaying expensive operations. ```python class LazyProvider: """Lazy provider that defers creation.""" __slots__ = ('_factory', '_instance', '_initialized') def __init__(self, factory): self._factory = factory self._instance = None self._initialized = False def __call__(self): if not self._initialized: self._instance = self._factory() self._initialized = True # Clear factory reference to free memory self._factory = None return self._instance ``` -------------------------------- ### Auto-Registration using Metaclass Source: https://github.com/qriusglobal/pyinj/blob/main/docs/usage.md Shows how to automatically register classes for dependency injection using a metaclass. The Injectable metaclass simplifies the process by allowing configuration directly within the class definition. ```Python from pyinj import Injectable class EmailService(metaclass=Injectable): __injectable__ = True __token_name__ = "email_service" __scope__ = Scope.SINGLETON def __init__(self, logger: Logger): self.logger = logger ``` -------------------------------- ### Async Providers and Concurrent Resolution in PyInj Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Shows how to register asynchronous providers and perform concurrent dependency resolution using PyInj. It highlights the singleton behavior with `asyncio.gather`. ```Python import asyncio from pyinj import Container, Token, Scope # Assuming AsyncService, service_token are defined async def create_async_service() -> AsyncService: service = AsyncService() await service.initialize() return service container = Container() container.register(service_token, create_async_service, Scope.SINGLETON) # Concurrent resolution with race condition protection async def resolve_concurrently(): results = await asyncio.gather(*[ container.aget(service_token) for _ in range(100) ]) # All results are the same instance (singleton) assert all(r is results[0] for r in results) # To run this: asyncio.run(resolve_concurrently()) ``` -------------------------------- ### Smart Caching Strategies with Container Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Demonstrates smart caching strategies within a Container class using functools.lru_cache for signature analysis and weakref.WeakValueDictionary for transient instances. ```python from functools import lru_cache from weakref import WeakValueDictionary import inspect class Container: def __init__(self): self._singletons = {} self._transients = WeakValueDictionary() @lru_cache(maxsize=1024) def _analyze_signature(self, func): """Cache expensive signature analysis.""" return inspect.signature(func) ``` -------------------------------- ### Run Full Test Suite Source: https://github.com/qriusglobal/pyinj/blob/main/CONTRIBUTING.md Before submitting changes, ensure the full test suite is executed. This command verifies the integrity and functionality of the project. ```shell make all ``` -------------------------------- ### Configure pyproject.toml for pyinj Project Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst This TOML configuration file defines the build system, project metadata, dependencies (including optional ones for development, testing, security, and docs), and project URLs for the pyinj Python package. It also includes Hatch build targets and versioning configuration. ```toml [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "di-core" version = "2.0.0" description = "A secure, type-safe, async-native DI container for Python" readme = "README.md" requires-python = ">=3.12" license = {text = "MIT"} authors = [ {name = "ServiceM8Py Team", email = "team@servicem8py.io"}, {name = "Mishal Rahman", email = "mishal@example.com"}, ] maintainers = [ {name = "Architecture Team", email = "arch@servicem8py.io"}, ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Typing :: Typed", "Framework :: AsyncIO", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Application Frameworks", ] dependencies = [] # stdlib-only [project.optional-dependencies] dev = [ "basedpyright>=1.21.0", "mypy>=1.11.0", "ruff>=0.8.0", "black>=24.10.0", "pre-commit>=3.8.0", ] test = [ "pytest>=8.3.0", "pytest-asyncio>=0.24.0", "pytest-cov>=5.0.0", "pytest-timeout>=2.3.0", "hypothesis>=6.100.0", "pytest-benchmark>=4.0.0", ] security = [ "bandit>=1.7.10", "safety>=3.2.0", "pip-audit>=2.7.0", ] docs = [ "sphinx>=8.0.0", "sphinx-rtd-theme>=2.0.0", "sphinx-autodoc-typehints>=2.5.0", ] [project.urls] Homepage = "https://github.com/servicem8py/di-core" Documentation = "https://di-core.readthedocs.io" Repository = "https://github.com/servicem8py/di-core.git" Issues = "https://github.com/servicem8py/di-core/issues" Changelog = "https://github.com/servicem8py/di-core/blob/main/CHANGELOG.md" [tool.hatch.build.targets.wheel] packages = ["src/di"] [tool.hatch.version] path = "src/di/__init__.py" [tool.coverage.run] branch = true source = ["di"] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "if TYPE_CHECKING:", "raise NotImplementedError", "@abstractmethod", ] ``` -------------------------------- ### Testing Dependencies with Mocking and Overrides Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Explains how to facilitate testing by overriding registered dependencies with mock objects. It shows how to register a production logger, override it with a mock for testing, and verify interactions. ```Python # Production setup container.register(logger_token, ConsoleLogger) # Test override test_logger = Mock(spec=Logger) container.override(logger_token, test_logger) # Test your code service = container.get(service_token) service.do_something() # Verify interactions test_logger.info.assert_called_with("Expected message") # Cleanup container.clear_overrides() ``` -------------------------------- ### Given Instances and Contextual Overrides in Python Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Illustrates a Scala-inspired pattern for registering 'given' instances by type and temporarily overriding them using a 'using' clause within a specific context. This facilitates type-based automatic resolution and controlled dependency injection. ```python # Register given instances by type container.given(Database, lambda: PostgresDB()) container.given(int, lambda: 42) # Default int value # Temporary override with using clause with container.using(Database=test_db): # test_db is used in this context service = container.get(ServiceToken) ``` -------------------------------- ### Migrate from dependency-injector to pyinj Source: https://github.com/qriusglobal/pyinj/blob/main/ref/DI_SPEC.rst Demonstrates the conversion of a dependency-injector container definition to the pyinj specification. It shows the shift from `DeclarativeContainer` and `providers` to `Container` and `Token` for defining dependencies and registering services. ```python # Before (dependency-injector) from dependency_injector import containers, providers class Container(containers.DeclarativeContainer): config = providers.Configuration() http_client = providers.Singleton(httpx.AsyncClient) api_client = providers.Factory( APIClient, http_client=http_client, ) # After (this spec) from di import Container, Token HTTP_CLIENT = Token[httpx.AsyncClient]("http_client") API_CLIENT = Token[APIClient]("api_client") container = Container() container.register_async(HTTP_CLIENT, httpx.AsyncClient) container.register(API_CLIENT, lambda: APIClient( http_client=container.get(HTTP_CLIENT) )) ``` -------------------------------- ### Python Test Override Helper for DI Source: https://github.com/qriusglobal/pyinj/blob/main/docs/PEP-DI-001-dependency-injection-specification.rst Provides a context manager helper function for testing in the SDK pattern, allowing temporary overrides of DI container registrations, such as the HTTP client factory. ```Python # tests/conftest.py @contextmanager def use_overrides(**kw: Any): mapping: dict[Any, Any] = {} if "http_client_factory" in kw: mapping[HTTP_CLIENT_FACTORY] = kw["http_client_factory"] with container.use_overrides(mapping): yield ``` -------------------------------- ### Registering Dependencies with Scopes in PyInj Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Demonstrates how to register dependencies with different scopes (singleton, transient, request) using PyInj's container. This is fundamental for managing object lifecycles. ```Python from pyinj import Container, Token, Scope # Assuming config_token, request_token, user_token, load_config, create_request, get_current_user are defined container = Container() # Singleton - one instance per container container.register(config_token, load_config, Scope.SINGLETON) # Transient - new instance every time container.register(request_token, create_request, Scope.TRANSIENT) # Request/Session - scoped to request/session context container.register(user_token, get_current_user, Scope.REQUEST) ``` -------------------------------- ### Async-Safe Resolution and Cleanup Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Demonstrates PyInj's support for asynchronous operations, including async-safe resolution of dependencies like `DatabaseConnection` and automatic cleanup of resources using `aclose`. ```Python class DatabaseConnection: async def connect(self) -> None: print("Connecting to database...") async def aclose(self) -> None: print("Closing database connection...") container.register( Token[DatabaseConnection]("db"), DatabaseConnection, Scope.SINGLETON ) # Async resolution db = await container.aget(Token[DatabaseConnection]("db")) await db.connect() # Automatic cleanup await container.dispose() # Safely closes all resources ``` -------------------------------- ### Django/Flask Integration with PyInj Source: https://github.com/qriusglobal/pyinj/blob/main/README.md Demonstrates how to set up and use a global PyInj container within Django or Flask applications for managing dependencies in views. ```Python from pyinj import Container, Token, Scope # Assuming service_token and handle_request are defined # Django settings.py # Global container DI_CONTAINER = Container() # Example registration (could be in an app's ready() method or similar) # DI_CONTAINER.register(service_token, ServiceImplementation, Scope.REQUEST) # In views (e.g., Django views.py or Flask routes.py) def my_view(request): # Assuming service_token is defined elsewhere # service = DI_CONTAINER.get(service_token) # return service.handle_request(request) pass # Placeholder for actual usage ``` -------------------------------- ### Python Dependency Injection Token Creation Test Source: https://github.com/qriusglobal/pyinj/blob/main/docs/PEP-DI-001-dependency-injection-specification.rst Tests the basic creation of a token with a string name and verifies its name attribute. ```Python def test_token_creation(): token = Token[str]("test") assert token.name == "test" ```