Skip to main content

Module Dependency

A Dependency Injection Framework for Modular Embedded Python Applications.

Overview

The goal of this project is to provide a comprehensive framework for managing structure for complex Python applications. The framework is designed to be modular, allowing developers to define components, interfaces, and instances that can be easily managed and injected throughout the application.

Declare components with interfaces, provide multiple implementations of them, and manage which implementation to use at runtime. Multiple components can be organized and composed together to form complex behaviors using modular design principles.

This repository includes a working example of a simple application that demonstrates these concepts in action. Based on a real-world use case, the example showcases how to effectively manage dependencies and implement modular design patterns in an embedded Python environment.

Install

This project is available on PyPI on module_dependency. It can be installed using pip:

pip install module-dependency

Documentation are available on GitHub Pages.

Core Components

The project is built around three components that implement different aspects of dependency management:

1. Module

  • Acts as a container for organizing and grouping related dependencies
  • Facilitates modular design and hierarchical structuring of application components
from dependency.core import Module, module
from ...plugin.........module import ParentModule

@module(
    module=ParentModule,  # Declares the parent module (leave empty for plugins)
)
class SomeModule(Module):
    """This is a module class. Use this to group related components.
    """
    pass

2. Component

  • Defines abstract interfaces or contracts for dependencies
  • Promotes loose coupling and enables easier testing and maintenance
from abc import abstractmethod
from dependency.core import Component, component
from ...plugin.........module import SomeModule

@component(
    module=SomeModule,     # Declares the module or plugin this component belongs to
)
class SomeService(Component):
    """This is the component class. A instance will be injected here.
       Components are only started when provided or bootstrapped.
       Components also defines the interface for all instances.
    """
    @abstractmethod
    def method(self, ...) -> ...:
        pass

3. Instance

  • Delivers concrete implementations of Components
  • Manages the lifecycle and injection of dependency objects
from dependency_injector.wiring import inject
from dependency.core import instance, providers
from dependency.core.injection import LazyProvide
from ...plugin.........component import SomeService
from ...plugin...other_component import OtherService
from ...plugin...........product import SomeProduct

@instance(
    imports=[OtherService, ...],    # List of dependencies (components) that this product needs
    provider=providers.Singleton,   # Provider type from di (Singleton, Factory, Resource)
    bootstrap=False,                # Whether to bootstrap on application start
)
class ImplementedSomeService(SomeService):
    """This is a instance class. Here the component is implemented.
       Instances are injected into the respective components when provided.
       Instances must inherit from the component class and implement all its methods.
    """
    def __init__(self) -> None:
        """Init method will be called when the instance is started.
           This will happen once for singleton and every time for factories.
        """
        # Once declared, i can use the dependencies for the class
        self.dependency: OtherService = OtherService.provide()

    @inject
    def method(self,
        # Dependencies also can be provided using @inject decorator with LazyProvide
        # With @inject always use LazyProvide, to avoid deferred evaluation issues.
        dependency: OtherService = LazyProvide(OtherService.reference),
    ...) -> ...:
        """Methods declared in the interface must be implemented.
        """
        # Once declared, i can safely create any product
        # Products are just normal classes (see next section)
        product = SomeProduct()

        # You can do anything here
        do_something()

These components work together to create a powerful and flexible dependency injection system, allowing for more maintainable and testable Python applications.

Extra Components

The project has additional components that enhance its functionality and organization. These components include:

1. Entrypoint

  • Represents a entrypoint for the application
  • Responsible for initializing and starting the application
from dependency.core import Entrypoint, Container
from ...plugin...... import SomePlugin

class SomeApplication(Entrypoint):
    """This is an application entry point.
       Plugins included here will be loaded and initialized.
    """
    def __init__(self) -> None:
        # Declare all the plugins that will be used in the application
        # Its recommended to declare the plugins list them in a separate file
        # You can also include in the same file all the instances imports
        PLUGINS = [
            SomePlugin,
            ...
        ]

        # This is the main container, it will hold all the containers and providers
        # Requires to have a valid configuration that will be used to initialize plugins
        container = Container.from_dict(config={...}, required=True)
        super().__init__(container, PLUGINS)

        # Import all the instances that will be used on the application
        # You can apply some logic to determine which instances to import
        # This will automatically generate the internal provider structure
        import ...plugin.........instance

        # Once all the plugins and instances are imported, we can initialize the application
        super().initialize()

2. Plugin

  • Represents a special module that can be included in the application
  • Provides additional functionality and features to the application
from pydantic import BaseModel
from dependency.core import Plugin, PluginMeta, module

class SomePluginConfig(BaseModel):
    """Include configuration options for the plugin.
    """
    pass

@module()
class SomePlugin(Plugin):
    """This is a plugin class. Plugins can be included in the application.
       Plugins are modules that provide additional functionality.
    """
    # Meta information about the plugin (only affects logging)
    meta = PluginMeta(name="SomePlugin", version="0.0.1")

    # Type hint for the plugin configuration
    # On startup, config will be instantiated using the container config
    config: SomePluginConfig

3. Product

  • Represents a class that requires dependencies injected from the framework
  • Allows to provide standalone classes without the need to define new providers
from dependency.core import Product, product, providers
from dependency.core.injection import LazyProvide, inject
from ...plugin.........component import SomeService
from ...plugin.....other_product import OtherProduct

@product(
    module=SomeModule,              # Declares the module or plugin this component belongs to
    imports=[SomeService, ...],     # List of dependencies (components) that this product needs
    provider=providers.Factory,   # Provider type (Singleton, Factory, Resource)
)
class SomeProduct(Interface, Product):
    """This is the product class. This class will check for its dependencies.
       Products must be declared in some instance and can be instantiated as normal classes.
    """
    def __init__(self, ...) -> None:
        # Dependencies can be used in the same way as before
        self.dependency: SomeService = SomeService.provide()

    @inject
    def method(self,
        # Dependencies also can be provided using @inject decorator with LazyProvide
        # With @inject always use LazyProvide, to avoid deferred evaluation issues.
        dependency: SomeService = LazyProvide(SomeService.reference),
    ...) -> ...:
        """Product interface can be defined using normal inheritance.
        """
        # Once declared, i can safely create any sub-product
        # Products are just normal classes (see next section)
        product = OtherProduct()

        # You can do anything here
        do_something()

Important Notes

  • Remember to declare all the dependencies you need in the imports parameter of the @instance or @product decorator.
  • Read the documentation carefully and refer to the examples to understand the framework's behavior.

Usage Examples

This repository includes a practical example demonstrating how to use the framework. You can find this example in the example directory. It showcases the implementation of the core components and how they interact to manage dependencies effectively in a sample application.

Future Work

This project is a work in progress, and there are several improvements and enhancements planned for the future.

Some planned features are:

  • Add pre-defined components for common patterns and use cases
  • Dependency CLI support for easier interaction with the framework
  • Pytest testing framework integration for better test management

Some new improvements that has been recently added:

  • Enhance documentation and examples for better understanding
  • Implement framework API and extension points for customization
  • Improve injection resolution and initialization process
  • Visualization tools for dependency graphs and relationships

Pending issues that eventually will be addressed:

  • Migration guide from previous versions (some breaking changes were introduced)

Aknowledgements

This project depends on:

  • dependency-injector a robust and flexible framework for dependency injection in Python.
  • pydantic a data validation and settings management library using Python type annotations.
  • jinja2 a modern and designer-friendly templating engine for Python.

Thanks to Reite for providing inspiration and guidance throughout the development of this project.

Metadata

Release files for module-dependency 1.1.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for module-dependency 1.1.7
File Size Uploaded Provenance Actions
module_dependency-1.1.7.tar.gz 71.2 kB With provenance Details for module_dependency-1.1.7.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for module-dependency 1.1.7
File Interpreter ABI Platform Actions
module_dependency-1.1.7-py3-none-any.whl Python 3 none any Details for module_dependency-1.1.7-py3-none-any.whl

Total release size: 149.7 kB

Release files / module_dependency-1.1.7.tar.gz

Download URL module_dependency-1.1.7.tar.gz
Size 71.2 kB
Tags Source
SHA-256 checksum
How to use checksums
55760c8786737ef3ce107a054d512722725aa87c33ded712f7702b932ad5b084
BLAKE2b-256 checksum
How to use checksums
cf17a3c04081f1f87844dca9c81b02ea23f8baf91d2365d89d76341084b8f61b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 4, 2026.

Transparency log

Release files / module_dependency-1.1.7-py3-none-any.whl

Download URL module_dependency-1.1.7-py3-none-any.whl
Size 78.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
78150684c45b867796c8437789084fdd5563b42ea1a0bd66bd85d7b2aaaa91b8
BLAKE2b-256 checksum
How to use checksums
fa0115a42ed1c5c0bb4ca20fa01817e283bfaf2627ca18892a1784e8d9726b3f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 4, 2026.

Transparency log

Security for module-dependency 1.1.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for module_dependency-1.1.7.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 4, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page