Skip to main content

the ioc container useful for Interface oriented programming

Project description

GhostOS Container

IoC container for GhostOS.

IoC Container

GhostOS follows the concept of interface-oriented programming to build the project. Most modules are divided into interface and implementation. Register and get implementations by IoC Container.

About IoC: Inverse of Control

Why?

In Java and PHP projects, IoC Container is widely used. For example:

However, in Python projects, it is rarely used, often replaced by singletons and factory methods.

GhostOS introduces the IoC Container, with the most fundamental motivation being to achieve interface-oriented programming and runtime dependency injection. Taking SpheroBoltGPT as an example:

from ghostos.prototypes.spherogpt.bolt import (
    RollFunc,
    Ball,
    Move,
    LedMatrix,
    Animation,
)
from ghostos_moss import Moss as Parent


class Moss(Parent):
    body: Ball
    """your sphero ball body"""

    face: LedMatrix
    """you 8*8 led matrix face"""

这部分代码会被自动反射成 prompt 提供给大模型. 但其中的 BallLedMatrix 在项目正式启动前都不应该实例化. 尤其是当一个 Meta-Agent 需要分析这段代码时, 它不应该在阅读代码时导致创建和 Sphero Bolt 的连接.

所以 BallLedMatrix 可以用抽象来设计:

This part of the code will be automatically reflected as a prompt provided to the large language model. However, Ball and LedMatrix should not be instantiated before the project officially starts.

Especially when a Meta-Agent needs to analyze this code, it should not cause the creation of a connection with Sphero Bolt while reading the code. Therefore, Ball and LedMatrix can be designed abstractly:

class Ball(ABC):
    """
    Sphero bolt body (which is a rolling ball) control interface.
    """

    @abstractmethod
    def new_move(
            self,
            *,
            run: bool = False,
            animation: Optional[Animation] = None,
    ) -> Move:
        """
        create a new Move instance, to define a sequence of movements.
        :param run: run immediately if True, otherwise the move will not execute until run it.
        :param animation: if animation is not none, it will be played while run the move.
        """
        pass

    @abstractmethod
    def run(self, move: Move, stop_at_first: bool = True) -> None:
        """
        run the bolt ball movement
        :param move: the Move instance that defined the movements by calling it methods one by one.
        :param stop_at_first: shall stop any movement of the ball before executing the new move?
        """
        pass

The actual instances are only injected through the container during runtime:

ioc container

Basic Usage

from abc import ABC, abstractmethod
from typing import Type
from ghostos_container import Container, Provider


def test_container_baseline():
    class Abstract(ABC):
        @abstractmethod
        def foo(self) -> int:
            pass

    class Foo(Abstract):
        count = 0

        def foo(self) -> int:
            self.count += 1
            return self.count

    container = Container()

    # set instance
    foo = Foo()
    container.set(Foo, foo)
    assert container.get(Foo) is foo

Provider

Implementations registered through the Container.set method are singletons. In scenarios oriented towards composition, a factory method is needed to obtain dependencies and generate instances. In this case, ghostos_container.Provider can be used:

from abc import ABC, abstractmethod
from typing import Type
from ghostos_container import Container, Provider


def test_container_baseline():
    class Abstract(ABC):
        @abstractmethod
        def foo(self) -> int:
            pass

    class Foo(Abstract):
        def __init__(self, count):
            self.count = count

        def foo(self) -> int:
            return self.count

    class FooProvider(Provider):

        def singleton(self) -> bool:
            return True

        def contract(self) -> Type[Abstract]:
            return Abstract

        def factory(self, con: Container) -> Abstract:
            # get dependencies from con
            count = con.get("count")
            return Foo(count)

    # register
    container = Container()
    container.set("count", 123)
    container.register(FooProvider())

    # get instance
    foo = container.force_fetch(Abstract)
    assert isinstance(foo, Foo)
    assert foo.foo() is 123

And syntax sugar ghostos_container.provide could decorate a factory function into a Provider.

from abc import ABC, abstractmethod
from ghostos_container import Container, provide


class Abstract(ABC):
    @abstractmethod
    def foo(self) -> int:
        pass


class Foo(Abstract):
    def __init__(self, count):
        self.count = count

    def foo(self) -> int:
        return self.count


@provide(Abstract, singleton=True)
def foo_factory(self, con: Container) -> Abstract:
    # get dependencies from con
    count = con.get("count")
    return Foo(count)


# register
container = Container()
container.set("count", 123)
container.register(foo_factory)

# get instance
foo = container.force_fetch(Abstract)
assert isinstance(foo, Foo)
assert foo.foo() is 123

Inheritance

Container is inheritable:

from ghostos_container import Container

container = Container(name="parent")
container.set("foo", "foo")

child_container = Container(parent=container, name="child")
assert child_container.get("foo") == "foo"

When a descendant Container looks for a registered dependency and does not find it, it will recursively search for it in the parent Container.

And Provider can also be inherited by child container:

from ghostos_container import Provider


class MyProvider(Provider):

    def inheritable(self) -> bool:
        return not self.singleton()

All inheritable providers registered in the parent container are also automatically registered in the child container.

Bootstrap and Shutdown

A Container can also serve as a container for starting and shutting down components.

from ghostos_container import Bootstrapper, Container

container = Container()


class MyBootstrapper(Bootstrapper):
    def bootstrap(self, container: Container) -> None:
        # do something 
        ...


# start all the bootstrapper
container.bootstrap()

Bootstrapper can also be defined by ghostos_container.BootstrapProvider.

Container useContainer.add_shutdown register shutdown callback, they are called when Container.shutdown is called.

Container Tree

In App, there are Containers at different levels, with each Container inheriting from its parent Container and managing its own independent set of dependencies.

  • When a child Container registers dependencies, it does not pollute the parent or sibling Containers.
  • When a child Container is destroyed, it does not affect the parent or sibling Containers.

In this way, Container is similar to Python contextvars, which can manage a separate execution context, for example:

  • Process level
  • Thread level
  • Coroutine level

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ghostos_container-0.2.0.tar.gz (13.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ghostos_container-0.2.0-py3-none-any.whl (10.5 kB view details)

Uploaded Python 3

File details

Details for the file ghostos_container-0.2.0.tar.gz.

File metadata

  • Download URL: ghostos_container-0.2.0.tar.gz
  • Upload date:
  • Size: 13.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.3

File hashes

Hashes for ghostos_container-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c298cb426454a7c14e243dfed0fcc76cbbf6b6a6fddf60444485897889ebf679
MD5 1c73fb587563eed5dc51c0529cf9c71e
BLAKE2b-256 ecef8b049aae1b2ef5669b0203f85ca590c05ea9cb96f5eb4e3a8e91cfdff850

See more details on using hashes here.

File details

Details for the file ghostos_container-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ghostos_container-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 79831b389eba6ad6dd7c9742dc478458acc8fb4aee41921d82c635811493a0bb
MD5 7f6f7dee27971327b19f7056b5408607
BLAKE2b-256 f60be97534401963ba1410b929c77dc2a3c94683e9775f517e515ff0119d8c63

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page