Skip to main content

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 提供给大模型. 但其中的 Ball 和 LedMatrix 在项目正式启动前都不应该实例化. 尤其是当一个 Meta-Agent 需要分析这段代码时, 它不应该在阅读代码时导致创建和 Sphero Bolt 的连接.

所以 Ball 和 LedMatrix 可以用抽象来设计:

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

Metadata

Release files for ghostos-container 0.2.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 ghostos-container 0.2.7
File Size Uploaded Provenance Actions
ghostos_container-0.2.7.tar.gz 13.8 kB Without provenance Details for ghostos_container-0.2.7.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ghostos-container 0.2.7
File Interpreter ABI Platform Actions
ghostos_container-0.2.7-py3-none-any.whl Python 3 none any Details for ghostos_container-0.2.7-py3-none-any.whl

Total release size: 24.6 kB

Release files / ghostos_container-0.2.7.tar.gz

Download URL ghostos_container-0.2.7.tar.gz
Size 13.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3d4f0f9102e01f4e86c38c918a887a2941b19a7a60e5252ebde62460ef40b03b
BLAKE2b-256 checksum
How to use checksums
dc27bf9347ea2f9da37125528a765fc135f0cf1eb6105c181fd788f7fe9841ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.3

Release files / ghostos_container-0.2.7-py3-none-any.whl

Download URL ghostos_container-0.2.7-py3-none-any.whl
Size 10.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2a4c0b820585728c859403fd90bc6c9a76def83178798b430fcf16f64ec6bb7e
BLAKE2b-256 checksum
How to use checksums
10c47bc645195fc02b92342827d0498934018c9b1a66b22ddcf57f62b30ba3bb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.6.3

Security for ghostos-container 0.2.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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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