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.1.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.1-py3-none-any.whl (10.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: ghostos_container-0.2.1.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.1.tar.gz
Algorithm Hash digest
SHA256 ad97951300352a9ed606bd0a04f0ebee019d258c3e5c3b631ffb091af2d46927
MD5 503002413af7303515f68af3e4c0a06a
BLAKE2b-256 13933353bb1babbc1239092c895f607b0fb43e4140493224bfe02e3147649a03

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for ghostos_container-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9d29d369906f79e64a4821abdc2cd04ac0b2813947c81547e4cd8eb95d392877
MD5 83a8044618f7813b73dcd56e403b32db
BLAKE2b-256 965439901053cc470a1cbff201a072df9998c90cd2544b9872a416563f73bced

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