Release/v0.4.2 #2
52 changed files with 340 additions and 300 deletions
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
from collections import defaultdict
|
||||
from collections.abc import Iterable
|
||||
from itertools import count
|
||||
from typing import Any, cast, overload
|
||||
from typing import Any, TypeVar, cast, overload
|
||||
|
||||
import networkx as nx # type: ignore
|
||||
|
||||
|
|
@ -12,6 +12,14 @@ from snakia.utils import nolock
|
|||
from .component import Component
|
||||
from .processor import Processor
|
||||
|
||||
A = TypeVar("A", bound=Component)
|
||||
B = TypeVar("B", bound=Component)
|
||||
C = TypeVar("C", bound=Component)
|
||||
D = TypeVar("D", bound=Component)
|
||||
E = TypeVar("E", bound=Component)
|
||||
|
||||
P = TypeVar("P", bound=Processor)
|
||||
|
||||
|
||||
class System:
|
||||
"""
|
||||
|
|
@ -46,9 +54,7 @@ class System:
|
|||
self.__entity_counter = count(start=1)
|
||||
self.__dead_entities = set()
|
||||
|
||||
def get_processor[P: Processor](
|
||||
self, processor_type: type[P], /
|
||||
) -> P | None:
|
||||
def get_processor(self, processor_type: type[P], /) -> P | None:
|
||||
"""Returns the first processor of the given type."""
|
||||
for processor in self.__processors:
|
||||
if isinstance(processor, processor_type):
|
||||
|
|
@ -67,39 +73,31 @@ class System:
|
|||
self.__processors.remove(processor)
|
||||
|
||||
@overload
|
||||
def get_components[A: Component](
|
||||
self, __c1: type[A], /
|
||||
) -> Iterable[tuple[int, tuple[A]]]: ...
|
||||
def get_components(self, c1: type[A], /) -> Iterable[tuple[int, tuple[A]]]: ...
|
||||
|
||||
@overload
|
||||
def get_components[A: Component, B: Component](
|
||||
self, __c1: type[A], __c2: type[B], /
|
||||
def get_components(
|
||||
self, c1: type[A], c2: type[B], /
|
||||
) -> Iterable[tuple[int, tuple[A, B]]]: ...
|
||||
|
||||
@overload
|
||||
def get_components[A: Component, B: Component, C: Component](
|
||||
self, __c1: type[A], __c2: type[B], __c3: type[C], /
|
||||
def get_components(
|
||||
self, c1: type[A], c2: type[B], c3: type[C], /
|
||||
) -> Iterable[tuple[int, tuple[A, B, C]]]: ...
|
||||
|
||||
@overload
|
||||
def get_components[A: Component, B: Component, C: Component, D: Component](
|
||||
self, __c1: type[A], __c2: type[B], __c3: type[C], __c4: type[D], /
|
||||
def get_components(
|
||||
self, c1: type[A], c2: type[B], c3: type[C], c4: type[D], /
|
||||
) -> Iterable[tuple[int, tuple[A, B, C, D]]]: ...
|
||||
|
||||
@overload
|
||||
def get_components[
|
||||
A: Component,
|
||||
B: Component,
|
||||
C: Component,
|
||||
D: Component,
|
||||
E: Component,
|
||||
](
|
||||
def get_components(
|
||||
self,
|
||||
__c1: type[A],
|
||||
__c2: type[B],
|
||||
__c3: type[C],
|
||||
__c4: type[D],
|
||||
__c5: type[E],
|
||||
c1: type[A],
|
||||
c2: type[B],
|
||||
c3: type[C],
|
||||
c4: type[D],
|
||||
c5: type[E],
|
||||
/,
|
||||
) -> Iterable[tuple[int, tuple[A, B, C, D]]]: ...
|
||||
|
||||
|
|
@ -108,10 +106,7 @@ class System:
|
|||
) -> Iterable[tuple[int, tuple[Component, ...]]]:
|
||||
"""Returns all entities with the given components."""
|
||||
entity_set = set.intersection(
|
||||
*(
|
||||
self.__components[component_type]
|
||||
for component_type in component_types
|
||||
)
|
||||
*(self.__components[component_type] for component_type in component_types)
|
||||
)
|
||||
for entity in entity_set:
|
||||
yield (
|
||||
|
|
@ -123,51 +118,40 @@ class System:
|
|||
)
|
||||
|
||||
@overload
|
||||
def get_components_of_entity[A: Component](
|
||||
self, entity: int, __c1: type[A], /
|
||||
def get_components_of_entity(
|
||||
self, entity: int, c1: type[A], /
|
||||
) -> tuple[A | None]: ...
|
||||
|
||||
@overload
|
||||
def get_components_of_entity[A: Component, B: Component](
|
||||
self, entity: int, __c1: type[A], __c2: type[B], /
|
||||
def get_components_of_entity(
|
||||
self, entity: int, c1: type[A], c2: type[B], /
|
||||
) -> tuple[A | None, B | None]: ...
|
||||
|
||||
@overload
|
||||
def get_components_of_entity[A: Component, B: Component, C: Component](
|
||||
self, entity: int, __c1: type[A], __c2: type[B], __c3: type[C], /
|
||||
def get_components_of_entity(
|
||||
self, entity: int, c1: type[A], c2: type[B], c3: type[C], /
|
||||
) -> tuple[A | None, B | None, C | None]: ...
|
||||
|
||||
@overload
|
||||
def get_components_of_entity[
|
||||
A: Component,
|
||||
B: Component,
|
||||
C: Component,
|
||||
D: Component,
|
||||
](
|
||||
def get_components_of_entity(
|
||||
self,
|
||||
entity: int,
|
||||
__c1: type[A],
|
||||
__c2: type[B],
|
||||
__c3: type[C],
|
||||
__c4: type[D],
|
||||
c1: type[A],
|
||||
c2: type[B],
|
||||
c3: type[C],
|
||||
c4: type[D],
|
||||
/,
|
||||
) -> tuple[A | None, B | None, C | None, D | None]: ...
|
||||
|
||||
@overload
|
||||
def get_components_of_entity[
|
||||
A: Component,
|
||||
B: Component,
|
||||
C: Component,
|
||||
D: Component,
|
||||
E: Component,
|
||||
](
|
||||
def get_components_of_entity(
|
||||
self,
|
||||
entity: int,
|
||||
__c1: type[A],
|
||||
__c2: type[B],
|
||||
__c3: type[C],
|
||||
__c4: type[D],
|
||||
__c5: type[E],
|
||||
c1: type[A],
|
||||
c2: type[B],
|
||||
c3: type[C],
|
||||
c4: type[D],
|
||||
c5: type[E],
|
||||
/,
|
||||
) -> tuple[A | None, B | None, C | None, D | None, E | None]: ...
|
||||
|
||||
|
|
@ -183,20 +167,18 @@ class System:
|
|||
),
|
||||
)
|
||||
|
||||
def get_component[C: Component](
|
||||
self, component_type: type[C], /
|
||||
) -> Iterable[tuple[int, C]]:
|
||||
def get_component(self, component_type: type[C], /) -> Iterable[tuple[int, C]]:
|
||||
"""Returns all entities with the given component."""
|
||||
for entity in self.__components[component_type].copy():
|
||||
yield entity, cast(C, self.__entitites[entity][component_type])
|
||||
|
||||
@overload
|
||||
def get_component_of_entity[C: Component](
|
||||
def get_component_of_entity(
|
||||
self, entity: int, component_type: type[C], /
|
||||
) -> C | None: ...
|
||||
|
||||
@overload
|
||||
def get_component_of_entity[C: Component, D: Any](
|
||||
def get_component_of_entity(
|
||||
self, entity: int, component_type: type[C], /, default: D
|
||||
) -> C | D: ...
|
||||
|
||||
|
|
@ -216,24 +198,16 @@ class System:
|
|||
self.__components[component_type].add(entity)
|
||||
self.__entitites[entity][component_type] = component
|
||||
|
||||
def has_component(
|
||||
self, entity: int, component_type: type[Component]
|
||||
) -> bool:
|
||||
def has_component(self, entity: int, component_type: type[Component]) -> bool:
|
||||
"""Returns True if the entity has the given component."""
|
||||
return component_type in self.__entitites[entity]
|
||||
|
||||
def has_components(
|
||||
self, entity: int, *component_types: type[Component]
|
||||
) -> bool:
|
||||
def has_components(self, entity: int, *component_types: type[Component]) -> bool:
|
||||
"""Returns True if the entity has all the given components."""
|
||||
components_dict = self.__entitites[entity]
|
||||
return all(
|
||||
comp_type in components_dict for comp_type in component_types
|
||||
)
|
||||
return all(comp_type in components_dict for comp_type in component_types)
|
||||
|
||||
def remove_component[C: Component](
|
||||
self, entity: int, component_type: type[C]
|
||||
) -> C | None:
|
||||
def remove_component(self, entity: int, component_type: type[C]) -> C | None:
|
||||
"""Removes a component from an entity."""
|
||||
self.__components[component_type].discard(entity)
|
||||
if not self.__components[component_type]:
|
||||
|
|
@ -265,9 +239,7 @@ class System:
|
|||
|
||||
def entity_exists(self, entity: int) -> bool:
|
||||
"""Returns True if the entity exists."""
|
||||
return (
|
||||
entity in self.__entitites and entity not in self.__dead_entities
|
||||
)
|
||||
return entity in self.__entitites and entity not in self.__dead_entities
|
||||
|
||||
def start(self) -> None:
|
||||
"""Starts the system."""
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ class Engine:
|
|||
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,7 +1,5 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Self
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
|
|
@ -9,27 +7,27 @@ class Action(BaseModel):
|
|||
move: int = Field(default=1)
|
||||
|
||||
@classmethod
|
||||
def stop(cls) -> Self:
|
||||
def stop(cls) -> Action:
|
||||
"""Skip all handlers."""
|
||||
return cls(move=2**8)
|
||||
|
||||
@classmethod
|
||||
def go_start(cls) -> Self:
|
||||
def go_start(cls) -> Action:
|
||||
"""Go to the first handler."""
|
||||
return cls(move=-(2**8))
|
||||
|
||||
@classmethod
|
||||
def next(cls, count: int = 1) -> Self:
|
||||
def next(cls, count: int = 1) -> Action:
|
||||
"""Skip one handler."""
|
||||
return cls(move=count)
|
||||
|
||||
@classmethod
|
||||
def prev(cls, count: int = 1) -> Self:
|
||||
def prev(cls, count: int = 1) -> Action:
|
||||
"""Go back one handler."""
|
||||
return cls(move=-count)
|
||||
|
||||
@classmethod
|
||||
def skip(cls, count: int = 1) -> Self:
|
||||
def skip(cls, count: int = 1) -> Action:
|
||||
"""Skip n handlers.
|
||||
|
||||
Args:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||
|
||||
import queue
|
||||
from collections import defaultdict
|
||||
from typing import Callable, Final
|
||||
from typing import Callable, Final, TypeVar
|
||||
|
||||
from snakia.utils import nolock
|
||||
|
||||
|
|
@ -11,6 +11,8 @@ from .filter import Filter
|
|||
from .handler import Handler
|
||||
from .subscriber import Subscriber
|
||||
|
||||
T = TypeVar("T", bound=Event)
|
||||
|
||||
|
||||
class Dispatcher:
|
||||
"""
|
||||
|
|
@ -21,9 +23,9 @@ class Dispatcher:
|
|||
|
||||
def __init__(self) -> None:
|
||||
self.__queue: Final = queue.Queue[Event]()
|
||||
self.__subscribers: Final[
|
||||
dict[type[Event], list[Subscriber[Event]]]
|
||||
] = defaultdict(list)
|
||||
self.__subscribers: Final[dict[type[Event], list[Subscriber[Event]]]] = (
|
||||
defaultdict(list)
|
||||
)
|
||||
self.__running = False
|
||||
|
||||
@property
|
||||
|
|
@ -31,15 +33,11 @@ class Dispatcher:
|
|||
"""Returns True if the dispatcher is running."""
|
||||
return self.__running
|
||||
|
||||
def subscribe[T: Event](
|
||||
self, event_type: type[T], subscriber: Subscriber[T]
|
||||
) -> None:
|
||||
def subscribe(self, event_type: type[T], subscriber: Subscriber[T]) -> None:
|
||||
"""Subscribe to an event type."""
|
||||
self.__subscribers[event_type].append(subscriber) # type: ignore
|
||||
|
||||
def unsubscribe[T: Event](
|
||||
self, event_type: type[T], subscriber: Subscriber[T]
|
||||
) -> None:
|
||||
def unsubscribe(self, event_type: type[T], subscriber: Subscriber[T]) -> None:
|
||||
"""Unsubscribe from an event type."""
|
||||
for sub in self.__subscribers[event_type].copy():
|
||||
if sub.handler != subscriber.handler:
|
||||
|
|
@ -48,7 +46,7 @@ class Dispatcher:
|
|||
continue
|
||||
self.__subscribers[event_type].remove(sub)
|
||||
|
||||
def on[T: Event](
|
||||
def on(
|
||||
self,
|
||||
event: type[T],
|
||||
filter: Filter[T] | None = None, # noqa: W0622 # pylint: disable=W0622
|
||||
|
|
@ -95,9 +93,7 @@ class Dispatcher:
|
|||
i = 0
|
||||
while i < len(subscribers):
|
||||
subscriber = subscribers[i]
|
||||
if subscriber.filters is not None and not subscriber.filters(
|
||||
event
|
||||
):
|
||||
if subscriber.filters is not None and not subscriber.filters(event):
|
||||
continue
|
||||
|
||||
action = subscriber.handler(event)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
from typing import Generic, Protocol, TypeVar
|
||||
|
||||
from .event import Event
|
||||
|
||||
T_contra = TypeVar("T_contra", bound=Event, contravariant=True)
|
||||
|
||||
class Filter[T: Event](Protocol):
|
||||
|
||||
class Filter(Protocol, Generic[T_contra]):
|
||||
"""Filter for an event."""
|
||||
|
||||
def __call__(self, event: T) -> bool: ...
|
||||
def __call__(self, event: T_contra) -> bool: ...
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Protocol
|
||||
from typing import Generic, Optional, Protocol, TypeVar
|
||||
|
||||
from .action import Action
|
||||
from .event import Event
|
||||
|
||||
T_contra = TypeVar("T_contra", bound=Event, contravariant=True)
|
||||
|
||||
class Handler[T: Event](Protocol):
|
||||
|
||||
class Handler(Protocol, Generic[T_contra]):
|
||||
"""Handler for an event."""
|
||||
|
||||
def __call__(self, event: T) -> Optional[Action]: ...
|
||||
def __call__(self, event: T_contra) -> Optional[Action]: ...
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import NamedTuple
|
||||
from typing import Generic, NamedTuple, TypeVar
|
||||
|
||||
from .event import Event
|
||||
from .filter import Filter
|
||||
from .handler import Handler
|
||||
|
||||
T_contra = TypeVar("T_contra", bound=Event, contravariant=True)
|
||||
|
||||
class Subscriber[T: Event](NamedTuple):
|
||||
|
||||
class Subscriber(NamedTuple, Generic[T_contra]):
|
||||
"""
|
||||
Subscriber for an event."""
|
||||
|
||||
handler: Handler[T]
|
||||
filters: Filter[T] | None
|
||||
handler: Handler[T_contra]
|
||||
filters: Filter[T_contra] | None
|
||||
priority: int
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
import sys
|
||||
from types import TracebackType
|
||||
from typing import Any, Callable, Protocol, final
|
||||
from typing import Any, Callable, Generic, Protocol, TypeVar, final
|
||||
|
||||
T = TypeVar("T", bound=BaseException)
|
||||
T_contra = TypeVar("T_contra", contravariant=True)
|
||||
|
||||
|
||||
class ExceptionHook[T: BaseException](Protocol):
|
||||
class ExceptionHook(Protocol, Generic[T_contra]):
|
||||
|
||||
def __call__(
|
||||
self, exception: T, frame: TracebackType | None, /
|
||||
self, exception: T_contra, frame: TracebackType | None, /
|
||||
) -> bool | None: ...
|
||||
|
||||
|
||||
|
|
@ -15,13 +19,13 @@ class _ExceptionManager:
|
|||
self.__hooks: list[tuple[type[BaseException], ExceptionHook[Any]]] = []
|
||||
sys.excepthook = self._excepthook
|
||||
|
||||
def hook_exception[T: BaseException](
|
||||
def hook_exception(
|
||||
self, exception_type: type[T], func: ExceptionHook[T]
|
||||
) -> ExceptionHook[T]:
|
||||
self.__hooks.append((exception_type, func))
|
||||
return func
|
||||
|
||||
def on_exception[T: BaseException](
|
||||
def on_exception(
|
||||
self, exception_type: type[T]
|
||||
) -> Callable[[ExceptionHook[T]], ExceptionHook[T]]:
|
||||
def inner(func: ExceptionHook[T]) -> ExceptionHook[T]:
|
||||
|
|
|
|||
|
|
@ -23,16 +23,12 @@ class Meta(BaseModel):
|
|||
max_length=32,
|
||||
pattern="^[a-z0-9_]{4,32}$",
|
||||
)
|
||||
version: Version = Field(
|
||||
default_factory=lambda: Version(major=1, minor=0, patch=0)
|
||||
)
|
||||
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
|
||||
)
|
||||
processors: tuple[type[PluginProcessor], ...] = Field(default_factory=tuple)
|
||||
|
||||
@property
|
||||
def id(self) -> str:
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from typing import Any, Awaitable, Callable, Literal, overload
|
||||
from typing import Any, Awaitable, Callable, Generic, Literal, TypeVar, overload
|
||||
|
||||
from .base_bindable import BaseBindable, BindableSubscriber, ValueChanged
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class AsyncBindable[T: Any](BaseBindable[T]):
|
||||
|
||||
class AsyncBindable(BaseBindable[T], Generic[T]):
|
||||
"""
|
||||
An asynchronous bindable.
|
||||
"""
|
||||
|
|
@ -53,25 +55,19 @@ class AsyncBindable[T: Any](BaseBindable[T]):
|
|||
if run_now:
|
||||
|
||||
async def _run() -> None:
|
||||
await subscriber(
|
||||
ValueChanged(self.__default_value, self.value)
|
||||
)
|
||||
await subscriber(ValueChanged(self.__default_value, self.value))
|
||||
|
||||
return _run()
|
||||
return None
|
||||
|
||||
def unsubscribe(
|
||||
self, subscriber: BindableSubscriber[T, Awaitable[Any]]
|
||||
) -> None:
|
||||
def unsubscribe(self, subscriber: BindableSubscriber[T, Awaitable[Any]]) -> None:
|
||||
"""Unsubscribe from an value."""
|
||||
self.__subscribers.remove(subscriber)
|
||||
|
||||
@overload
|
||||
def on(
|
||||
self, run_now: Literal[True]
|
||||
) -> Callable[
|
||||
[BindableSubscriber[T, Awaitable[Any]]], Awaitable[None]
|
||||
]: ...
|
||||
) -> Callable[[BindableSubscriber[T, Awaitable[Any]]], Awaitable[None]]: ...
|
||||
|
||||
@overload
|
||||
def on(
|
||||
|
|
@ -91,9 +87,7 @@ class AsyncBindable[T: Any](BaseBindable[T]):
|
|||
if run_now:
|
||||
|
||||
async def _run() -> None:
|
||||
await subscriber(
|
||||
ValueChanged(self.__default_value, self.value)
|
||||
)
|
||||
await subscriber(ValueChanged(self.__default_value, self.value))
|
||||
|
||||
return _run()
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
from typing import Any, NamedTuple, Protocol
|
||||
from typing import Generic, NamedTuple, Protocol, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
R_co = TypeVar("R_co", covariant=True)
|
||||
|
||||
|
||||
class ValueChanged[T](NamedTuple):
|
||||
class ValueChanged(NamedTuple, Generic[T]):
|
||||
old_value: T
|
||||
new_value: T
|
||||
|
||||
|
||||
class BindableSubscriber[T: Any, R: Any](Protocol):
|
||||
def __call__(self, value: ValueChanged[T], /) -> R: ...
|
||||
class BindableSubscriber(Protocol, Generic[T, R_co]):
|
||||
def __call__(self, value: ValueChanged[T], /) -> R_co: ...
|
||||
|
||||
|
||||
class BaseBindable[T: Any]:
|
||||
class BaseBindable(Generic[T]):
|
||||
def __init__(self, default_value: T | None = None) -> None:
|
||||
if default_value is not None:
|
||||
self.__default_value: T = default_value
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from typing import Any, Callable
|
||||
from typing import Any, Callable, Generic, TypeVar
|
||||
|
||||
from .base_bindable import BaseBindable, BindableSubscriber, ValueChanged
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class Bindable[T: Any](BaseBindable[T]):
|
||||
|
||||
class Bindable(BaseBindable[T], Generic[T]):
|
||||
"""
|
||||
A bindable value.
|
||||
"""
|
||||
|
|
@ -36,9 +38,7 @@ class Bindable[T: Any](BaseBindable[T]):
|
|||
"""Unsubscribe from an value."""
|
||||
self.__subscribers.remove(subscriber)
|
||||
|
||||
def on(
|
||||
self, run_now: bool = False
|
||||
) -> Callable[[BindableSubscriber[T, Any]], None]:
|
||||
def on(self, run_now: bool = False) -> Callable[[BindableSubscriber[T, Any]], None]:
|
||||
"""Decorator to subscribe to an value."""
|
||||
|
||||
def wrapper(subscriber: BindableSubscriber[T, Any]) -> None:
|
||||
|
|
|
|||
|
|
@ -1,20 +1,26 @@
|
|||
from typing import Any, Callable, overload
|
||||
from typing import Any, Callable, ParamSpec, TypeVar, overload
|
||||
|
||||
P = ParamSpec("P")
|
||||
|
||||
A = TypeVar("A")
|
||||
B = TypeVar("B")
|
||||
C = TypeVar("C")
|
||||
D = TypeVar("D")
|
||||
E = TypeVar("E")
|
||||
|
||||
|
||||
@overload
|
||||
def chain[**P, A](func1: Callable[P, A], /) -> Callable[P, A]: ...
|
||||
def chain(func1: Callable[P, A], /) -> Callable[P, A]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def chain[**P, A, B](
|
||||
func1: Callable[P, A], func2: Callable[[A], B], /
|
||||
) -> Callable[P, B]: ...
|
||||
def chain(func1: Callable[P, A], func2: Callable[[A], B], /) -> Callable[P, B]: ...
|
||||
@overload
|
||||
def chain[**P, A, B, C](
|
||||
def chain(
|
||||
func1: Callable[P, A], func2: Callable[[A], B], func3: Callable[[B], C], /
|
||||
) -> Callable[P, C]: ...
|
||||
@overload
|
||||
def chain[**P, A, B, C, D](
|
||||
def chain(
|
||||
func1: Callable[P, A],
|
||||
func2: Callable[[A], B],
|
||||
func3: Callable[[B], C],
|
||||
|
|
@ -24,7 +30,7 @@ def chain[**P, A, B, C, D](
|
|||
|
||||
|
||||
@overload
|
||||
def chain[**P, A, B, C, D, E](
|
||||
def chain(
|
||||
func1: Callable[P, A],
|
||||
func2: Callable[[A], B],
|
||||
func3: Callable[[B], C],
|
||||
|
|
@ -35,14 +41,12 @@ def chain[**P, A, B, C, D, E](
|
|||
|
||||
|
||||
@overload
|
||||
def chain[**P](
|
||||
def chain(
|
||||
func1: Callable[P, Any], /, *funcs: Callable[[Any], Any]
|
||||
) -> Callable[P, Any]: ...
|
||||
|
||||
|
||||
def chain[**P](
|
||||
func1: Callable[P, Any], /, *funcs: Callable[[Any], Any]
|
||||
) -> Callable[P, Any]:
|
||||
def chain(func1: Callable[P, Any], /, *funcs: Callable[[Any], Any]) -> Callable[P, Any]:
|
||||
|
||||
def inner(*args: P.args, **kwargs: P.kwargs) -> Any:
|
||||
v = func1(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import operator
|
||||
from typing import Any, Callable, overload
|
||||
from typing import Any, Callable, TypeVar, overload
|
||||
|
||||
from snakia.utils import to_async
|
||||
|
||||
|
|
@ -8,9 +8,16 @@ from .base_bindable import ValueChanged
|
|||
from .bindable import Bindable
|
||||
from .concat import concat
|
||||
|
||||
A = TypeVar("A")
|
||||
B = TypeVar("B")
|
||||
C = TypeVar("C")
|
||||
D = TypeVar("D")
|
||||
E = TypeVar("E")
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
@overload
|
||||
def combine[A, R](
|
||||
def combine(
|
||||
source1: Bindable[A] | AsyncBindable[A],
|
||||
/,
|
||||
*,
|
||||
|
|
@ -19,7 +26,7 @@ def combine[A, R](
|
|||
|
||||
|
||||
@overload
|
||||
def combine[A, B, R](
|
||||
def combine(
|
||||
source1: Bindable[A] | AsyncBindable[A],
|
||||
source2: Bindable[B] | AsyncBindable[B],
|
||||
/,
|
||||
|
|
@ -29,7 +36,7 @@ def combine[A, B, R](
|
|||
|
||||
|
||||
@overload
|
||||
def combine[A, B, C, R](
|
||||
def combine(
|
||||
source1: Bindable[A] | AsyncBindable[A],
|
||||
source2: Bindable[B] | AsyncBindable[B],
|
||||
source3: Bindable[C] | AsyncBindable[C],
|
||||
|
|
@ -40,7 +47,7 @@ def combine[A, B, C, R](
|
|||
|
||||
|
||||
@overload
|
||||
def combine[A, B, C, D, R](
|
||||
def combine(
|
||||
source1: Bindable[A] | AsyncBindable[A],
|
||||
source2: Bindable[B] | AsyncBindable[B],
|
||||
source3: Bindable[C] | AsyncBindable[C],
|
||||
|
|
@ -52,7 +59,7 @@ def combine[A, B, C, D, R](
|
|||
|
||||
|
||||
@overload
|
||||
def combine[A, B, C, D, R](
|
||||
def combine(
|
||||
source1: Bindable[A] | AsyncBindable[A],
|
||||
source2: Bindable[B] | AsyncBindable[B],
|
||||
source3: Bindable[C] | AsyncBindable[C],
|
||||
|
|
@ -64,13 +71,13 @@ def combine[A, B, C, D, R](
|
|||
|
||||
|
||||
@overload
|
||||
def combine[R](
|
||||
def combine(
|
||||
*sources: Bindable[Any] | AsyncBindable[Any],
|
||||
combiner: Callable[..., R],
|
||||
) -> Bindable[R]: ...
|
||||
|
||||
|
||||
def combine[R](
|
||||
def combine(
|
||||
*sources: Bindable[Any] | AsyncBindable[Any],
|
||||
combiner: Callable[..., R],
|
||||
) -> Bindable[R]:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
from typing import Any, Callable
|
||||
from typing import Any, Callable, ParamSpec
|
||||
|
||||
P = ParamSpec("P")
|
||||
|
||||
|
||||
def concat[**P](*funcs: Callable[P, Any]) -> Callable[P, None]:
|
||||
def concat(*funcs: Callable[P, Any]) -> Callable[P, None]:
|
||||
def inner(*args: P.args, **kwargs: P.kwargs) -> None:
|
||||
for f in funcs:
|
||||
f(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
from typing import Callable
|
||||
from typing import Callable, ParamSpec, TypeVar
|
||||
|
||||
P = ParamSpec("P")
|
||||
T = TypeVar("T")
|
||||
F = TypeVar("F")
|
||||
|
||||
|
||||
def cond[**P, T, F](
|
||||
def cond(
|
||||
condition: Callable[P, bool],
|
||||
if_true: Callable[P, T],
|
||||
if_false: Callable[P, F],
|
||||
) -> Callable[P, T | F]:
|
||||
return lambda *args, **kw: (
|
||||
if_true(*args, **kw)
|
||||
if condition(*args, **kw)
|
||||
else if_false(*args, **kw)
|
||||
if_true(*args, **kw) if condition(*args, **kw) else if_false(*args, **kw)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from typing import Callable
|
||||
from typing import Callable, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def const[T](value: T) -> Callable[[], T]:
|
||||
def const(value: T) -> Callable[[], T]:
|
||||
return lambda: value
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import builtins
|
||||
from typing import Callable, Iterable, TypeGuard
|
||||
from typing import Callable, Iterable, TypeGuard, TypeVar
|
||||
|
||||
S = TypeVar("S")
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
# noqa: W0622 # pylint: disable=W0622
|
||||
def filter[S, T](
|
||||
def filter(
|
||||
f: Callable[[S], TypeGuard[T]],
|
||||
) -> Callable[[Iterable[S]], Iterable[T]]:
|
||||
return lambda iterable: builtins.filter(f, iterable)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import builtins
|
||||
from typing import Any, Callable, Iterable
|
||||
from typing import Callable, Iterable, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
U = TypeVar("U")
|
||||
|
||||
|
||||
# noqa: W0622 # pylint: disable=W0622
|
||||
def map[T: Any, U](
|
||||
func: Callable[[T], U], /
|
||||
) -> Callable[[Iterable[T]], Iterable[U]]:
|
||||
def map(func: Callable[[T], U], /) -> Callable[[Iterable[T]], Iterable[U]]:
|
||||
return lambda iterable: builtins.map(func, iterable)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
from typing import TypeVar
|
||||
|
||||
from .async_bindable import AsyncBindable
|
||||
from .bindable import Bindable
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
def merge[T](
|
||||
|
||||
def merge(
|
||||
*sources: Bindable[T],
|
||||
) -> Bindable[T]:
|
||||
merged = Bindable[T]()
|
||||
|
|
@ -11,7 +15,7 @@ def merge[T](
|
|||
return merged
|
||||
|
||||
|
||||
async def async_merge[T](
|
||||
async def async_merge(
|
||||
*sources: AsyncBindable[T],
|
||||
) -> AsyncBindable[T]:
|
||||
merged = AsyncBindable[T]()
|
||||
|
|
|
|||
|
|
@ -40,9 +40,7 @@ class Canvas:
|
|||
|
||||
def get_column(self, x: int, /) -> Iterable[CanvasChar]:
|
||||
"""Get the column at the given position."""
|
||||
return (
|
||||
self.__buffer[self._get_index(x, y)] for y in range(self.height)
|
||||
)
|
||||
return (self.__buffer[self._get_index(x, y)] for y in range(self.height))
|
||||
|
||||
def set(self, x: int, y: int, value: CanvasChar, /) -> None:
|
||||
"""Set the character at the given position."""
|
||||
|
|
@ -68,9 +66,7 @@ class Canvas:
|
|||
value: CanvasChar,
|
||||
) -> None:
|
||||
"""Set the area at the given position."""
|
||||
for i in range(
|
||||
self._get_index(x, y), self._get_index(x + width, y + height)
|
||||
):
|
||||
for i in range(self._get_index(x, y), self._get_index(x + width, y + height)):
|
||||
self.__buffer[i] = value
|
||||
|
||||
def clear(self) -> None:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Final, final
|
||||
from typing import Final, TypeVar, final
|
||||
|
||||
from snakia.core.rx import AsyncBindable, Bindable
|
||||
from snakia.utils import to_async
|
||||
|
||||
from .canvas import Canvas
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class Widget(ABC):
|
||||
def __init__(self) -> None:
|
||||
|
|
@ -24,13 +26,13 @@ class Widget(ABC):
|
|||
return self.__cache
|
||||
|
||||
@final
|
||||
def state[T](self, default_value: T) -> Bindable[T]:
|
||||
def state(self, default_value: T) -> Bindable[T]:
|
||||
field = Bindable(default_value)
|
||||
field.subscribe(lambda _: self.dirty.set(True))
|
||||
return field
|
||||
|
||||
@final
|
||||
def async_state[T](self, default_value: T) -> AsyncBindable[T]:
|
||||
def async_state(self, default_value: T) -> AsyncBindable[T]:
|
||||
field = AsyncBindable(default_value)
|
||||
field.subscribe(to_async(lambda _: self.dirty.set(True)))
|
||||
return field
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ from .container import ContainerWidget
|
|||
|
||||
|
||||
class HorizontalSplitWidget(ContainerWidget):
|
||||
def __init__(
|
||||
self, children: Iterable[Widget], splitter_char: str = "|"
|
||||
) -> None:
|
||||
def __init__(self, children: Iterable[Widget], splitter_char: str = "|") -> None:
|
||||
super().__init__(children)
|
||||
self.splitter_char = splitter_char
|
||||
|
||||
|
|
@ -21,9 +19,7 @@ class HorizontalSplitWidget(ContainerWidget):
|
|||
|
||||
child_canvases = [child.render() for child in children_list]
|
||||
total_width = (
|
||||
sum(canvas.width for canvas in child_canvases)
|
||||
+ len(child_canvases)
|
||||
- 1
|
||||
sum(canvas.width for canvas in child_canvases) + len(child_canvases) - 1
|
||||
)
|
||||
max_height = max(canvas.height for canvas in child_canvases)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ from .container import ContainerWidget
|
|||
|
||||
|
||||
class VerticalSplitWidget(ContainerWidget):
|
||||
def __init__(
|
||||
self, children: Iterable[Widget], splitter_char: str = "-"
|
||||
) -> None:
|
||||
def __init__(self, children: Iterable[Widget], splitter_char: str = "-") -> None:
|
||||
super().__init__(children)
|
||||
self.splitter_char = splitter_char
|
||||
|
||||
|
|
@ -22,9 +20,7 @@ class VerticalSplitWidget(ContainerWidget):
|
|||
child_canvases = [child.render() for child in children_list]
|
||||
max_width = max(canvas.width for canvas in child_canvases)
|
||||
total_height = (
|
||||
sum(canvas.height for canvas in child_canvases)
|
||||
+ len(child_canvases)
|
||||
- 1
|
||||
sum(canvas.height for canvas in child_canvases) + len(child_canvases) - 1
|
||||
)
|
||||
|
||||
result = Canvas(max_width, total_height, CanvasChar())
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
from typing import Callable
|
||||
from typing import Callable, ParamSpec, TypeVar
|
||||
|
||||
from .inject_replace import inject_replace
|
||||
|
||||
P = ParamSpec("P")
|
||||
T = TypeVar("T")
|
||||
R = TypeVar("R")
|
||||
|
||||
def inject_after[T: object, **P, R](
|
||||
obj: T, target: Callable[P, R], hook: Callable[[R], R]
|
||||
) -> T:
|
||||
|
||||
def inject_after(obj: T, target: Callable[P, R], hook: Callable[[R], R]) -> T:
|
||||
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
return hook(target(*args, **kwargs))
|
||||
|
||||
return inject_replace(obj, target, inner)
|
||||
|
||||
|
||||
def after_hook[**P, R](
|
||||
def after_hook(
|
||||
obj: object, target: Callable[P, R]
|
||||
) -> Callable[[Callable[[R], R]], Callable[[R], R]]:
|
||||
def hook(new: Callable[[R], R]) -> Callable[[R], R]:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
from typing import Any, Callable
|
||||
from typing import Any, Callable, ParamSpec, TypeVar
|
||||
|
||||
from .inject_replace import inject_replace
|
||||
|
||||
P = ParamSpec("P")
|
||||
T = TypeVar("T")
|
||||
R = TypeVar("R")
|
||||
|
||||
def inject_before[T: object, **P, R](
|
||||
obj: T, target: Callable[P, R], hook: Callable[P, Any]
|
||||
) -> T:
|
||||
|
||||
def inject_before(obj: T, target: Callable[P, R], hook: Callable[P, Any]) -> T:
|
||||
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
hook(*args, **kwargs)
|
||||
return target(*args, **kwargs)
|
||||
|
|
@ -13,7 +15,7 @@ def inject_before[T: object, **P, R](
|
|||
return inject_replace(obj, target, inner)
|
||||
|
||||
|
||||
def before_hook[**P, R](
|
||||
def before_hook(
|
||||
obj: object, target: Callable[P, R]
|
||||
) -> Callable[[Callable[P, Any]], Callable[P, Any]]:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import sys
|
||||
from types import FunctionType
|
||||
from typing import Any, Callable, cast
|
||||
from typing import Any, Callable, TypeVar, cast
|
||||
|
||||
T = TypeVar("T", bound=Callable[..., Any])
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
|
||||
def inject_const[T: Callable[..., Any]](**consts: Any) -> Callable[[T], T]:
|
||||
def inject_const(**consts: Any) -> Callable[[T], T]:
|
||||
def inner(func: T) -> T:
|
||||
values = [*func.__code__.co_consts]
|
||||
for i, name in enumerate(func.__code__.co_varnames):
|
||||
|
|
@ -26,7 +28,7 @@ if sys.version_info >= (3, 13):
|
|||
|
||||
else:
|
||||
|
||||
def inject_const[T: Callable[..., Any]](**consts: Any) -> Callable[[T], T]:
|
||||
def inject_const(**consts: Any) -> Callable[[T], T]:
|
||||
def inner(func: T) -> T:
|
||||
values = [*func.__code__.co_consts]
|
||||
for i, name in enumerate(func.__code__.co_varnames):
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
from typing import Callable
|
||||
from typing import Callable, ParamSpec, TypeVar
|
||||
|
||||
P = ParamSpec("P")
|
||||
T = TypeVar("T")
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
def inject_replace[T: object, **P, R](
|
||||
obj: T, old: Callable[P, R], new: Callable[P, R]
|
||||
) -> T:
|
||||
def inject_replace(obj: T, old: Callable[P, R], new: Callable[P, R]) -> T:
|
||||
for k, v in obj.__dict__.items():
|
||||
if v is old:
|
||||
setattr(obj, k, new)
|
||||
return obj
|
||||
|
||||
|
||||
def replace_hook[**P, R](
|
||||
def replace_hook(
|
||||
obj: object, old: Callable[P, R]
|
||||
) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
||||
def hook(new: Callable[P, R]) -> Callable[P, R]:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, overload
|
||||
from typing import Any, Callable, ParamSpec, TypeVar, overload
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
@overload
|
||||
def pass_exceptions[**P](
|
||||
def pass_exceptions(
|
||||
*errors: type[Exception],
|
||||
) -> Callable[[Callable[P, Any | None]], Callable[P, Any | None]]: ...
|
||||
@overload
|
||||
def pass_exceptions[**P, R](
|
||||
def pass_exceptions(
|
||||
*errors: type[Exception],
|
||||
default: R,
|
||||
) -> Callable[[Callable[P, R]], Callable[P, R]]: ...
|
||||
|
|
|
|||
|
|
@ -1,2 +1,7 @@
|
|||
def singleton[T](cls: type[T]) -> T:
|
||||
from typing import TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def singleton(cls: type[T]) -> T:
|
||||
return cls()
|
||||
|
|
|
|||
|
|
@ -1,23 +1,21 @@
|
|||
import pickle
|
||||
from typing import Final, override
|
||||
from typing import Final, Generic, TypeVar
|
||||
|
||||
from .field import Field
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class AutoField[T](Field[T]):
|
||||
|
||||
class AutoField(Field[T], Generic[T]):
|
||||
__slots__ = ("__target_type",)
|
||||
|
||||
def __init__(
|
||||
self, default_value: T, *, target_type: type[T] | None = None
|
||||
) -> None:
|
||||
def __init__(self, default_value: T, *, target_type: type[T] | None = None) -> None:
|
||||
super().__init__(default_value)
|
||||
self.__target_type: Final = target_type
|
||||
|
||||
@override
|
||||
def serialize(self, value: T, /) -> bytes:
|
||||
return pickle.dumps(value)
|
||||
|
||||
@override
|
||||
def deserialize(self, serialized: bytes, /) -> T:
|
||||
value = pickle.loads(serialized)
|
||||
if not isinstance(value, self.__target_type or object):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
from typing import override
|
||||
|
||||
from .field import Field
|
||||
|
||||
|
||||
class BoolField(Field[bool]):
|
||||
@override
|
||||
def serialize(self, value: bool, /) -> bytes:
|
||||
return b"\x01" if value else b"\x00"
|
||||
|
||||
@override
|
||||
def deserialize(self, serialized: bytes, /) -> bool:
|
||||
return serialized == b"\x01"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Callable, Final, final
|
||||
from typing import TYPE_CHECKING, Any, Callable, Final, Generic, TypeVar, final
|
||||
|
||||
from snakia.property.priv_property import PrivProperty
|
||||
from snakia.utils import inherit
|
||||
|
||||
T = TypeVar("T")
|
||||
R = TypeVar("R")
|
||||
|
||||
class Field[T: Any](ABC, PrivProperty[T]):
|
||||
|
||||
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)
|
||||
|
|
@ -34,23 +37,19 @@ class Field[T: Any](ABC, PrivProperty[T]):
|
|||
|
||||
@final
|
||||
@classmethod
|
||||
def custom[R](
|
||||
def custom(
|
||||
cls: type[Field[Any]],
|
||||
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:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
import struct
|
||||
from typing import override
|
||||
|
||||
from .field import Field
|
||||
|
||||
|
||||
class FloatField(Field[float]):
|
||||
@override
|
||||
def serialize(self, value: float, /) -> bytes:
|
||||
return struct.pack(">f", value)
|
||||
|
||||
@override
|
||||
def deserialize(self, serialized: bytes, /) -> float:
|
||||
return struct.unpack(">f", serialized)[0] # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
from typing import override
|
||||
|
||||
from .field import Field
|
||||
|
||||
|
||||
class IntField(Field[int]):
|
||||
@override
|
||||
def serialize(self, value: int, /) -> bytes:
|
||||
length = (value.bit_length() + 7) // 8
|
||||
return value.to_bytes(length, "little")
|
||||
|
||||
@override
|
||||
def deserialize(self, serialized: bytes, /) -> int:
|
||||
return int.from_bytes(serialized, "little")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Final, override
|
||||
from typing import Final
|
||||
|
||||
from .field import Field
|
||||
|
||||
|
|
@ -8,10 +8,8 @@ class StrField(Field[str]):
|
|||
super().__init__(default_value)
|
||||
self.encoding: Final = encoding
|
||||
|
||||
@override
|
||||
def serialize(self, value: str, /) -> bytes:
|
||||
return value.encode(self.encoding)
|
||||
|
||||
@override
|
||||
def deserialize(self, serialized: bytes, /) -> str:
|
||||
return serialized.decode(self.encoding)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from ctypes import CDLL, Array, c_char, c_char_p, create_string_buffer
|
||||
from typing import Any, Final, Literal, cast, overload
|
||||
from typing import Any, Final, Literal, TypeVar, cast, overload
|
||||
|
||||
from .layer import PlatformLayer
|
||||
from .os import PlatformOS
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class AndroidLayer(PlatformLayer[Literal[PlatformOS.ANDROID]]):
|
||||
target = PlatformOS.ANDROID
|
||||
|
|
@ -16,7 +18,7 @@ class AndroidLayer(PlatformLayer[Literal[PlatformOS.ANDROID]]):
|
|||
def get_prop(self, name: str) -> str | None: ...
|
||||
|
||||
@overload
|
||||
def get_prop[T](self, name: str, default: T) -> str | T: ...
|
||||
def get_prop(self, name: str, default: T) -> str | T: ...
|
||||
|
||||
def get_prop(self, name: str, default: Any = None) -> Any:
|
||||
buffer = create_string_buffer(self.PROP_VALUE_MAX)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar, Self, final, overload
|
||||
from typing import ClassVar, Generic, TypeVar, final, overload
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from .os import PlatformOS
|
||||
|
||||
T = TypeVar("T", bound=PlatformOS)
|
||||
|
||||
class PlatformLayer[T: PlatformOS]:
|
||||
|
||||
class PlatformLayer(Generic[T]):
|
||||
target: ClassVar[PlatformOS] = PlatformOS.UNKNOWN
|
||||
|
||||
@final
|
||||
|
|
|
|||
|
|
@ -1,14 +1,29 @@
|
|||
from typing import Any, Callable, Self
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Generic, Protocol, TypeAlias, TypeVar
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from snakia.types import empty
|
||||
|
||||
type _Cell[T] = T | None
|
||||
type _Getter[T] = Callable[[Any, _Cell[T]], T]
|
||||
type _Setter[T] = Callable[[Any, _Cell[T], T], _Cell[T]]
|
||||
type _Deleter[T] = Callable[[Any, _Cell[T]], _Cell[T]]
|
||||
T = TypeVar("T")
|
||||
|
||||
_Cell: TypeAlias = T | None
|
||||
|
||||
|
||||
class CellProperty[T]:
|
||||
class _Getter(Protocol, Generic[T]):
|
||||
def __call__(self, instance: Any, cell: _Cell[T], /) -> T: ...
|
||||
|
||||
|
||||
class _Setter(Protocol, Generic[T]):
|
||||
def __call__(self, instance: Any, cell: _Cell[T], value: T, /) -> _Cell[T]: ...
|
||||
|
||||
|
||||
class _Deleter(Protocol, Generic[T]):
|
||||
def __call__(self, instance: Any, cell: _Cell[T], /) -> _Cell[T]: ...
|
||||
|
||||
|
||||
class CellProperty(Generic[T]):
|
||||
"""
|
||||
A property that uses a cell to store its value.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
from typing import Any, Callable, Self
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, Generic, TypeVar
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from snakia.types import empty
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class ClassProperty[T]:
|
||||
|
||||
class ClassProperty(Generic[T]):
|
||||
"""
|
||||
Class property
|
||||
"""
|
||||
|
|
@ -47,7 +53,7 @@ class ClassProperty[T]:
|
|||
return self
|
||||
|
||||
|
||||
def classproperty[T](
|
||||
def classproperty(
|
||||
fget: Callable[[Any], T],
|
||||
fset: Callable[[Any, T], None] = empty.func,
|
||||
fdel: Callable[[Any], None] = empty.func,
|
||||
|
|
@ -59,6 +65,6 @@ def classproperty[T](
|
|||
fset (Callable[[Any, T], None], optional): The setter function. Defaults to empty.func.
|
||||
fdel (Callable[[Any], None], optional): The deleter function. Defaults to empty.func.
|
||||
Returns:
|
||||
ClassProperty[T]: The class property.
|
||||
Self: The class property.
|
||||
"""
|
||||
return ClassProperty(fget, fset, fdel)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
from typing import Any, Callable, Self
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, Generic, TypeVar
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from snakia.types import empty
|
||||
|
||||
from .priv_property import PrivProperty
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class HookProperty[T](PrivProperty[T]):
|
||||
|
||||
class HookProperty(PrivProperty[T], Generic[T]):
|
||||
"""
|
||||
A property that calls a function when the property is set, get, or deleted.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from typing import Any
|
||||
from typing import Any, Generic, TypeVar
|
||||
|
||||
from .priv_property import PrivProperty
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class Initonly[T](PrivProperty[T]):
|
||||
|
||||
class Initonly(PrivProperty[T], Generic[T]):
|
||||
"""Property that can only be set once."""
|
||||
|
||||
def __set__(self, instance: Any, value: T, /) -> None:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
from typing import Any
|
||||
from typing import Any, Generic, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class PrivProperty[T]:
|
||||
class PrivProperty(Generic[T]):
|
||||
__slots__ = "__name", "__default_value"
|
||||
|
||||
__name: str
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
from typing import Any, Callable, Self
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, Generic, TypeVar
|
||||
|
||||
from snakia.types import empty
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class Property[T]:
|
||||
|
||||
class Property(Generic[T]):
|
||||
"""
|
||||
A property that can be set, get, and deleted.
|
||||
"""
|
||||
|
|
@ -34,17 +38,17 @@ class Property[T]:
|
|||
def __delete__(self, instance: Any, /) -> None:
|
||||
return self.__fdel(instance)
|
||||
|
||||
def getter(self, fget: Callable[[Any], T], /) -> Self:
|
||||
def getter(self, fget: Callable[[Any], T], /) -> Property[T]:
|
||||
"""Descriptor getter."""
|
||||
self.__fget = fget
|
||||
return self
|
||||
|
||||
def setter(self, fset: Callable[[Any, T], None], /) -> Self:
|
||||
def setter(self, fset: Callable[[Any, T], None], /) -> Property[T]:
|
||||
"""Descriptor setter."""
|
||||
self.__fset = fset
|
||||
return self
|
||||
|
||||
def deleter(self, fdel: Callable[[Any], None], /) -> Self:
|
||||
def deleter(self, fdel: Callable[[Any], None], /) -> Property[T]:
|
||||
"""Descriptor deleter."""
|
||||
self.__fdel = fdel
|
||||
return self
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
from typing import Any, Callable
|
||||
from typing import Any, Callable, Generic, TypeVar
|
||||
|
||||
from snakia.utils import throw
|
||||
|
||||
from .property import Property
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
class Readonly[T](Property[T]):
|
||||
|
||||
class Readonly(Property[T], Generic[T]):
|
||||
"""
|
||||
Readonly property.
|
||||
"""
|
||||
|
|
@ -26,7 +28,7 @@ class Readonly[T](Property[T]):
|
|||
)
|
||||
|
||||
|
||||
def readonly[T](value: T, *, strict: bool = False) -> Readonly[T]:
|
||||
def readonly(value: T, *, strict: bool = False) -> Readonly[T]:
|
||||
"""Create a readonly property with the given value.
|
||||
|
||||
Args:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ class OSRandom(Random[None]):
|
|||
"""
|
||||
|
||||
def bits(self, k: int) -> int:
|
||||
return int.from_bytes(os.urandom((k + 7) // 8)) & ((1 << k) - 1)
|
||||
v = os.urandom((k + 7) // 8)
|
||||
return int.from_bytes(v, "little") & ((1 << k) - 1)
|
||||
|
||||
def get_state(self) -> None:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import random
|
||||
from typing import TypeAlias
|
||||
|
||||
from .random import Random
|
||||
|
||||
type _State = tuple[int, tuple[int, ...], int | float | None]
|
||||
_State: TypeAlias = tuple[int, tuple[int, ...], int | float | None]
|
||||
|
||||
|
||||
class PythonRandom(Random[_State]):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
import builtins
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, MutableSequence, Sequence, final
|
||||
from typing import Any, Generic, MutableSequence, Sequence, TypeVar, final
|
||||
|
||||
S = TypeVar("S")
|
||||
T = TypeVar("T")
|
||||
M = TypeVar("M", bound=MutableSequence[Any])
|
||||
|
||||
|
||||
class Random[S](ABC):
|
||||
class Random(ABC, Generic[S]):
|
||||
"""
|
||||
A random number generator.
|
||||
"""
|
||||
|
|
@ -45,12 +49,12 @@ class Random[S](ABC):
|
|||
return self.bits(32) / (1 << 32)
|
||||
|
||||
@final
|
||||
def choice[T](self, seq: Sequence[T]) -> T:
|
||||
def choice(self, seq: Sequence[T]) -> T:
|
||||
"""Return a random element from a non-empty sequence."""
|
||||
return seq[self.below(len(seq))]
|
||||
|
||||
@final
|
||||
def shuffle[T: MutableSequence[Any]](self, seq: T) -> T:
|
||||
def shuffle(self, seq: M) -> M:
|
||||
"""Shuffle a sequence in place."""
|
||||
for i in range(len(seq) - 1, 0, -1):
|
||||
j = self.below(i + 1)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
from typing import Any, final
|
||||
from typing import Any, TypeVar, final
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@final
|
||||
|
|
@ -31,7 +33,7 @@ class UniqueType(type):
|
|||
def __eq__(cls, other: Any) -> bool:
|
||||
return cls is other
|
||||
|
||||
def __call__[T](cls: type[T]) -> T:
|
||||
def __call__(cls: type[T]) -> T:
|
||||
return cls.__new__(cls) # noqa: E1120 # pylint: disable=E1120
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from typing import Any
|
||||
from typing import Any, TypeVar
|
||||
|
||||
T = TypeVar("T", bound=type)
|
||||
|
||||
|
||||
def inherit[T: type](
|
||||
type_: T, attrs: dict[str, Any] | None = None, /, **kwargs: Any
|
||||
) -> T:
|
||||
def inherit(type_: T, attrs: dict[str, Any] | None = None, /, **kwargs: Any) -> T:
|
||||
"""
|
||||
Create a new class that inherits from the given class.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
from typing import Any, NoReturn, overload
|
||||
from typing import Any, NoReturn, TypeVar, overload
|
||||
|
||||
from exceptiongroup import ExceptionGroup
|
||||
|
||||
|
|
||||
from snakia.types.unset import Unset
|
||||
|
||||
T = TypeVar("T", bound=Exception)
|
||||
|
||||
|
||||
@overload
|
||||
def throw[T: Exception](
|
||||
def throw(
|
||||
*exceptions: T, # pyright: ignore[reportInvalidTypeVarUse]
|
||||
from_: Unset | BaseException = Unset(),
|
||||
) -> NoReturn: ...
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
from typing import Awaitable, Callable
|
||||
from typing import Awaitable, Callable, ParamSpec, TypeVar
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
def to_async[**P, R](func: Callable[P, R]) -> Callable[P, Awaitable[R]]:
|
||||
def to_async(func: Callable[P, R]) -> Callable[P, Awaitable[R]]:
|
||||
"""Convert a sync function to an async function."""
|
||||
|
||||
async def inner(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue
🚫 Codacy found a high ErrorProne issue: Redefining built-in 'ExceptionGroup'
The issue arises because the name
ExceptionGroupbeing imported from theexceptiongroupmodule is conflicting with the built-in nameExceptionGroupin Python. This can lead to confusion or unexpected behavior since it redefines a built-in name.To resolve this issue, you can rename the imported
ExceptionGroupto avoid the conflict. Here's the suggested code change:This comment was generated by an experimental AI tool.