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