Release/v0.4.2 #2
59 changed files with 450 additions and 322 deletions
2
.github/workflows/docs-publish.yml
vendored
2
.github/workflows/docs-publish.yml
vendored
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.12"]
|
||||
python-version: ["3.10"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
2
.github/workflows/pylint.yml
vendored
2
.github/workflows/pylint.yml
vendored
|
|
@ -7,7 +7,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.12"]
|
||||
python-version: ["3.10"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
2
.github/workflows/pypi-publish.yml
vendored
2
.github/workflows/pypi-publish.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.12"]
|
||||
python-version: ["3.10"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
13
flake.nix
13
flake.nix
|
|
@ -6,23 +6,20 @@
|
|||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
outputs = {
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
in {
|
||||
devShells.default = pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
python312
|
||||
python310
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
|
|
@ -31,7 +28,7 @@
|
|||
uv
|
||||
isort
|
||||
mypy
|
||||
python312Packages.pylint
|
||||
pylint
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "snakia"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
description = "Modern python framework"
|
||||
readme = "README.md"
|
||||
authors = [
|
||||
|
|
@ -10,15 +10,19 @@ keywords = ["python3", "event system", "ecs", "reactive programming"]
|
|||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: Free Threading",
|
||||
]
|
||||
requires-python = ">=3.12"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"exceptiongroup>=1.3.0",
|
||||
"networkx>=3.4.2",
|
||||
"pydantic>=2.12.3",
|
||||
"typing-extensions>=4.15.0",
|
||||
]
|
||||
license = "CC0-1.0"
|
||||
license-files = ["LICENSE"]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
# uv pip compile pyproject.toml -o requirements.txt
|
||||
annotated-types==0.7.0
|
||||
# via pydantic
|
||||
networkx==3.5
|
||||
exceptiongroup==1.3.0
|
||||
# via snakia (pyproject.toml)
|
||||
networkx==3.4.2
|
||||
# via snakia (pyproject.toml)
|
||||
pydantic==2.12.3
|
||||
# via snakia (pyproject.toml)
|
||||
|
|
@ -10,6 +12,8 @@ pydantic-core==2.41.4
|
|||
# via pydantic
|
||||
typing-extensions==4.15.0
|
||||
# via
|
||||
# snakia (pyproject.toml)
|
||||
# exceptiongroup
|
||||
# pydantic
|
||||
# pydantic-core
|
||||
# typing-inspection
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
89
uv.lock
generated
89
uv.lock
generated
|
|
@ -1,6 +1,10 @@
|
|||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.12"
|
||||
requires-python = ">=3.10"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.11'",
|
||||
"python_full_version < '3.11'",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
|
|
@ -11,10 +15,37 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "networkx"
|
||||
version = "3.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version < '3.11'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "networkx"
|
||||
version = "3.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.11'",
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" },
|
||||
|
|
@ -44,6 +75,33 @@ dependencies = [
|
|||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5", size = 457557, upload-time = "2025-10-14T10:23:47.909Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/3d/9b8ca77b0f76fcdbf8bc6b72474e264283f461284ca84ac3fde570c6c49a/pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e", size = 2111197, upload-time = "2025-10-14T10:19:43.303Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/92/b7b0fe6ed4781642232755cb7e56a86e2041e1292f16d9ae410a0ccee5ac/pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b", size = 1917909, upload-time = "2025-10-14T10:19:45.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/8c/3eb872009274ffa4fb6a9585114e161aa1a0915af2896e2d441642929fe4/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd", size = 1969905, upload-time = "2025-10-14T10:19:46.567Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/21/35adf4a753bcfaea22d925214a0c5b880792e3244731b3f3e6fec0d124f7/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945", size = 2051938, upload-time = "2025-10-14T10:19:48.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/d0/cdf7d126825e36d6e3f1eccf257da8954452934ede275a8f390eac775e89/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706", size = 2250710, upload-time = "2025-10-14T10:19:49.619Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/1c/af1e6fd5ea596327308f9c8d1654e1285cc3d8de0d584a3c9d7705bf8a7c/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba", size = 2367445, upload-time = "2025-10-14T10:19:51.269Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/81/8cece29a6ef1b3a92f956ea6da6250d5b2d2e7e4d513dd3b4f0c7a83dfea/pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b", size = 2072875, upload-time = "2025-10-14T10:19:52.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/37/a6a579f5fc2cd4d5521284a0ab6a426cc6463a7b3897aeb95b12f1ba607b/pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d", size = 2191329, upload-time = "2025-10-14T10:19:54.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/03/505020dc5c54ec75ecba9f41119fd1e48f9e41e4629942494c4a8734ded1/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700", size = 2151658, upload-time = "2025-10-14T10:19:55.843Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/5d/2c0d09fb53aa03bbd2a214d89ebfa6304be7df9ed86ee3dc7770257f41ee/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6", size = 2316777, upload-time = "2025-10-14T10:19:57.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/4b/c2c9c8f5e1f9c864b57d08539d9d3db160e00491c9f5ee90e1bfd905e644/pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9", size = 2320705, upload-time = "2025-10-14T10:19:59.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/c3/a74c1c37f49c0a02c89c7340fafc0ba816b29bd495d1a31ce1bdeacc6085/pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57", size = 1975464, upload-time = "2025-10-14T10:20:00.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/23/5dd5c1324ba80303368f7569e2e2e1a721c7d9eb16acb7eb7b7f85cb1be2/pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc", size = 2024497, upload-time = "2025-10-14T10:20:03.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/4c/f6cbfa1e8efacd00b846764e8484fe173d25b8dab881e277a619177f3384/pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80", size = 2109062, upload-time = "2025-10-14T10:20:04.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/f8/40b72d3868896bfcd410e1bd7e516e762d326201c48e5b4a06446f6cf9e8/pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae", size = 1916301, upload-time = "2025-10-14T10:20:06.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/4d/d203dce8bee7faeca791671c88519969d98d3b4e8f225da5b96dad226fc8/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827", size = 1968728, upload-time = "2025-10-14T10:20:08.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/f5/6a66187775df87c24d526985b3a5d78d861580ca466fbd9d4d0e792fcf6c/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f", size = 2050238, upload-time = "2025-10-14T10:20:09.766Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/b9/78336345de97298cf53236b2f271912ce11f32c1e59de25a374ce12f9cce/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def", size = 2249424, upload-time = "2025-10-14T10:20:11.732Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/bb/a4584888b70ee594c3d374a71af5075a68654d6c780369df269118af7402/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2", size = 2366047, upload-time = "2025-10-14T10:20:13.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/8d/17fc5de9d6418e4d2ae8c675f905cdafdc59d3bf3bf9c946b7ab796a992a/pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8", size = 2071163, upload-time = "2025-10-14T10:20:15.307Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/e7/03d2c5c0b8ed37a4617430db68ec5e7dbba66358b629cd69e11b4d564367/pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265", size = 2190585, upload-time = "2025-10-14T10:20:17.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/fc/15d1c9fe5ad9266a5897d9b932b7f53d7e5cfc800573917a2c5d6eea56ec/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c", size = 2150109, upload-time = "2025-10-14T10:20:19.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/ef/e735dd008808226c83ba56972566138665b71477ad580fa5a21f0851df48/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a", size = 2315078, upload-time = "2025-10-14T10:20:20.742Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/00/806efdcf35ff2ac0f938362350cd9827b8afb116cc814b6b75cf23738c7c/pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e", size = 2318737, upload-time = "2025-10-14T10:20:22.306Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/7e/6ac90673fe6cb36621a2283552897838c020db343fa86e513d3f563b196f/pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03", size = 1974160, upload-time = "2025-10-14T10:20:23.817Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/9d/7c5e24ee585c1f8b6356e1d11d40ab807ffde44d2db3b7dfd6d20b09720e/pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e", size = 2021883, upload-time = "2025-10-14T10:20:25.48Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/90/5c172357460fc28b2871eb4a0fb3843b136b429c6fa827e4b588877bf115/pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db", size = 1968026, upload-time = "2025-10-14T10:20:27.039Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/81/d3b3e95929c4369d30b2a66a91db63c8ed0a98381ae55a45da2cd1cc1288/pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887", size = 2099043, upload-time = "2025-10-14T10:20:28.561Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/da/46fdac49e6717e3a94fc9201403e08d9d61aa7a770fab6190b8740749047/pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2", size = 1910699, upload-time = "2025-10-14T10:20:30.217Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/63/4d948f1b9dd8e991a5a98b77dd66c74641f5f2e5225fee37994b2e07d391/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999", size = 1952121, upload-time = "2025-10-14T10:20:32.246Z" },
|
||||
|
|
@ -96,25 +154,50 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/1e/29/b53a9ca6cd366bfc928823679c6a76c7a4c69f8201c0ba7903ad18ebae2f/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa", size = 2041183, upload-time = "2025-10-14T10:22:08.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/3d/f8c1a371ceebcaf94d6dd2d77c6cf4b1c078e13a5837aee83f760b4f7cfd/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d", size = 1993542, upload-time = "2025-10-14T10:22:11.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897, upload-time = "2025-10-14T10:22:13.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/12/5ba58daa7f453454464f92b3ca7b9d7c657d8641c48e370c3ebc9a82dd78/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b", size = 2122139, upload-time = "2025-10-14T10:22:47.288Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/fb/6860126a77725c3108baecd10fd3d75fec25191d6381b6eb2ac660228eac/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42", size = 1936674, upload-time = "2025-10-14T10:22:49.555Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/be/57dcaa3ed595d81f8757e2b44a38240ac5d37628bce25fb20d02c7018776/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee", size = 1956398, upload-time = "2025-10-14T10:22:52.19Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/1d/679a344fadb9695f1a6a294d739fbd21d71fa023286daeea8c0ed49e7c2b/pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c", size = 2138674, upload-time = "2025-10-14T10:22:54.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/48/ae937e5a831b7c0dc646b2ef788c27cd003894882415300ed21927c21efa/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537", size = 2112087, upload-time = "2025-10-14T10:22:56.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/db/6db8073e3d32dae017da7e0d16a9ecb897d0a4d92e00634916e486097961/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94", size = 1920387, upload-time = "2025-10-14T10:22:59.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/c1/dd3542d072fcc336030d66834872f0328727e3b8de289c662faa04aa270e/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c", size = 1951495, upload-time = "2025-10-14T10:23:02.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/c6/db8d13a1f8ab3f1eb08c88bd00fd62d44311e3456d1e85c0e59e0a0376e7/pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335", size = 2139008, upload-time = "2025-10-14T10:23:04.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/d4/912e976a2dd0b49f31c98a060ca90b353f3b73ee3ea2fd0030412f6ac5ec/pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00", size = 2106739, upload-time = "2025-10-14T10:23:06.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/f0/66ec5a626c81eba326072d6ee2b127f8c139543f1bf609b4842978d37833/pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9", size = 1932549, upload-time = "2025-10-14T10:23:09.24Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/af/625626278ca801ea0a658c2dcf290dc9f21bb383098e99e7c6a029fccfc0/pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2", size = 2135093, upload-time = "2025-10-14T10:23:11.626Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/f6/2fba049f54e0f4975fef66be654c597a1d005320fa141863699180c7697d/pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258", size = 2187971, upload-time = "2025-10-14T10:23:14.437Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/80/65ab839a2dfcd3b949202f9d920c34f9de5a537c3646662bdf2f7d999680/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347", size = 2147939, upload-time = "2025-10-14T10:23:16.831Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/58/627565d3d182ce6dfda18b8e1c841eede3629d59c9d7cbc1e12a03aeb328/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa", size = 2311400, upload-time = "2025-10-14T10:23:19.234Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/06/8a84711162ad5a5f19a88cead37cca81b4b1f294f46260ef7334ae4f24d3/pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a", size = 2316840, upload-time = "2025-10-14T10:23:21.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/8b/b7bb512a4682a2f7fbfae152a755d37351743900226d29bd953aaf870eaa/pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d", size = 2149135, upload-time = "2025-10-14T10:23:24.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/7d/138e902ed6399b866f7cfe4435d22445e16fff888a1c00560d9dc79a780f/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5", size = 2104721, upload-time = "2025-10-14T10:23:26.906Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/13/0525623cf94627f7b53b4c2034c81edc8491cbfc7c28d5447fa318791479/pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2", size = 1931608, upload-time = "2025-10-14T10:23:29.306Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/f9/744bc98137d6ef0a233f808bfc9b18cf94624bf30836a18d3b05d08bf418/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd", size = 2132986, upload-time = "2025-10-14T10:23:32.057Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/c8/629e88920171173f6049386cc71f893dff03209a9ef32b4d2f7e7c264bcf/pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c", size = 2187516, upload-time = "2025-10-14T10:23:34.871Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/0f/4f2734688d98488782218ca61bcc118329bf5de05bb7fe3adc7dd79b0b86/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405", size = 2146146, upload-time = "2025-10-14T10:23:37.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/f2/ab385dbd94a052c62224b99cf99002eee99dbec40e10006c78575aead256/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8", size = 2311296, upload-time = "2025-10-14T10:23:40.145Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/8e/e4f12afe1beeb9823bba5375f8f258df0cc61b056b0195fb1cf9f62a1a58/pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308", size = 2315386, upload-time = "2025-10-14T10:23:42.624Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/f7/925f65d930802e3ea2eb4d5afa4cb8730c8dc0d2cb89a59dc4ed2fcb2d74/pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f", size = 2147775, upload-time = "2025-10-14T10:23:45.406Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snakia"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "networkx" },
|
||||
{ name = "exceptiongroup" },
|
||||
{ name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
||||
{ name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "exceptiongroup", specifier = ">=1.3.0" },
|
||||
{ name = "networkx", specifier = ">=3.4.2" },
|
||||
{ name = "pydantic", specifier = ">=2.12.3" },
|
||||
{ name = "typing-extensions", specifier = ">=4.15.0" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
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.