the ioc container useful for Interface oriented programming
Reason this release was yanked:
bugfix
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 提供给大模型. 但其中的 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:
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ghostos_container-0.1.1.tar.gz.
File metadata
- Download URL: ghostos_container-0.1.1.tar.gz
- Upload date:
- Size: 11.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.0.1 CPython/3.10.16 Darwin/23.6.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ce7fcb9c1f42da9f0a1ab0a0d752f441d2f1c7289780eed5aaf36bfde5279c0
|
|
| MD5 |
b431f200d04af42318b66242a5644d8d
|
|
| BLAKE2b-256 |
a92a4412201f2cb36e30c7ba7be784c7c938612f664c474e8ee55d1303e8d447
|
File details
Details for the file ghostos_container-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ghostos_container-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.0.1 CPython/3.10.16 Darwin/23.6.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4afe29136d7f980345b399940503d4758852f0a6a4385e4422f0843066f6b69
|
|
| MD5 |
1cfab7b2f36913fc46fb801f7f6fc8d2
|
|
| BLAKE2b-256 |
4e0046914d6842132f24873a020817262b11e5d2265ceefba38ffd5b2c6c5963
|