commit
006c0a494d
25 changed files with 196 additions and 319 deletions
|
|
@ -1,88 +0,0 @@
|
|||
from typing import final
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from snakia.core.ecs import Component
|
||||
from snakia.core.ecs.system import System
|
||||
from snakia.core.engine import Engine
|
||||
from snakia.core.es import Event
|
||||
from snakia.core.loader import Meta, Plugin, PluginProcessor
|
||||
from snakia.types import Version
|
||||
|
||||
|
||||
class HealthComponent(Component):
|
||||
max_value: int = Field(default=100, ge=0)
|
||||
value: int = Field(default=100, ge=0)
|
||||
|
||||
|
||||
class DamageComponent(Component):
|
||||
damage: int = Field(ge=0)
|
||||
ticks: int = Field(default=1, ge=0)
|
||||
|
||||
|
||||
class HealComponent(Component):
|
||||
heal: int = Field(ge=0)
|
||||
ticks: int = Field(default=1, ge=0)
|
||||
|
||||
|
||||
class DeathEvent(Event):
|
||||
entity: int = Field()
|
||||
|
||||
|
||||
class HealthProcessor(PluginProcessor):
|
||||
def process(self, system: System) -> None:
|
||||
for entity, (heal, health) in system.get_components(
|
||||
HealComponent, HealthComponent
|
||||
):
|
||||
health.value += heal.heal
|
||||
heal.ticks -= 1
|
||||
if heal.ticks <= 0:
|
||||
system.remove_component(entity, HealComponent)
|
||||
for entity, (damage, health) in system.get_components(
|
||||
DamageComponent, HealthComponent
|
||||
):
|
||||
health.value -= damage.damage
|
||||
damage.ticks -= 1
|
||||
if damage.ticks <= 0:
|
||||
system.remove_component(entity, DamageComponent)
|
||||
if health.value <= 0:
|
||||
system.remove_component(entity, HealthComponent)
|
||||
self.plugin.dispatcher.publish(DeathEvent(entity=entity))
|
||||
|
||||
|
||||
@final
|
||||
class HealthPlugin(
|
||||
Plugin,
|
||||
meta=Meta(
|
||||
name="health",
|
||||
author="snakia",
|
||||
version=Version.from_args(1, 0, 0),
|
||||
subscribers=(),
|
||||
processors=(HealthProcessor,),
|
||||
),
|
||||
):
|
||||
def on_load(self) -> None:
|
||||
pass
|
||||
|
||||
def on_unload(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def main() -> None:
|
||||
engine = Engine()
|
||||
engine.loader.register(HealthPlugin)
|
||||
engine.loader.load_all()
|
||||
|
||||
@engine.dispatcher.on(DeathEvent)
|
||||
def on_death(event: DeathEvent) -> None:
|
||||
print(f"Entity: {event.entity} is death!")
|
||||
|
||||
player = engine.system.create_entity()
|
||||
engine.system.add_component(player, HealthComponent())
|
||||
engine.system.add_component(player, DamageComponent(damage=10, ticks=10))
|
||||
|
||||
engine.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
0
py.typed
Normal file
0
py.typed
Normal file
|
|
@ -26,7 +26,7 @@ dependencies = [
|
|||
"types-networkx>=3.5.0.20251106",
|
||||
"typing-extensions>=4.15.0",
|
||||
]
|
||||
license = "CC0-1.0"
|
||||
license = "Unlicense"
|
||||
license-files = ["LICENSE"]
|
||||
|
||||
[project.urls]
|
||||
|
|
|
|||
|
|
@ -3,14 +3,12 @@ from typing import Final
|
|||
|
||||
from .ecs import System
|
||||
from .es import Dispatcher
|
||||
from .loader.loader import Loader
|
||||
|
||||
|
||||
class Engine:
|
||||
__slots__ = (
|
||||
"system",
|
||||
"dispatcher",
|
||||
"loader",
|
||||
"__system_thread",
|
||||
"__dispatcher_thread",
|
||||
)
|
||||
|
|
@ -18,12 +16,13 @@ class Engine:
|
|||
def __init__(self) -> None:
|
||||
self.system: Final = System()
|
||||
self.dispatcher: Final = Dispatcher()
|
||||
self.loader: Final = Loader(self)
|
||||
self.__system_thread: threading.Thread | None = None
|
||||
self.__dispatcher_thread: threading.Thread | None = None
|
||||
|
||||
def start(self) -> None:
|
||||
self.__system_thread = threading.Thread(target=self.system.start, daemon=False)
|
||||
self.__system_thread = threading.Thread(
|
||||
target=self.system.start, daemon=False
|
||||
)
|
||||
self.__dispatcher_thread = threading.Thread(
|
||||
target=self.dispatcher.start, daemon=False
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
from .loadable import Loadable
|
||||
from .meta import Meta
|
||||
from .plugin import Plugin
|
||||
from .plugin_processor import PluginProcessor
|
||||
|
||||
__all__ = ["Loadable", "Meta", "Plugin", "PluginProcessor"]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from snakia.core.engine import Engine
|
||||
|
||||
|
||||
class Loadable(ABC):
|
||||
@abstractmethod
|
||||
def __init__(self, engine: Engine) -> None: ...
|
||||
|
||||
@abstractmethod
|
||||
def load(self) -> None: ...
|
||||
|
||||
@abstractmethod
|
||||
def unload(self) -> None: ...
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Callable, Final
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from snakia.core.engine import Engine
|
||||
from snakia.core.loader import Loadable
|
||||
|
||||
|
||||
class Loader:
|
||||
__slots__ = ("__engine", "__loadables")
|
||||
|
||||
def __init__(self, engine: Engine) -> None:
|
||||
self.__engine: Final = engine
|
||||
self.__loadables: Final[list[Loadable]] = []
|
||||
|
||||
def register(self, loadable: Callable[[Engine], Loadable]) -> None:
|
||||
self.__loadables.append(loadable(self.__engine))
|
||||
|
||||
def load_all(self) -> None:
|
||||
for loadable in self.__loadables:
|
||||
loadable.load()
|
||||
|
||||
def unload_all(self) -> None:
|
||||
for loadable in reversed(self.__loadables):
|
||||
loadable.unload()
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from snakia.core.es import Event, Subscriber
|
||||
from snakia.types import Version
|
||||
|
||||
from .plugin_processor import PluginProcessor
|
||||
|
||||
|
||||
class Meta(BaseModel):
|
||||
model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
|
||||
|
||||
name: str = Field(
|
||||
default="unknown",
|
||||
min_length=4,
|
||||
max_length=32,
|
||||
pattern="^[a-z0-9_]{4,32}$",
|
||||
)
|
||||
author: str = Field(
|
||||
default="unknown",
|
||||
min_length=4,
|
||||
max_length=32,
|
||||
pattern="^[a-z0-9_]{4,32}$",
|
||||
)
|
||||
version: Version = Field(default_factory=lambda: Version(major=1, minor=0, patch=0))
|
||||
|
||||
subscribers: tuple[tuple[type[Event], Subscriber[Event]], ...] = Field(
|
||||
default_factory=tuple
|
||||
)
|
||||
processors: tuple[type[PluginProcessor], ...] = Field(default_factory=tuple)
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
return f"{self.author}.{self.name}"
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from typing import TYPE_CHECKING, ClassVar, Final, final
|
||||
|
||||
from snakia.core.ecs import System
|
||||
from snakia.core.es import Dispatcher
|
||||
|
||||
from .loadable import Loadable
|
||||
from .meta import Meta
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from snakia.core.engine import Engine
|
||||
|
||||
|
||||
class Plugin(Loadable):
|
||||
__meta: ClassVar[Meta]
|
||||
|
||||
@final
|
||||
def __init__(self, engine: Engine) -> None:
|
||||
self.__engine: Final = engine
|
||||
|
||||
@final
|
||||
@property
|
||||
def meta(self) -> Meta:
|
||||
"""The plugin's metadata."""
|
||||
return self.__meta
|
||||
|
||||
@final
|
||||
@property
|
||||
def dispatcher(self) -> Dispatcher:
|
||||
return self.__engine.dispatcher
|
||||
|
||||
@final
|
||||
@property
|
||||
def system(self) -> System:
|
||||
return self.__engine.system
|
||||
|
||||
@final
|
||||
def load(self) -> None:
|
||||
for processor in self.meta.processors:
|
||||
self.__engine.system.add_processor(processor(self))
|
||||
for event_type, subscriber in self.meta.subscribers:
|
||||
self.__engine.dispatcher.subscribe(event_type, subscriber)
|
||||
self.on_load()
|
||||
|
||||
@final
|
||||
def unload(self) -> None:
|
||||
for processor in self.meta.processors:
|
||||
self.__engine.system.remove_processor(processor)
|
||||
for event_type, subscriber in self.meta.subscribers:
|
||||
self.__engine.dispatcher.unsubscribe(event_type, subscriber)
|
||||
self.on_unload()
|
||||
|
||||
@abstractmethod
|
||||
def on_load(self) -> None:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_unload(self) -> None:
|
||||
pass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@final
|
||||
def __init_subclass__(cls, meta: Meta) -> None:
|
||||
pass
|
||||
|
||||
else:
|
||||
|
||||
def __init_subclass__(cls, meta: Meta) -> None:
|
||||
cls.meta = meta
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Final, final
|
||||
|
||||
from snakia.core.ecs import Processor
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .plugin import Plugin
|
||||
|
||||
|
||||
class PluginProcessor(Processor):
|
||||
@final
|
||||
def __init__(self, plugin: Plugin) -> None:
|
||||
self.plugin: Final = plugin
|
||||
|
|
@ -29,8 +29,7 @@ class BaseBindable(Generic[T]):
|
|||
def value(self) -> T:
|
||||
if self.has_value:
|
||||
return self.__value
|
||||
else:
|
||||
return self.default_value
|
||||
return self.default_value
|
||||
|
||||
@property
|
||||
def has_value(self) -> bool:
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ def combine(
|
|||
)
|
||||
|
||||
def subscriber(_: ValueChanged[Any]) -> None:
|
||||
combined.set(combiner(*[*map(lambda s: s.value, sources)]))
|
||||
combined.set(combiner(*map(lambda s: s.value, sources)))
|
||||
|
||||
for source in sources:
|
||||
if isinstance(source, Bindable):
|
||||
|
|
@ -185,7 +185,7 @@ def async_combine(
|
|||
)
|
||||
|
||||
async def subscriber(_: ValueChanged[Any]) -> None:
|
||||
result = await combiner(*[*map(lambda s: s.value, sources)])
|
||||
result = await combiner(*map(lambda s: s.value, sources))
|
||||
await combined.set(result)
|
||||
|
||||
for source in sources:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ from .bool import BoolField
|
|||
from .field import Field
|
||||
from .float import FloatField
|
||||
from .int import IntField
|
||||
from .list import ListField
|
||||
from .optional import OptionalField
|
||||
from .str import StrField
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -11,5 +13,7 @@ __all__ = [
|
|||
"BoolField",
|
||||
"FloatField",
|
||||
"IntField",
|
||||
"ListField",
|
||||
"OptionalField",
|
||||
"StrField",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import pickle
|
||||
from typing import Final, Generic, TypeVar
|
||||
from typing import Callable, Final, Generic, TypeVar, overload
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from snakia.types import Unset
|
||||
|
||||
from .field import Field
|
||||
|
||||
|
|
@ -9,15 +13,40 @@ T = TypeVar("T")
|
|||
class AutoField(Field[T], Generic[T]):
|
||||
__slots__ = ("__target_type",)
|
||||
|
||||
def __init__(self, default_value: T, *, target_type: type[T] | None = None) -> None:
|
||||
super().__init__(default_value)
|
||||
self.__target_type: Final = target_type
|
||||
@overload
|
||||
def __init__(
|
||||
self, default_value: T, *, target_type: type[T] | Unset = Unset()
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
default_factory: Callable[[Self], T],
|
||||
target_type: type[T] | Unset = Unset(),
|
||||
) -> None: ...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
default_value: T | Unset = Unset(),
|
||||
*,
|
||||
default_factory: Callable[[Self], T] | Unset = Unset(),
|
||||
target_type: type[T] | Unset = Unset(),
|
||||
) -> None:
|
||||
if not Unset.itis(default_factory):
|
||||
super().__init__(default_factory=Unset.unwrap(default_factory))
|
||||
elif not Unset.itis(default_value):
|
||||
super().__init__(Unset.unwrap(default_value))
|
||||
else:
|
||||
super().__init__()
|
||||
self.__target_type: Final[type] = Unset.unwrap_or(target_type, object)
|
||||
|
||||
def serialize(self, value: T, /) -> bytes:
|
||||
return pickle.dumps(value)
|
||||
|
||||
def deserialize(self, serialized: bytes, /) -> T:
|
||||
value = pickle.loads(serialized)
|
||||
if not isinstance(value, self.__target_type or object):
|
||||
return self.default_value
|
||||
|
||||
if not isinstance(value, self.__target_type):
|
||||
return self._get_default()
|
||||
return value # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Callable, Final, Generic, TypeVar, final
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Generic,
|
||||
TypeVar,
|
||||
final,
|
||||
)
|
||||
|
||||
from snakia.property.priv_property import PrivProperty
|
||||
from snakia.utils import inherit
|
||||
|
|
@ -11,10 +18,6 @@ R = TypeVar("R")
|
|||
|
||||
|
||||
class Field(ABC, PrivProperty[T], Generic[T]):
|
||||
def __init__(self, default_value: T) -> None:
|
||||
self.default_value: Final[T] = default_value
|
||||
super().__init__(default_value)
|
||||
|
||||
@abstractmethod
|
||||
def serialize(self, value: T, /) -> bytes:
|
||||
"""Serialize a value
|
||||
|
|
@ -42,14 +45,18 @@ class Field(ABC, PrivProperty[T], Generic[T]):
|
|||
serialize: Callable[[Field[R], R], bytes],
|
||||
deserialize: Callable[[Field[R], bytes], R],
|
||||
) -> type[Field[R]]:
|
||||
return inherit(cls, {"serialize": serialize, "deserialize": deserialize})
|
||||
return inherit(
|
||||
cls, {"serialize": serialize, "deserialize": deserialize}
|
||||
)
|
||||
|
||||
@final
|
||||
@staticmethod
|
||||
def get_fields(class_: type[Any] | Any, /) -> dict[str, Field[Any]]:
|
||||
if not isinstance(class_, type):
|
||||
class_ = class_.__class__
|
||||
return {k: v for k, v in class_.__dict__.items() if isinstance(v, Field)}
|
||||
return {
|
||||
k: v for k, v in class_.__dict__.items() if isinstance(v, Field)
|
||||
}
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
|
|
|
|||
38
src/snakia/field/list.py
Normal file
38
src/snakia/field/list.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from typing import Callable, Final, Iterable, TypeVar
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from .field import Field
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class ListField(Field[list[T]]):
|
||||
def __init__(
|
||||
self,
|
||||
field: Field[T],
|
||||
*,
|
||||
length_size: int = 1,
|
||||
default_factory: Callable[[Self], Iterable[T]] = lambda _: (),
|
||||
) -> None:
|
||||
self.length_size: Final[int] = length_size
|
||||
self.field: Final = field
|
||||
super().__init__(default_factory=lambda s: [*default_factory(s)])
|
||||
|
||||
def serialize(self, items: list[T], /) -> bytes:
|
||||
result = b""
|
||||
for item in items:
|
||||
value = self.field.serialize(item)
|
||||
length_prefix = len(value).to_bytes(self.length_size, "big")
|
||||
result += length_prefix + value
|
||||
return result
|
||||
|
||||
def deserialize(self, serialized: bytes, /) -> list[T]:
|
||||
result = []
|
||||
while serialized:
|
||||
length = int.from_bytes(serialized[: self.length_size], "big")
|
||||
serialized = serialized[self.length_size :]
|
||||
item = self.field.deserialize(serialized[:length])
|
||||
serialized = serialized[length:]
|
||||
result.append(item)
|
||||
return result
|
||||
28
src/snakia/field/optional.py
Normal file
28
src/snakia/field/optional.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from typing import Final, TypeVar
|
||||
|
||||
from .field import Field
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class OptionalField(Field[T | None]):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
field: Field[T],
|
||||
*,
|
||||
none_value: bytes = b"",
|
||||
) -> None:
|
||||
super().__init__(None)
|
||||
self.none_value: Final = none_value
|
||||
self.field: Final = field
|
||||
|
||||
def serialize(self, value: T | None, /) -> bytes:
|
||||
if value is None:
|
||||
return self.none_value
|
||||
return self.field.serialize(value)
|
||||
|
||||
def deserialize(self, serialized: bytes, /) -> T | None:
|
||||
if serialized == self.none_value:
|
||||
return None
|
||||
return self.field.deserialize(serialized)
|
||||
|
|
@ -4,6 +4,8 @@ from .bool import BoolField as bool
|
|||
from .field import Field as field
|
||||
from .float import FloatField as float
|
||||
from .int import IntField as int
|
||||
from .list import ListField as list
|
||||
from .optional import OptionalField as optional
|
||||
from .str import StrField as str
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -12,5 +14,7 @@ __all__ = [
|
|||
"field",
|
||||
"float",
|
||||
"int",
|
||||
"list",
|
||||
"optional",
|
||||
"str",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,22 +1,44 @@
|
|||
from typing import Any, Generic, TypeVar
|
||||
from typing import Any, Callable, Final, Generic, TypeVar, overload
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from snakia.types import Unset
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class PrivProperty(Generic[T]):
|
||||
__slots__ = "__name", "__default_value"
|
||||
__slots__ = "__name", "__default_value", "__default_factory"
|
||||
|
||||
__name: str
|
||||
|
||||
def __init__(self, default_value: T | None = None) -> None:
|
||||
self.__default_value: T | None = default_value
|
||||
@overload
|
||||
def __init__(self) -> None: ...
|
||||
@overload
|
||||
def __init__(self, default_value: T) -> None: ...
|
||||
@overload
|
||||
def __init__(self, *, default_factory: Callable[[Self], T]) -> None: ...
|
||||
def __init__(
|
||||
self,
|
||||
default_value: T | Unset = Unset(),
|
||||
default_factory: Callable[[Self], T] | Unset = Unset(),
|
||||
) -> None:
|
||||
self.__default_value: Final[T | Unset] = default_value
|
||||
self.__default_factory: Final[Callable[[Self], T] | Unset] = default_factory
|
||||
|
||||
def _get_default(self: Self) -> T:
|
||||
return Unset.map(
|
||||
self.__default_factory,
|
||||
lambda f: f(self),
|
||||
lambda _: Unset.unwrap(self.__default_value),
|
||||
)
|
||||
|
||||
def __set_name__(self, owner: type, name: str) -> None:
|
||||
self.__name = f"_{owner.__name__}__{name}"
|
||||
|
||||
def __get__(self, instance: Any, owner: type | None = None, /) -> T:
|
||||
if self.__default_value:
|
||||
return getattr(instance, self.__name, self.__default_value)
|
||||
if not hasattr(instance, self.__name):
|
||||
setattr(instance, self.__name, self._get_default())
|
||||
return getattr(instance, self.__name) # type: ignore
|
||||
|
||||
def __set__(self, instance: Any, value: T, /) -> None:
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ MARKERS_ATTR = "__snakia_markers__"
|
|||
|
||||
|
||||
def _get_all_markers(obj: Any) -> dict[type["Marker"], "Marker"]:
|
||||
return get_or_set_attr(obj, MARKERS_ATTR, dict[type[Marker], Marker]())
|
||||
return get_or_set_attr(obj, MARKERS_ATTR, dict())
|
||||
|
||||
|
||||
class Marker:
|
||||
|
|
|
|||
|
|
@ -49,24 +49,29 @@ class UniqueType(type):
|
|||
raise TypeError(f"{cls} not unwrapped")
|
||||
return value # type: ignore
|
||||
|
||||
def unwrap_or(cls: type[T], value: V | type[T] | T, default: R, /) -> V | R:
|
||||
if value is cls or isinstance(value, cls):
|
||||
return default
|
||||
return value # type: ignore
|
||||
|
||||
def map(
|
||||
cls: type[T],
|
||||
value: V | type[T] | T,
|
||||
and_then: Callable[[V], R],
|
||||
or_else: Callable[[type[T]], R],
|
||||
or_else: Callable[[V], R],
|
||||
and_then: Callable[[type[T]], R],
|
||||
) -> R:
|
||||
if value is cls or isinstance(value, cls):
|
||||
return or_else(cls)
|
||||
return and_then(value) # type: ignore
|
||||
return and_then(cls)
|
||||
return or_else(value) # type: ignore
|
||||
|
||||
def and_then(
|
||||
def or_else(
|
||||
cls: type[T], value: V | type[T] | T, func: Callable[[V], R]
|
||||
) -> type[T] | R:
|
||||
if value is cls or isinstance(value, cls):
|
||||
return cls
|
||||
return func(value) # type: ignore
|
||||
|
||||
def or_else(
|
||||
def and_then(
|
||||
cls: type[T], value: V | type[T] | T, func: Callable[[type[T]], R]
|
||||
) -> R | V:
|
||||
if value is cls or isinstance(value, cls):
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
from .attrs import get_attrs, get_or_set_attr
|
||||
from .calls import call, caller
|
||||
from .exceptions import catch, throw
|
||||
from .frames import frame
|
||||
from .funcs import call, caller, ret, side, side_func
|
||||
from .gil import GIL_ENABLED, nolock
|
||||
from .inherit import inherit
|
||||
from .side import side, side_func
|
||||
from .this import this
|
||||
from .to_async import to_async
|
||||
|
||||
|
|
@ -17,6 +16,7 @@ __all__ = [
|
|||
"frame",
|
||||
"inherit",
|
||||
"nolock",
|
||||
"ret",
|
||||
"side",
|
||||
"side_func",
|
||||
"this",
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
from typing import Callable, ParamSpec, TypeVar
|
||||
|
||||
P = ParamSpec("P")
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def call(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
|
||||
def caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> Callable[..., T]:
|
||||
return lambda *_, **__: f(*args, **kwargs)
|
||||
24
src/snakia/utils/funcs.py
Normal file
24
src/snakia/utils/funcs.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from typing import Any, Callable, ParamSpec, TypeVar
|
||||
|
||||
P = ParamSpec("P")
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def call(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
|
||||
def caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> Callable[..., T]:
|
||||
return lambda *_, **__: f(*args, **kwargs)
|
||||
|
||||
|
||||
def side(value: T, *_: Any, **__: Any) -> T:
|
||||
return value
|
||||
|
||||
|
||||
def side_func(value: T, *_: Any, **__: Any) -> Callable[..., T]:
|
||||
return lambda *_, **__: value
|
||||
|
||||
|
||||
def ret() -> Callable[[T], T]:
|
||||
return lambda x: x
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
from typing import Any, Callable, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def side(value: T, *_: Any, **__: Any) -> T:
|
||||
return value
|
||||
|
||||
|
||||
def side_func(value: T, *_: Any, **__: Any) -> Callable[..., T]:
|
||||
return lambda *_, **__: value
|
||||
Loading…
Add table
Add a link
Reference in a new issue