Compare commits

...

40 commits
v0.4.1 ... main

Author SHA1 Message Date
rus07tam
006c0a494d
Merge pull request #12 from rus07tam/release/v0.6.0
Release/v0.6.0
2025-11-26 17:17:42 +03:00
rus07tam
36d9da10bc feat: add OptionalField 2025-11-26 14:06:10 +00:00
rus07tam
7e8b57793f feat: add ListField 2025-11-26 14:02:48 +00:00
rus07tam
2149fcf308 refactor: add Unset annotation 2025-11-26 14:00:22 +00:00
rus07tam
fee08f3609 feat: add UniqueType.unwrap_or() 2025-11-26 13:58:06 +00:00
rus07tam
2cc15bf250 feat: add ret() 2025-11-26 13:48:31 +00:00
rus07tam
3d132251fc refactor: merge side.py and calls.py to funcs.py 2025-11-26 13:46:49 +00:00
rus07tam
420f7626fb feat: add support default_factory in AutoField 2025-11-26 13:33:16 +00:00
rus07tam
1e82a457ac feat: add default_factory in PrivProperty 2025-11-26 13:26:21 +00:00
rus07tam
12c0f42f99 fix: renaming methods to more logical ones 2025-11-24 15:24:45 +00:00
rus07tam
e61dd387d4 feat!: remove broken plugin system 2025-11-24 15:05:07 +00:00
rus07tam
34b5a1272b refactor: small changes 2025-11-24 15:01:44 +00:00
rus07tam
af4974075c fix: sync license in pyproject.toml with LICENSE file 2025-11-24 14:55:29 +00:00
rus07tam
631c416723 types: add py.typed file 2025-11-24 14:54:46 +00:00
rus07tam
ae0427ec75
Merge pull request #8 from rus07tam/release/v0.5.0
Release/v0.5.0
2025-11-24 16:28:03 +03:00
rus07tam
cb7629b6fe chore: bump version to 0.5.0 2025-11-24 13:05:03 +00:00
rus07tam
ef8db0787e chore: update deps 2025-11-24 13:03:50 +00:00
rus07tam
9d04fb8a2e chore: apply isort & black 2025-11-24 12:58:50 +00:00
rus07tam
002ec9b63f chore: switch license from CC0 to Unlicense 2025-11-24 12:55:51 +00:00
rus07tam
43a6a8b183 feat: add async_chain() 2025-11-24 12:55:27 +00:00
rus07tam
498087befb chore: merge nolock.py and gil_enabled.py to gil.py 2025-11-23 11:01:28 +00:00
rus07tam
7b34426c82 chore: format code 2025-11-23 10:49:54 +00:00
rus07tam
a49fe64be8 feat: add async_combine 2025-11-23 10:48:20 +00:00
rus07tam
c7cd08b7e0 feat: more safety in BaseBindable, AsyncBindable, Bindable 2025-11-23 10:47:37 +00:00
rus07tam
7f83370873 chore: rename rx modules 2025-11-23 10:46:45 +00:00
rus07tam
e2286f1944 fix: UniqueType.map() 2025-11-23 10:44:08 +00:00
rus07tam
fe2a53860b feat: add Marker with object tagging system 2025-11-23 10:31:23 +00:00
rus07tam
346140d675 feat: add map(), and_then(), or_else() in UniqueType 2025-11-23 10:30:34 +00:00
rus07tam
e065b51490 feat: add throw(), call(), caller() 2025-11-23 10:28:19 +00:00
rus07tam
c879b22f43 feat: add side and side_func – clean alternative to "or print()" in lambdas 2025-11-23 10:14:46 +00:00
rus07tam
6ca21a633c feat: add get_attrs, get_or_set_attr 2025-11-23 10:14:12 +00:00
rus07tam
694dbf99be feat: add inject_decorator, hook_decorator and replace_decorator 2025-11-23 09:20:45 +00:00
rus07tam
a392685506 fix: replace typing.NamedTuple with typing_extensions.NamedTuple to support multiple inheritance 2025-11-23 09:16:25 +00:00
rus07tam
9840c1706e chore: add types-networkx 2025-11-21 15:55:39 +00:00
rus07tam
8f9347381a perf: add slots 2025-11-21 15:41:49 +00:00
rus07tam
dbc57b8ecb
Merge pull request #2 from rus07tam/release/v0.4.2
Release/v0.4.2
2025-10-29 18:15:30 +03:00
rus07tam
7f25bcbcb4 chore: bump version to 0.4.2 2025-10-29 15:05:43 +00:00
rus07tam
dc33a78700 chore: update pyproject.toml classifiers 2025-10-29 15:04:49 +00:00
rus07tam
4f5d3d550b refactor: replace typing.Self, ExceptionGroup and use explicit generics for 3.10 compat 2025-10-29 15:04:05 +00:00
rus07tam
521aa7c323 ci: lower minimum python version from 3.12 to 3.10 2025-10-29 15:03:12 +00:00
91 changed files with 1523 additions and 983 deletions

View file

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
python-version: ["3.10"]
steps:
- uses: actions/checkout@v4

View file

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
python-version: ["3.10"]
steps:
- uses: actions/checkout@v4

View file

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
python-version: ["3.10"]
steps:
- uses: actions/checkout@v4

137
LICENSE
View file

@ -1,121 +1,24 @@
Creative Commons Legal Code
This is free and unencumbered software released into the public domain.
CC0 1.0 Universal
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
Statement of Purpose
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
For more information, please refer to <https://unlicense.org>

View file

@ -1,9 +1,7 @@
import sys
from pathlib import Path
sys.path.insert(
0, str((Path(__file__).parent.parent.parent / "src").resolve())
)
sys.path.insert(0, str((Path(__file__).parent.parent.parent / "src").resolve()))
project = "Snakia"
copyright = "2025, RuJect"

View file

@ -1,88 +0,0 @@
from typing import final
from pydantic import Field
from snakia.core.ecs import Component
from snakia.core.ecs.system import System
from snakia.core.engine import Engine
from snakia.core.es import Event
from snakia.core.loader import Meta, Plugin, PluginProcessor
from snakia.types import Version
class HealthComponent(Component):
max_value: int = Field(default=100, ge=0)
value: int = Field(default=100, ge=0)
class DamageComponent(Component):
damage: int = Field(ge=0)
ticks: int = Field(default=1, ge=0)
class HealComponent(Component):
heal: int = Field(ge=0)
ticks: int = Field(default=1, ge=0)
class DeathEvent(Event):
entity: int = Field()
class HealthProcessor(PluginProcessor):
def process(self, system: System) -> None:
for entity, (heal, health) in system.get_components(
HealComponent, HealthComponent
):
health.value += heal.heal
heal.ticks -= 1
if heal.ticks <= 0:
system.remove_component(entity, HealComponent)
for entity, (damage, health) in system.get_components(
DamageComponent, HealthComponent
):
health.value -= damage.damage
damage.ticks -= 1
if damage.ticks <= 0:
system.remove_component(entity, DamageComponent)
if health.value <= 0:
system.remove_component(entity, HealthComponent)
self.plugin.dispatcher.publish(DeathEvent(entity=entity))
@final
class HealthPlugin(
Plugin,
meta=Meta(
name="health",
author="snakia",
version=Version.from_args(1, 0, 0),
subscribers=(),
processors=(HealthProcessor,),
),
):
def on_load(self) -> None:
pass
def on_unload(self) -> None:
pass
def main() -> None:
engine = Engine()
engine.loader.register(HealthPlugin)
engine.loader.load_all()
@engine.dispatcher.on(DeathEvent)
def on_death(event: DeathEvent) -> None:
print(f"Entity: {event.entity} is death!")
player = engine.system.create_entity()
engine.system.add_component(player, HealthComponent())
engine.system.add_component(player, DamageComponent(damage=10, ticks=10))
engine.start()
if __name__ == "__main__":
main()

View file

@ -2,8 +2,12 @@ import sys
from snakia.core.tui import CanvasChar, RenderContext
from snakia.core.tui.render import ANSIRenderer
from snakia.core.tui.widgets import (BoxWidget, HorizontalSplitWidget,
TextWidget, VerticalSplitWidget)
from snakia.core.tui.widgets import (
BoxWidget,
HorizontalSplitWidget,
TextWidget,
VerticalSplitWidget,
)
class StdoutTarget:

View file

@ -6,23 +6,20 @@
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{
nixpkgs,
flake-utils,
...
}:
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
];
};
}

0
py.typed Normal file
View file

View file

@ -1,6 +1,6 @@
[project]
name = "snakia"
version = "0.4.1"
version = "0.5.0"
description = "Modern python framework"
readme = "README.md"
authors = [
@ -10,17 +10,23 @@ 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 = [
"annotated-types>=0.7.0",
"exceptiongroup>=1.3.1",
"networkx>=3.4.2",
"pydantic>=2.12.3",
"pydantic>=2.11.10",
"types-networkx>=3.5.0.20251106",
"typing-extensions>=4.15.0",
]
license = "CC0-1.0"
license = "Unlicense"
license-files = ["LICENSE"]
[project.urls]
@ -38,4 +44,4 @@ disable = ["C0114", "C0115", "C0116", "R0801"]
max-args = 8
max-positional-arguments = 7
min-public-methods = 1
fail-on = "error"
fail-on = "error"

View file

@ -1,15 +1,25 @@
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o requirements.txt
annotated-types==0.7.0
# via pydantic
networkx==3.5
# via
# snakia (pyproject.toml)
# pydantic
exceptiongroup==1.3.1
# via snakia (pyproject.toml)
networkx==3.4.2
# via snakia (pyproject.toml)
numpy==2.2.6
# via types-networkx
pydantic==2.12.3
# via snakia (pyproject.toml)
pydantic-core==2.41.4
# via pydantic
types-networkx==3.5.0.20251106
# via snakia (pyproject.toml)
typing-extensions==4.15.0
# via
# snakia (pyproject.toml)
# exceptiongroup
# pydantic
# pydantic-core
# typing-inspection

View file

@ -3,21 +3,38 @@ 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
import networkx as nx
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:
"""
A system is a collection of entities and components that can be processed by processors.
"""
__slots__ = (
"__processors",
"__components",
"__entitites",
"__entity_counter",
"__dead_entities",
"__is_running",
)
__processors: list[Processor]
__components: dict[type[Component], set[int]]
__entitites: dict[int, dict[type[Component], Component]]
@ -46,9 +63,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 +82,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 +115,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 +127,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 +176,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 +207,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 +248,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."""

View file

@ -3,14 +3,19 @@ from typing import Final
from .ecs import System
from .es import Dispatcher
from .loader.loader import Loader
class Engine:
__slots__ = (
"system",
"dispatcher",
"__system_thread",
"__dispatcher_thread",
)
def __init__(self) -> None:
self.system: Final = System()
self.dispatcher: Final = Dispatcher()
self.loader: Final = Loader(self)
self.__system_thread: threading.Thread | None = None
self.__dispatcher_thread: threading.Thread | None = None

View file

@ -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:

View file

@ -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,35 +11,33 @@ from .filter import Filter
from .handler import Handler
from .subscriber import Subscriber
T = TypeVar("T", bound=Event)
class Dispatcher:
"""
Event dispatcher
"""
__running: bool
__slots__ = ("__queue", "__subscribers", "__running")
def __init__(self) -> None:
self.__queue: Final = queue.Queue[Event]()
self.__subscribers: Final[
dict[type[Event], list[Subscriber[Event]]]
] = defaultdict(list)
self.__running = False
self.__subscribers: Final[dict[type[Event], list[Subscriber[Event]]]] = (
defaultdict(list)
)
self.__running: bool = False
@property
def is_running(self) -> bool:
"""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)

View file

@ -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: ...

View file

@ -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]: ...

View file

@ -1,16 +1,20 @@
from __future__ import annotations
from typing import NamedTuple
from typing import Generic, TypeVar
from typing_extensions import NamedTuple
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

View file

@ -1,27 +1,33 @@
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: ...
@final
class _ExceptionManager:
__slots__ = ("__hooks", "excepthook")
def __init__(self) -> None:
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]:

View file

@ -1,6 +0,0 @@
from .loadable import Loadable
from .meta import Meta
from .plugin import Plugin
from .plugin_processor import PluginProcessor
__all__ = ["Loadable", "Meta", "Plugin", "PluginProcessor"]

View file

@ -1,18 +0,0 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from snakia.core.engine import Engine
class Loadable(ABC):
@abstractmethod
def __init__(self, engine: Engine) -> None: ...
@abstractmethod
def load(self) -> None: ...
@abstractmethod
def unload(self) -> None: ...

View file

@ -1,24 +0,0 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Callable, Final
if TYPE_CHECKING:
from snakia.core.engine import Engine
from snakia.core.loader import Loadable
class Loader:
def __init__(self, engine: Engine) -> None:
self.__engine: Final = engine
self.__loadables: Final[list[Loadable]] = []
def register(self, loadable: Callable[[Engine], Loadable]) -> None:
self.__loadables.append(loadable(self.__engine))
def load_all(self) -> None:
for loadable in self.__loadables:
loadable.load()
def unload_all(self) -> None:
for loadable in reversed(self.__loadables):
loadable.unload()

View file

@ -1,39 +0,0 @@
from __future__ import annotations
from pydantic import BaseModel, ConfigDict, Field
from snakia.core.es import Event, Subscriber
from snakia.types import Version
from .plugin_processor import PluginProcessor
class Meta(BaseModel):
model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
name: str = Field(
default="unknown",
min_length=4,
max_length=32,
pattern="^[a-z0-9_]{4,32}$",
)
author: str = Field(
default="unknown",
min_length=4,
max_length=32,
pattern="^[a-z0-9_]{4,32}$",
)
version: Version = Field(
default_factory=lambda: Version(major=1, minor=0, patch=0)
)
subscribers: tuple[tuple[type[Event], Subscriber[Event]], ...] = Field(
default_factory=tuple
)
processors: tuple[type[PluginProcessor], ...] = Field(
default_factory=tuple
)
@property
def id(self) -> str:
return f"{self.author}.{self.name}"

View file

@ -1,72 +0,0 @@
from __future__ import annotations
from abc import abstractmethod
from typing import TYPE_CHECKING, ClassVar, Final, final
from snakia.core.ecs import System
from snakia.core.es import Dispatcher
from .loadable import Loadable
from .meta import Meta
if TYPE_CHECKING:
from snakia.core.engine import Engine
class Plugin(Loadable):
__meta: ClassVar[Meta]
@final
def __init__(self, engine: Engine) -> None:
self.__engine: Final = engine
@final
@property
def meta(self) -> Meta:
"""The plugin's metadata."""
return self.__meta
@final
@property
def dispatcher(self) -> Dispatcher:
return self.__engine.dispatcher
@final
@property
def system(self) -> System:
return self.__engine.system
@final
def load(self) -> None:
for processor in self.meta.processors:
self.__engine.system.add_processor(processor(self))
for event_type, subscriber in self.meta.subscribers:
self.__engine.dispatcher.subscribe(event_type, subscriber)
self.on_load()
@final
def unload(self) -> None:
for processor in self.meta.processors:
self.__engine.system.remove_processor(processor)
for event_type, subscriber in self.meta.subscribers:
self.__engine.dispatcher.unsubscribe(event_type, subscriber)
self.on_unload()
@abstractmethod
def on_load(self) -> None:
pass
@abstractmethod
def on_unload(self) -> None:
pass
if TYPE_CHECKING:
@final
def __init_subclass__(cls, meta: Meta) -> None:
pass
else:
def __init_subclass__(cls, meta: Meta) -> None:
cls.meta = meta

View file

@ -1,14 +0,0 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Final, final
from snakia.core.ecs import Processor
if TYPE_CHECKING:
from .plugin import Plugin
class PluginProcessor(Processor):
@final
def __init__(self, plugin: Plugin) -> None:
self.plugin: Final = plugin

View file

@ -1,14 +1,14 @@
from .async_bindable import AsyncBindable
from .base_bindable import BaseBindable, BindableSubscriber, ValueChanged
from .bindable import Bindable
from .chain import chain
from .combine import combine
from .concat import concat
from .cond import cond
from .const import const
from .filter import filter # noqa: W0622 # pylint: disable=W0622
from .map import map # noqa: W0622 # pylint: disable=W0622
from .merge import async_merge, merge
from .chains import async_chain, chain
from .combines import async_combine, combine
from .concats import concat
from .conds import cond
from .consts import const
from .filters import filter # noqa: W0622 # pylint: disable=W0622
from .maps import map # noqa: W0622 # pylint: disable=W0622
from .merges import async_merge, merge
__all__ = [
"Bindable",
@ -16,6 +16,9 @@ __all__ = [
"BaseBindable",
"BindableSubscriber",
"ValueChanged",
"async_chain",
"async_combine",
"async_merge",
"chain",
"combine",
"concat",
@ -24,5 +27,4 @@ __all__ = [
"filter",
"map",
"merge",
"async_merge",
]

View file

@ -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.
"""
@ -21,6 +23,9 @@ class AsyncBindable[T: Any](BaseBindable[T]):
async def set(self, value: T) -> None:
"""Set the value."""
if not (self.has_default_value or self.has_value):
self.set_silent(value)
return
e = ValueChanged(self.value, value)
self.set_silent(value)
for subscriber in self.__subscribers:
@ -53,25 +58,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 +90,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

View file

@ -1,16 +1,21 @@
from typing import Any, NamedTuple, Protocol
from typing import Generic, Protocol, TypeVar
from typing_extensions import NamedTuple
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
@ -22,7 +27,25 @@ class BaseBindable[T: Any]:
@property
def value(self) -> T:
return self.__value
if self.has_value:
return self.__value
return self.default_value
@property
def has_value(self) -> bool:
try:
_ = self.__value
return True
except AttributeError:
return False
@property
def has_default_value(self) -> bool:
try:
_ = self.__default_value
return True
except AttributeError:
return False
def set_silent(self, value: T) -> None:
self.__value = value

View file

@ -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.
"""
@ -19,6 +21,9 @@ class Bindable[T: Any](BaseBindable[T]):
def set(self, value: T) -> None:
"""Set the value."""
if not (self.has_default_value or self.has_value):
self.set_silent(value)
return
e = ValueChanged(self.value, value)
self.set_silent(value)
for subscriber in self.__subscribers:
@ -36,9 +41,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:

View file

@ -1,53 +0,0 @@
from typing import Any, Callable, overload
@overload
def chain[**P, A](func1: Callable[P, A], /) -> Callable[P, A]: ...
@overload
def chain[**P, A, B](
func1: Callable[P, A], func2: Callable[[A], B], /
) -> Callable[P, B]: ...
@overload
def chain[**P, A, B, C](
func1: Callable[P, A], func2: Callable[[A], B], func3: Callable[[B], C], /
) -> Callable[P, C]: ...
@overload
def chain[**P, A, B, C, D](
func1: Callable[P, A],
func2: Callable[[A], B],
func3: Callable[[B], C],
func4: Callable[[C], D],
/,
) -> Callable[P, D]: ...
@overload
def chain[**P, A, B, C, D, E](
func1: Callable[P, A],
func2: Callable[[A], B],
func3: Callable[[B], C],
func4: Callable[[C], D],
func5: Callable[[D], E],
/,
) -> Callable[P, E]: ...
@overload
def chain[**P](
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 inner(*args: P.args, **kwargs: P.kwargs) -> Any:
v = func1(*args, **kwargs)
for f in funcs:
v = f(v)
return v
return inner

View file

@ -0,0 +1,98 @@
from typing import Any, Awaitable, 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(func1: Callable[P, A], /) -> Callable[P, A]: ...
@overload
def chain(func1: Callable[P, A], func2: Callable[[A], B], /) -> Callable[P, B]: ...
@overload
def chain(
func1: Callable[P, A], func2: Callable[[A], B], func3: Callable[[B], C], /
) -> Callable[P, C]: ...
@overload
def chain(
func1: Callable[P, A],
func2: Callable[[A], B],
func3: Callable[[B], C],
func4: Callable[[C], D],
/,
) -> Callable[P, D]: ...
@overload
def chain(
func1: Callable[P, A],
func2: Callable[[A], B],
func3: Callable[[B], C],
func4: Callable[[C], D],
func5: Callable[[D], E],
/,
) -> Callable[P, E]: ...
@overload
def chain(
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)
for f in funcs:
v = f(v)
return v
return inner
@overload
def async_chain(func1: Callable[P, Awaitable[A]], /) -> Callable[P, Awaitable[A]]: ...
@overload
def async_chain(
func1: Callable[P, Awaitable[A]], func2: Callable[[A], Awaitable[B]], /
) -> Callable[P, Awaitable[B]]: ...
@overload
def async_chain(
func1: Callable[P, Awaitable[A]],
func2: Callable[[A], Awaitable[B]],
func3: Callable[[B], Awaitable[C]],
/,
) -> Callable[P, Awaitable[C]]: ...
@overload
def async_chain(
func1: Callable[P, Awaitable[A]],
func2: Callable[[A], Awaitable[B]],
func3: Callable[[B], Awaitable[C]],
func4: Callable[[C], Awaitable[D]],
/,
) -> Callable[P, Awaitable[D]]: ...
@overload
def async_chain(
func1: Callable[P, Awaitable[A]],
func2: Callable[[A], Awaitable[B]],
func3: Callable[[B], Awaitable[C]],
func4: Callable[[C], Awaitable[D]],
func5: Callable[[D], Awaitable[E]],
/,
) -> Callable[P, Awaitable[E]]: ...
@overload
def async_chain(
func1: Callable[P, Any], /, *funcs: Callable[[Any], Awaitable[Any]]
) -> Callable[P, Awaitable[Any]]: ...
def async_chain(
func1: Callable[P, Awaitable[Any]],
/,
*funcs: Callable[[Any], Awaitable[Any]],
) -> Callable[P, Awaitable[Any]]:
async def inner(*args: P.args, **kwargs: P.kwargs) -> Any:
v = await func1(*args, **kwargs)
for f in funcs:
v = await f(v)
return v
return inner

View file

@ -1,94 +0,0 @@
import operator
from typing import Any, Callable, overload
from snakia.utils import to_async
from .async_bindable import AsyncBindable
from .base_bindable import ValueChanged
from .bindable import Bindable
from .concat import concat
@overload
def combine[A, R](
source1: Bindable[A] | AsyncBindable[A],
/,
*,
combiner: Callable[[A], R],
) -> Bindable[R]: ...
@overload
def combine[A, B, R](
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
/,
*,
combiner: Callable[[A, B], R],
) -> Bindable[R]: ...
@overload
def combine[A, B, C, R](
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
source3: Bindable[C] | AsyncBindable[C],
/,
*,
combiner: Callable[[A, B, C], R],
) -> Bindable[R]: ...
@overload
def combine[A, B, C, D, R](
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
source3: Bindable[C] | AsyncBindable[C],
source4: Bindable[D] | AsyncBindable[D],
/,
*,
combiner: Callable[[A, B, C, D], R],
) -> Bindable[R]: ...
@overload
def combine[A, B, C, D, R](
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
source3: Bindable[C] | AsyncBindable[C],
source4: Bindable[D] | AsyncBindable[D],
/,
*,
combiner: Callable[[A, B, C, D], R],
) -> Bindable[R]: ...
@overload
def combine[R](
*sources: Bindable[Any] | AsyncBindable[Any],
combiner: Callable[..., R],
) -> Bindable[R]: ...
def combine[R](
*sources: Bindable[Any] | AsyncBindable[Any],
combiner: Callable[..., R],
) -> Bindable[R]:
combined = Bindable[R]()
values = [*map(lambda s: s.value, sources)]
for i, source in enumerate(sources):
def make_subscriber(
index: int,
) -> Callable[[ValueChanged[Any]], None]:
return concat(
lambda v: operator.setitem(values, index, v.new_value),
lambda _: combiner(*values),
)
if isinstance(source, Bindable):
source.subscribe(make_subscriber(i))
else:
source.subscribe(to_async(make_subscriber(i)))
return combined

View file

@ -0,0 +1,193 @@
from typing import Any, Awaitable, Callable, TypeVar, overload
from snakia.types import Unset
from snakia.utils import caller, to_async
from .async_bindable import AsyncBindable
from .base_bindable import ValueChanged
from .bindable import Bindable
A = TypeVar("A")
B = TypeVar("B")
C = TypeVar("C")
D = TypeVar("D")
E = TypeVar("E")
R = TypeVar("R")
@overload
def combine(
source1: Bindable[A] | AsyncBindable[A],
/,
*,
combiner: Callable[[A], R],
default_value: R | Unset = Unset(),
) -> Bindable[R]: ...
@overload
def combine(
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
/,
*,
combiner: Callable[[A, B], R],
default_value: R | Unset = Unset(),
) -> Bindable[R]: ...
@overload
def combine(
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
source3: Bindable[C] | AsyncBindable[C],
/,
*,
combiner: Callable[[A, B, C], R],
default_value: R | Unset = Unset(),
) -> Bindable[R]: ...
@overload
def combine(
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
source3: Bindable[C] | AsyncBindable[C],
source4: Bindable[D] | AsyncBindable[D],
/,
*,
combiner: Callable[[A, B, C, D], R],
default_value: R | Unset = Unset(),
) -> Bindable[R]: ...
@overload
def combine(
source1: Bindable[A] | AsyncBindable[A],
source2: Bindable[B] | AsyncBindable[B],
source3: Bindable[C] | AsyncBindable[C],
source4: Bindable[D] | AsyncBindable[D],
/,
*,
combiner: Callable[[A, B, C, D], R],
default_value: R | Unset = Unset(),
) -> Bindable[R]: ...
@overload
def combine(
*sources: Bindable[Any] | AsyncBindable[Any],
combiner: Callable[..., R],
default_value: R | Unset = Unset(),
) -> Bindable[R]: ...
def combine(
*sources: Bindable[Any] | AsyncBindable[Any],
combiner: Callable[..., R],
default_value: R | Unset = Unset(),
) -> Bindable[R]:
combined = Bindable[R]()
Unset.map(
default_value,
combined.set_silent,
caller(combined.set_silent, sources[0].default_value),
)
def subscriber(_: ValueChanged[Any]) -> None:
combined.set(combiner(*map(lambda s: s.value, sources)))
for source in sources:
if isinstance(source, Bindable):
source.subscribe(subscriber)
else:
source.subscribe(to_async(subscriber))
return combined
@overload
def async_combine(
source1: AsyncBindable[A],
/,
*,
combiner: Callable[[A], Awaitable[R]],
default_value: R | Unset = Unset(),
) -> AsyncBindable[R]: ...
@overload
def async_combine(
source1: AsyncBindable[A],
source2: AsyncBindable[B],
/,
*,
combiner: Callable[[A, B], Awaitable[R]],
default_value: R | Unset = Unset(),
) -> AsyncBindable[R]: ...
@overload
def async_combine(
source1: AsyncBindable[A],
source2: AsyncBindable[B],
source3: AsyncBindable[C],
/,
*,
combiner: Callable[[A, B, C], Awaitable[R]],
default_value: R | Unset = Unset(),
) -> AsyncBindable[R]: ...
@overload
def async_combine(
source1: AsyncBindable[A],
source2: AsyncBindable[B],
source3: AsyncBindable[C],
source4: AsyncBindable[D],
/,
*,
combiner: Callable[[A, B, C, D], Awaitable[R]],
default_value: R | Unset = Unset(),
) -> AsyncBindable[R]: ...
@overload
def async_combine(
source1: AsyncBindable[A],
source2: AsyncBindable[B],
source3: AsyncBindable[C],
source4: AsyncBindable[D],
/,
*,
combiner: Callable[[A, B, C, D], Awaitable[R]],
default_value: R | Unset = Unset(),
) -> AsyncBindable[R]: ...
@overload
def async_combine(
*sources: AsyncBindable[Any],
combiner: Callable[..., Awaitable[R]],
default_value: R | Unset = Unset(),
) -> AsyncBindable[R]: ...
def async_combine(
*sources: AsyncBindable[Any],
combiner: Callable[..., Awaitable[R]],
default_value: R | Unset = Unset(),
) -> AsyncBindable[R]:
combined = AsyncBindable[R]()
Unset.map(
default_value,
combined.set_silent,
caller(combined.set_silent, sources[0].default_value),
)
async def subscriber(_: ValueChanged[Any]) -> None:
result = await combiner(*map(lambda s: s.value, sources))
await combined.set(result)
for source in sources:
source.subscribe(subscriber)
return combined

View file

@ -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)

View file

@ -1,13 +0,0 @@
from typing import Callable
def cond[**P, T, F](
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)
)

View file

@ -0,0 +1,15 @@
from typing import Callable, ParamSpec, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
F = TypeVar("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)
)

View file

@ -1,5 +0,0 @@
from typing import Callable
def const[T](value: T) -> Callable[[], T]:
return lambda: value

View file

@ -0,0 +1,7 @@
from typing import Callable, TypeVar
T = TypeVar("T")
def const(value: T) -> Callable[[], T]:
return lambda: value

View file

@ -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)

View file

@ -1,9 +0,0 @@
import builtins
from typing import Any, Callable, Iterable
# noqa: W0622 # pylint: disable=W0622
def map[T: Any, U](
func: Callable[[T], U], /
) -> Callable[[Iterable[T]], Iterable[U]]:
return lambda iterable: builtins.map(func, iterable)

View file

@ -0,0 +1,10 @@
import builtins
from typing import Callable, Iterable, TypeVar
T = TypeVar("T")
U = TypeVar("U")
# noqa: W0622 # pylint: disable=W0622
def map(func: Callable[[T], U], /) -> Callable[[Iterable[T]], Iterable[U]]:
return lambda iterable: builtins.map(func, iterable)

View file

@ -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]()

View file

@ -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:

View file

@ -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

View file

@ -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)

View file

@ -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())

View file

@ -2,17 +2,21 @@ from .inject_after import after_hook, inject_after
from .inject_before import before_hook, inject_before
from .inject_const import inject_const
from .inject_replace import inject_replace, replace_hook
from .meta_decorators import hook_decorator, inject_decorator, replace_decorator
from .pass_exceptions import pass_exceptions
from .singleton import singleton
__all__ = [
"inject_replace",
"replace_hook",
"inject_after",
"after_hook",
"inject_before",
"before_hook",
"inject_after",
"inject_before",
"inject_const",
"inject_decorator",
"inject_replace",
"hook_decorator",
"pass_exceptions",
"replace_decorator",
"replace_hook",
"singleton",
]

View file

@ -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]:

View file

@ -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]]:

View file

@ -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):

View file

@ -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]:

View file

@ -0,0 +1,72 @@
import functools
from typing import Callable, Concatenate, ParamSpec, TypeVar
T = TypeVar("T")
R = TypeVar("R")
D = ParamSpec("D")
P = ParamSpec("P")
def inject_decorator(
decorator: Callable[Concatenate[Callable[P, T], D], None],
) -> Callable[D, Callable[[Callable[P, T]], Callable[P, T]]]:
@functools.wraps(decorator)
def wrapper(
*d_args: D.args, **d_kwargs: D.kwargs
) -> Callable[[Callable[P, T]], Callable[P, T]]:
def inner(obj: Callable[P, T]) -> Callable[P, T]:
@functools.wraps(obj)
def func(*args: P.args, **kwargs: P.kwargs) -> T:
decorator(obj, *d_args, **d_kwargs)
return obj(*args, **kwargs)
return func
return inner
return wrapper
def hook_decorator(
decorator: Callable[Concatenate[Callable[P, T], T, D], T],
) -> Callable[D, Callable[[Callable[P, T]], Callable[P, T]]]:
@functools.wraps(decorator)
def wrapper(
*d_args: D.args, **d_kwargs: D.kwargs
) -> Callable[[Callable[P, T]], Callable[P, T]]:
def inner(obj: Callable[P, T]) -> Callable[P, T]:
@functools.wraps(obj)
def func(*args: P.args, **kwargs: P.kwargs) -> T:
val = obj(*args, **kwargs)
return decorator(obj, val, *d_args, **d_kwargs)
return func
return inner
return wrapper
def replace_decorator(
decorator: Callable[Concatenate[T, D], T],
) -> Callable[D, Callable[[T], T]]:
@functools.wraps(decorator)
def wrapper(*d_args: D.args, **d_kwargs: D.kwargs) -> Callable[[T], T]:
def inner(obj: T) -> T:
result = decorator(obj, *d_args, **d_kwargs)
if not callable(obj):
return result
for attr in functools.WRAPPER_ASSIGNMENTS:
try:
value = getattr(obj, attr)
except AttributeError:
pass
else:
setattr(result, attr, value)
return result
return inner
return wrapper

View file

@ -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]]: ...

View file

@ -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()

View file

@ -3,6 +3,8 @@ from .bool import BoolField
from .field import Field
from .float import FloatField
from .int import IntField
from .list import ListField
from .optional import OptionalField
from .str import StrField
__all__ = [
@ -11,5 +13,7 @@ __all__ = [
"BoolField",
"FloatField",
"IntField",
"ListField",
"OptionalField",
"StrField",
]

View file

@ -1,25 +1,52 @@
import pickle
from typing import Final, override
from typing import Callable, Final, Generic, TypeVar, overload
from typing_extensions import Self
from snakia.types import Unset
from .field import Field
T = TypeVar("T")
class AutoField[T](Field[T]):
class AutoField(Field[T], Generic[T]):
__slots__ = ("__target_type",)
@overload
def __init__(
self, default_value: T, *, target_type: type[T] | None = None
) -> None:
super().__init__(default_value)
self.__target_type: Final = target_type
self, default_value: T, *, target_type: type[T] | Unset = Unset()
) -> None: ...
@overload
def __init__(
self,
*,
default_factory: Callable[[Self], T],
target_type: type[T] | Unset = Unset(),
) -> None: ...
def __init__(
self,
default_value: T | Unset = Unset(),
*,
default_factory: Callable[[Self], T] | Unset = Unset(),
target_type: type[T] | Unset = Unset(),
) -> None:
if not Unset.itis(default_factory):
super().__init__(default_factory=Unset.unwrap(default_factory))
elif not Unset.itis(default_value):
super().__init__(Unset.unwrap(default_value))
else:
super().__init__()
self.__target_type: Final[type] = Unset.unwrap_or(target_type, object)
@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):
return self.default_value
if not isinstance(value, self.__target_type):
return self._get_default()
return value # type: ignore

View file

@ -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"

View file

@ -1,17 +1,23 @@
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,
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]):
def __init__(self, default_value: T) -> None:
self.default_value: Final[T] = default_value
super().__init__(default_value)
class Field(ABC, PrivProperty[T], Generic[T]):
@abstractmethod
def serialize(self, value: T, /) -> bytes:
"""Serialize a value
@ -34,7 +40,7 @@ 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],

View file

@ -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

View file

@ -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")

38
src/snakia/field/list.py Normal file
View file

@ -0,0 +1,38 @@
from typing import Callable, Final, Iterable, TypeVar
from typing_extensions import Self
from .field import Field
T = TypeVar("T")
class ListField(Field[list[T]]):
def __init__(
self,
field: Field[T],
*,
length_size: int = 1,
default_factory: Callable[[Self], Iterable[T]] = lambda _: (),
) -> None:
self.length_size: Final[int] = length_size
self.field: Final = field
super().__init__(default_factory=lambda s: [*default_factory(s)])
def serialize(self, items: list[T], /) -> bytes:
result = b""
for item in items:
value = self.field.serialize(item)
length_prefix = len(value).to_bytes(self.length_size, "big")
result += length_prefix + value
return result
def deserialize(self, serialized: bytes, /) -> list[T]:
result = []
while serialized:
length = int.from_bytes(serialized[: self.length_size], "big")
serialized = serialized[self.length_size :]
item = self.field.deserialize(serialized[:length])
serialized = serialized[length:]
result.append(item)
return result

View file

@ -0,0 +1,28 @@
from typing import Final, TypeVar
from .field import Field
T = TypeVar("T")
class OptionalField(Field[T | None]):
def __init__(
self,
field: Field[T],
*,
none_value: bytes = b"",
) -> None:
super().__init__(None)
self.none_value: Final = none_value
self.field: Final = field
def serialize(self, value: T | None, /) -> bytes:
if value is None:
return self.none_value
return self.field.serialize(value)
def deserialize(self, serialized: bytes, /) -> T | None:
if serialized == self.none_value:
return None
return self.field.deserialize(serialized)

View file

@ -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)

View file

@ -4,6 +4,8 @@ from .bool import BoolField as bool
from .field import Field as field
from .float import FloatField as float
from .int import IntField as int
from .list import ListField as list
from .optional import OptionalField as optional
from .str import StrField as str
__all__ = [
@ -12,5 +14,7 @@ __all__ = [
"field",
"float",
"int",
"list",
"optional",
"str",
]

View file

@ -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)

View file

@ -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

View file

@ -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.
"""

View file

@ -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)

View file

@ -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.
"""

View file

@ -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:

View file

@ -1,20 +1,44 @@
from typing import Any
from typing import Any, Callable, Final, Generic, TypeVar, overload
from typing_extensions import Self
from snakia.types import Unset
T = TypeVar("T")
class PrivProperty[T]:
__slots__ = "__name", "__default_value"
class PrivProperty(Generic[T]):
__slots__ = "__name", "__default_value", "__default_factory"
__name: str
def __init__(self, default_value: T | None = None) -> None:
self.__default_value: T | None = default_value
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, default_value: T) -> None: ...
@overload
def __init__(self, *, default_factory: Callable[[Self], T]) -> None: ...
def __init__(
self,
default_value: T | Unset = Unset(),
default_factory: Callable[[Self], T] | Unset = Unset(),
) -> None:
self.__default_value: Final[T | Unset] = default_value
self.__default_factory: Final[Callable[[Self], T] | Unset] = default_factory
def _get_default(self: Self) -> T:
return Unset.map(
self.__default_factory,
lambda f: f(self),
lambda _: Unset.unwrap(self.__default_value),
)
def __set_name__(self, owner: type, name: str) -> None:
self.__name = f"_{owner.__name__}__{name}"
def __get__(self, instance: Any, owner: type | None = None, /) -> T:
if self.__default_value:
return getattr(instance, self.__name, self.__default_value)
if not hasattr(instance, self.__name):
setattr(instance, self.__name, self._get_default())
return getattr(instance, self.__name) # type: ignore
def __set__(self, instance: Any, value: T, /) -> None:

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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]):

View file

@ -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)

View file

@ -2,12 +2,16 @@
from . import empty
from .color import Color
from .marker import Marker, mark, marker
from .unique import Unique, UniqueType, unique
from .unset import Unset
from .version import Version
__all__ = [
"Color",
"Marker",
"mark",
"marker",
"Version",
"UniqueType",
"Unique",

View file

@ -0,0 +1,96 @@
from typing import Any, Callable, Literal, ParamSpec, TypeVar, final, overload
from typing_extensions import Self
from snakia.utils import get_attrs, get_or_set_attr
T = TypeVar("T")
M = TypeVar("M", bound="Marker")
P = ParamSpec("P")
MARKERS_ATTR = "__snakia_markers__"
def _get_all_markers(obj: Any) -> dict[type["Marker"], "Marker"]:
return get_or_set_attr(obj, MARKERS_ATTR, dict())
class Marker:
@overload
@classmethod
def get(cls, obj: Any, default: None = None) -> Self: ...
@overload
@classmethod
def get(cls, obj: Any, default: T) -> Self | T: ...
@final
@classmethod
def get(cls, obj: Any, default: Any = None) -> Any:
markers = _get_all_markers(obj)
return markers.get(cls, default)
@final
@classmethod
def has(cls, obj: Any) -> bool:
if not hasattr(obj, MARKERS_ATTR):
return False
_marker = obj.__dict__[MARKERS_ATTR].get(cls, None)
return isinstance(_marker, cls)
@final
def set_mark(self, obj: T) -> T:
markers = _get_all_markers(obj)
markers[self.__class__] = self
return obj
@final
@classmethod
def mark(cls, *a: Any, **kw: Any) -> Callable[[T], T]:
def inner(obj: T) -> T:
return cls(*a, **kw).set_mark(obj)
return inner
@final
@classmethod
def unmark(cls, obj: T) -> T:
markers = _get_all_markers(obj)
if cls in markers:
del markers[cls]
return obj
@overload
@classmethod
def get_marks(
cls, container: Any, *, only_values: Literal[False] = False
) -> dict[str, tuple[Any, Self]]: ...
@overload
@classmethod
def get_marks(
cls, container: Any, *, only_values: Literal[True]
) -> dict[str, Self]: ...
@final
@classmethod
def get_marks(
cls, container: Any, *, only_values: bool = False
) -> dict[str, tuple[Any, Self]] | dict[str, Self]:
markers = {}
for k, v in get_attrs(container).items():
if not cls.has(v):
continue
_marker = cls.get(v)
if _marker is not None:
markers[k] = _marker if only_values else (v, _marker)
return markers # type: ignore
def marker() -> type[Marker]:
return type("<anonym marker>", (Marker,), {})
def mark(
func: Callable[P, Marker], /, *args: P.args, **kwargs: P.kwargs
) -> Callable[[T], T]:
def inner(obj: T) -> T:
return func(*args, **kwargs).set_mark(obj)
return inner

View file

@ -1,4 +1,8 @@
from typing import Any, final
from typing import Any, Callable, TypeGuard, TypeVar, final
T = TypeVar("T")
V = TypeVar("V")
R = TypeVar("R")
@final
@ -31,9 +35,49 @@ 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
def __hash__(cls) -> int:
return id(cls)
def itis(cls: type[T], value: Any) -> TypeGuard[T]:
return value is cls or isinstance(value, cls)
def unwrap(cls: type[T], value: V | type[T] | T, /) -> V:
if value is cls or isinstance(value, cls):
raise TypeError(f"{cls} not unwrapped")
return value # type: ignore
def unwrap_or(cls: type[T], value: V | type[T] | T, default: R, /) -> V | R:
if value is cls or isinstance(value, cls):
return default
return value # type: ignore
def map(
cls: type[T],
value: V | type[T] | T,
or_else: Callable[[V], R],
and_then: Callable[[type[T]], R],
) -> R:
if value is cls or isinstance(value, cls):
return and_then(cls)
return or_else(value) # type: ignore
def or_else(
cls: type[T], value: V | type[T] | T, func: Callable[[V], R]
) -> type[T] | R:
if value is cls or isinstance(value, cls):
return cls
return func(value) # type: ignore
def and_then(
cls: type[T], value: V | type[T] | T, func: Callable[[type[T]], R]
) -> R | V:
if value is cls or isinstance(value, cls):
return func(cls)
return value # type: ignore
class Unique(metaclass=UniqueType): # noqa: R0903 # pylint: disable=R0903
"""

View file

@ -1,15 +1,26 @@
from .frame import frame
from .attrs import get_attrs, get_or_set_attr
from .exceptions import catch, throw
from .frames import frame
from .funcs import call, caller, ret, side, side_func
from .gil import GIL_ENABLED, nolock
from .inherit import inherit
from .nolock import nolock
from .this import this
from .throw import throw
from .to_async import to_async
__all__ = [
"call",
"caller",
"get_or_set_attr",
"get_attrs",
"GIL_ENABLED",
"frame",
"inherit",
"nolock",
"ret",
"side",
"side_func",
"this",
"throw",
"catch",
"to_async",
]

28
src/snakia/utils/attrs.py Normal file
View file

@ -0,0 +1,28 @@
from typing import Any, TypeVar
T = TypeVar("T")
def get_or_set_attr(obj: Any, name: str, default: T) -> T:
if not hasattr(obj, name):
setattr(obj, name, default)
attr = getattr(obj, name)
if not isinstance(attr, type(default)):
setattr(obj, name, default)
return default
return attr
def get_attrs(
obj: Any, *, use_dir: bool = False, of_class: bool = False
) -> dict[str, Any]:
if of_class and not isinstance(obj, type):
obj = obj.__class__
if not use_dir:
if hasattr(obj, "__dict__"):
return obj.__dict__ # type: ignore
if hasattr(obj, "__slots__"):
return {k: getattr(obj, k) for k in obj.__slots__}
raise NotImplementedError("Unknown layout")
else:
return {k: getattr(obj, k) for k in dir(obj)}

View file

@ -0,0 +1,62 @@
import contextlib
from typing import Any, Callable, NoReturn, TypeVar, overload
from exceptiongroup import ExceptionGroup
from snakia.types.unset import Unset
E = TypeVar("E", bound=Exception)
T = TypeVar("T")
D = TypeVar("D")
@overload
def throw(
*exceptions: E, # pyright: ignore[reportInvalidTypeVarUse]
from_: Unset | BaseException = Unset(),
) -> NoReturn: ...
@overload
def throw(
exception: BaseException, from_: Unset | BaseException = Unset(), /
) -> NoReturn: ...
def throw(*exceptions: Any, from_: Unset | BaseException = Unset()) -> NoReturn:
"""Throw an exception."""
if isinstance(from_, Unset):
if len(exceptions) == 1:
raise exceptions[0]
raise ExceptionGroup("", exceptions)
if len(exceptions) == 1:
raise exceptions[0] from from_
raise ExceptionGroup("", exceptions) from from_
contextlib.suppress()
@overload
def catch(
func: Callable[[], T],
*exceptions: type[Exception] | type[BaseException],
default: None = None,
) -> T | None: ...
@overload
def catch(
func: Callable[[], T],
*exceptions: type[Exception] | type[BaseException],
default: D,
) -> T | D: ...
def catch(
func: Callable[[], T],
*exceptions: type[Exception] | type[BaseException],
default: Any = None,
) -> T | Any:
try:
return func()
except BaseException as e:
if any(isinstance(e, exc) for exc in exceptions):
return default
raise

24
src/snakia/utils/funcs.py Normal file
View file

@ -0,0 +1,24 @@
from typing import Any, Callable, ParamSpec, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
def call(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
return f(*args, **kwargs)
def caller(f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> Callable[..., T]:
return lambda *_, **__: f(*args, **kwargs)
def side(value: T, *_: Any, **__: Any) -> T:
return value
def side_func(value: T, *_: Any, **__: Any) -> Callable[..., T]:
return lambda *_, **__: value
def ret() -> Callable[[T], T]:
return lambda x: x

View file

@ -4,6 +4,9 @@ if TYPE_CHECKING:
GIL_ENABLED: Final[bool] = bool(...)
"""
Whether the GIL is enabled."""
def nolock() -> None: ...
else:
import sys
@ -12,3 +15,14 @@ else:
GIL_ENABLED = sys._is_gil_enabled()
else:
GIL_ENABLED = True
if GIL_ENABLED:
import time
def nolock() -> None:
time.sleep(0.001)
else:
def nolock() -> None:
pass

View file

@ -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.

View file

@ -1,20 +0,0 @@
from typing import TYPE_CHECKING
from .gil_enabled import GIL_ENABLED
if TYPE_CHECKING:
def nolock() -> None: ...
else:
if GIL_ENABLED:
import time
def nolock() -> None:
time.sleep(0.001)
else:
def nolock() -> None:
pass

View file

@ -2,7 +2,7 @@ import gc
from types import FunctionType, MethodType
from typing import Any
from .frame import frame
from .frames import frame
def this() -> Any:

View file

@ -1,29 +0,0 @@
from typing import Any, NoReturn, overload
from snakia.types.unset import Unset
@overload
def throw[T: Exception](
*exceptions: T, # pyright: ignore[reportInvalidTypeVarUse]
from_: Unset | BaseException = Unset(),
) -> NoReturn: ...
@overload
def throw(
exception: BaseException, from_: Unset | BaseException = Unset(), /
) -> NoReturn: ...
def throw(
*exceptions: Any, from_: Unset | BaseException = Unset()
) -> NoReturn:
"""Throw an exception."""
if isinstance(from_, Unset):
if len(exceptions) == 1:
raise exceptions[0]
raise ExceptionGroup("", exceptions)
if len(exceptions) == 1:
raise exceptions[0] from from_
raise ExceptionGroup("", exceptions) from from_

View file

@ -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:

360
uv.lock generated
View file

@ -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"
@ -12,17 +16,193 @@ wheels = [
]
[[package]]
name = "networkx"
version = "3.5"
name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
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" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
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" },
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
]
[[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.6"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.11'",
]
sdist = { url = "https://files.pythonhosted.org/packages/e8/fc/7b6fd4d22c8c4dc5704430140d8b3f520531d4fe7328b8f8d03f5a7950e8/networkx-3.6.tar.gz", hash = "sha256:285276002ad1f7f7da0f7b42f004bcba70d381e936559166363707fdad3d72ad", size = 2511464, upload-time = "2025-11-24T03:03:47.158Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/c7/d64168da60332c17d24c0d2f08bdf3987e8d1ae9d84b5bbd0eec2eb26a55/networkx-3.6-py3-none-any.whl", hash = "sha256:cdb395b105806062473d3be36458d8f1459a4e4b98e236a66c3a48996e07684f", size = 2063713, upload-time = "2025-11-24T03:03:45.21Z" },
]
[[package]]
name = "numpy"
version = "2.2.6"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.11'",
]
sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
{ url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
{ url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
{ url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
{ url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
{ url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
{ url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
{ url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
{ url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
{ url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
{ url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
{ url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
{ url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
{ url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
{ url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
{ url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
{ url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
{ url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
{ url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
{ url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
{ url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
{ url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
{ url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
{ url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
{ url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
{ url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
{ url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
{ url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
{ url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
{ url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
{ url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
{ url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
{ url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
{ url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
{ url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
{ url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
{ url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
{ url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
{ url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
{ url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
{ url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
{ url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
{ url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
{ url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
{ url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
{ url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
{ url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
{ url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
{ url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
{ url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
{ url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
{ url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
{ url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
{ url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
]
[[package]]
name = "numpy"
version = "2.3.5"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.11'",
]
sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", size = 17034641, upload-time = "2025-11-16T22:49:19.336Z" },
{ url = "https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", size = 12528324, upload-time = "2025-11-16T22:49:22.582Z" },
{ url = "https://files.pythonhosted.org/packages/4d/1a/e85f0eea4cf03d6a0228f5c0256b53f2df4bc794706e7df019fc622e47f1/numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", size = 5356872, upload-time = "2025-11-16T22:49:25.408Z" },
{ url = "https://files.pythonhosted.org/packages/5c/bb/35ef04afd567f4c989c2060cde39211e4ac5357155c1833bcd1166055c61/numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", size = 6893148, upload-time = "2025-11-16T22:49:27.549Z" },
{ url = "https://files.pythonhosted.org/packages/f2/2b/05bbeb06e2dff5eab512dfc678b1cc5ee94d8ac5956a0885c64b6b26252b/numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", size = 14557282, upload-time = "2025-11-16T22:49:30.964Z" },
{ url = "https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", size = 16897903, upload-time = "2025-11-16T22:49:34.191Z" },
{ url = "https://files.pythonhosted.org/packages/ac/14/085f4cf05fc3f1e8aa95e85404e984ffca9b2275a5dc2b1aae18a67538b8/numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", size = 16341672, upload-time = "2025-11-16T22:49:37.2Z" },
{ url = "https://files.pythonhosted.org/packages/6f/3b/1f73994904142b2aa290449b3bb99772477b5fd94d787093e4f24f5af763/numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", size = 18838896, upload-time = "2025-11-16T22:49:39.727Z" },
{ url = "https://files.pythonhosted.org/packages/cd/b9/cf6649b2124f288309ffc353070792caf42ad69047dcc60da85ee85fea58/numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", size = 6563608, upload-time = "2025-11-16T22:49:42.079Z" },
{ url = "https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", size = 13078442, upload-time = "2025-11-16T22:49:43.99Z" },
{ url = "https://files.pythonhosted.org/packages/6d/a7/f99a41553d2da82a20a2f22e93c94f928e4490bb447c9ff3c4ff230581d3/numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", size = 10458555, upload-time = "2025-11-16T22:49:47.092Z" },
{ url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" },
{ url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" },
{ url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" },
{ url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" },
{ url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" },
{ url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" },
{ url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" },
{ url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" },
{ url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" },
{ url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" },
{ url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" },
{ url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" },
{ url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" },
{ url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" },
{ url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" },
{ url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" },
{ url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" },
{ url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" },
{ url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" },
{ url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" },
{ url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" },
{ url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" },
{ url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" },
{ url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" },
{ url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" },
{ url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" },
{ url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" },
{ url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" },
{ url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" },
{ url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" },
{ url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" },
{ url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" },
{ url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" },
{ url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" },
{ url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" },
{ url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" },
{ url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" },
{ url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" },
{ url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" },
{ url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" },
{ url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" },
{ url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" },
{ url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" },
{ url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" },
{ url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" },
{ url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" },
{ url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" },
{ url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" },
{ url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" },
{ url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" },
{ url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" },
{ url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" },
{ url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" },
{ url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" },
{ url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
{ url = "https://files.pythonhosted.org/packages/c6/65/f9dea8e109371ade9c782b4e4756a82edf9d3366bca495d84d79859a0b79/numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", size = 16910689, upload-time = "2025-11-16T22:52:23.247Z" },
{ url = "https://files.pythonhosted.org/packages/00/4f/edb00032a8fb92ec0a679d3830368355da91a69cab6f3e9c21b64d0bb986/numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", size = 12457053, upload-time = "2025-11-16T22:52:26.367Z" },
{ url = "https://files.pythonhosted.org/packages/16/a4/e8a53b5abd500a63836a29ebe145fc1ab1f2eefe1cfe59276020373ae0aa/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", size = 5285635, upload-time = "2025-11-16T22:52:29.266Z" },
{ url = "https://files.pythonhosted.org/packages/a3/2f/37eeb9014d9c8b3e9c55bc599c68263ca44fdbc12a93e45a21d1d56df737/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", size = 6801770, upload-time = "2025-11-16T22:52:31.421Z" },
{ url = "https://files.pythonhosted.org/packages/7d/e4/68d2f474df2cb671b2b6c2986a02e520671295647dad82484cde80ca427b/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", size = 14391768, upload-time = "2025-11-16T22:52:33.593Z" },
{ url = "https://files.pythonhosted.org/packages/b8/50/94ccd8a2b141cb50651fddd4f6a48874acb3c91c8f0842b08a6afc4b0b21/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", size = 16729263, upload-time = "2025-11-16T22:52:36.369Z" },
{ url = "https://files.pythonhosted.org/packages/2d/ee/346fa473e666fe14c52fcdd19ec2424157290a032d4c41f98127bfb31ac7/numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", size = 12967213, upload-time = "2025-11-16T22:52:39.38Z" },
]
[[package]]
name = "pydantic"
version = "2.12.3"
version = "2.11.10"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
@ -30,91 +210,133 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f3/1e/4f0a3233767010308f2fd6bd0814597e3f63f1dc98304a9112b8759df4ff/pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74", size = 819383, upload-time = "2025-10-17T15:04:21.222Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ae/54/ecab642b3bed45f7d5f59b38443dcb36ef50f85af192e6ece103dbfe9587/pydantic-2.11.10.tar.gz", hash = "sha256:dc280f0982fbda6c38fada4e476dc0a4f3aeaf9c6ad4c28df68a666ec3c61423", size = 788494, upload-time = "2025-10-04T10:40:41.338Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/6b/83661fa77dcefa195ad5f8cd9af3d1a7450fd57cc883ad04d65446ac2029/pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf", size = 462431, upload-time = "2025-10-17T15:04:19.346Z" },
{ url = "https://files.pythonhosted.org/packages/bd/1f/73c53fcbfb0b5a78f91176df41945ca466e71e9d9d836e5c522abda39ee7/pydantic-2.11.10-py3-none-any.whl", hash = "sha256:802a655709d49bd004c31e865ef37da30b540786a46bfce02333e0e24b5fe29a", size = 444823, upload-time = "2025-10-04T10:40:39.055Z" },
]
[[package]]
name = "pydantic-core"
version = "2.41.4"
version = "2.33.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
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" }
sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" }
wheels = [
{ 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" },
{ url = "https://files.pythonhosted.org/packages/b2/a7/e5fc60a6f781fc634ecaa9ecc3c20171d238794cef69ae0af79ac11b89d7/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4", size = 2041590, upload-time = "2025-10-14T10:20:34.332Z" },
{ url = "https://files.pythonhosted.org/packages/70/69/dce747b1d21d59e85af433428978a1893c6f8a7068fa2bb4a927fba7a5ff/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f", size = 2219869, upload-time = "2025-10-14T10:20:35.965Z" },
{ url = "https://files.pythonhosted.org/packages/83/6a/c070e30e295403bf29c4df1cb781317b6a9bac7cd07b8d3acc94d501a63c/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b", size = 2345169, upload-time = "2025-10-14T10:20:37.627Z" },
{ url = "https://files.pythonhosted.org/packages/f0/83/06d001f8043c336baea7fd202a9ac7ad71f87e1c55d8112c50b745c40324/pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47", size = 2070165, upload-time = "2025-10-14T10:20:39.246Z" },
{ url = "https://files.pythonhosted.org/packages/14/0a/e567c2883588dd12bcbc110232d892cf385356f7c8a9910311ac997ab715/pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970", size = 2189067, upload-time = "2025-10-14T10:20:41.015Z" },
{ url = "https://files.pythonhosted.org/packages/f4/1d/3d9fca34273ba03c9b1c5289f7618bc4bd09c3ad2289b5420481aa051a99/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed", size = 2132997, upload-time = "2025-10-14T10:20:43.106Z" },
{ url = "https://files.pythonhosted.org/packages/52/70/d702ef7a6cd41a8afc61f3554922b3ed8d19dd54c3bd4bdbfe332e610827/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8", size = 2307187, upload-time = "2025-10-14T10:20:44.849Z" },
{ url = "https://files.pythonhosted.org/packages/68/4c/c06be6e27545d08b802127914156f38d10ca287a9e8489342793de8aae3c/pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431", size = 2305204, upload-time = "2025-10-14T10:20:46.781Z" },
{ url = "https://files.pythonhosted.org/packages/b0/e5/35ae4919bcd9f18603419e23c5eaf32750224a89d41a8df1a3704b69f77e/pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd", size = 1972536, upload-time = "2025-10-14T10:20:48.39Z" },
{ url = "https://files.pythonhosted.org/packages/1e/c2/49c5bb6d2a49eb2ee3647a93e3dae7080c6409a8a7558b075027644e879c/pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff", size = 2031132, upload-time = "2025-10-14T10:20:50.421Z" },
{ url = "https://files.pythonhosted.org/packages/06/23/936343dbcba6eec93f73e95eb346810fc732f71ba27967b287b66f7b7097/pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8", size = 1969483, upload-time = "2025-10-14T10:20:52.35Z" },
{ url = "https://files.pythonhosted.org/packages/13/d0/c20adabd181a029a970738dfe23710b52a31f1258f591874fcdec7359845/pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746", size = 2105688, upload-time = "2025-10-14T10:20:54.448Z" },
{ url = "https://files.pythonhosted.org/packages/00/b6/0ce5c03cec5ae94cca220dfecddc453c077d71363b98a4bbdb3c0b22c783/pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced", size = 1910807, upload-time = "2025-10-14T10:20:56.115Z" },
{ url = "https://files.pythonhosted.org/packages/68/3e/800d3d02c8beb0b5c069c870cbb83799d085debf43499c897bb4b4aaff0d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a", size = 1956669, upload-time = "2025-10-14T10:20:57.874Z" },
{ url = "https://files.pythonhosted.org/packages/60/a4/24271cc71a17f64589be49ab8bd0751f6a0a03046c690df60989f2f95c2c/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02", size = 2051629, upload-time = "2025-10-14T10:21:00.006Z" },
{ url = "https://files.pythonhosted.org/packages/68/de/45af3ca2f175d91b96bfb62e1f2d2f1f9f3b14a734afe0bfeff079f78181/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1", size = 2224049, upload-time = "2025-10-14T10:21:01.801Z" },
{ url = "https://files.pythonhosted.org/packages/af/8f/ae4e1ff84672bf869d0a77af24fd78387850e9497753c432875066b5d622/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2", size = 2342409, upload-time = "2025-10-14T10:21:03.556Z" },
{ url = "https://files.pythonhosted.org/packages/18/62/273dd70b0026a085c7b74b000394e1ef95719ea579c76ea2f0cc8893736d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84", size = 2069635, upload-time = "2025-10-14T10:21:05.385Z" },
{ url = "https://files.pythonhosted.org/packages/30/03/cf485fff699b4cdaea469bc481719d3e49f023241b4abb656f8d422189fc/pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d", size = 2194284, upload-time = "2025-10-14T10:21:07.122Z" },
{ url = "https://files.pythonhosted.org/packages/f9/7e/c8e713db32405dfd97211f2fc0a15d6bf8adb7640f3d18544c1f39526619/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d", size = 2137566, upload-time = "2025-10-14T10:21:08.981Z" },
{ url = "https://files.pythonhosted.org/packages/04/f7/db71fd4cdccc8b75990f79ccafbbd66757e19f6d5ee724a6252414483fb4/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2", size = 2316809, upload-time = "2025-10-14T10:21:10.805Z" },
{ url = "https://files.pythonhosted.org/packages/76/63/a54973ddb945f1bca56742b48b144d85c9fc22f819ddeb9f861c249d5464/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab", size = 2311119, upload-time = "2025-10-14T10:21:12.583Z" },
{ url = "https://files.pythonhosted.org/packages/f8/03/5d12891e93c19218af74843a27e32b94922195ded2386f7b55382f904d2f/pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c", size = 1981398, upload-time = "2025-10-14T10:21:14.584Z" },
{ url = "https://files.pythonhosted.org/packages/be/d8/fd0de71f39db91135b7a26996160de71c073d8635edfce8b3c3681be0d6d/pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4", size = 2030735, upload-time = "2025-10-14T10:21:16.432Z" },
{ url = "https://files.pythonhosted.org/packages/72/86/c99921c1cf6650023c08bfab6fe2d7057a5142628ef7ccfa9921f2dda1d5/pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564", size = 1973209, upload-time = "2025-10-14T10:21:18.213Z" },
{ url = "https://files.pythonhosted.org/packages/36/0d/b5706cacb70a8414396efdda3d72ae0542e050b591119e458e2490baf035/pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4", size = 1877324, upload-time = "2025-10-14T10:21:20.363Z" },
{ url = "https://files.pythonhosted.org/packages/de/2d/cba1fa02cfdea72dfb3a9babb067c83b9dff0bbcb198368e000a6b756ea7/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2", size = 1884515, upload-time = "2025-10-14T10:21:22.339Z" },
{ url = "https://files.pythonhosted.org/packages/07/ea/3df927c4384ed9b503c9cc2d076cf983b4f2adb0c754578dfb1245c51e46/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf", size = 2042819, upload-time = "2025-10-14T10:21:26.683Z" },
{ url = "https://files.pythonhosted.org/packages/6a/ee/df8e871f07074250270a3b1b82aad4cd0026b588acd5d7d3eb2fcb1471a3/pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2", size = 1995866, upload-time = "2025-10-14T10:21:28.951Z" },
{ url = "https://files.pythonhosted.org/packages/fc/de/b20f4ab954d6d399499c33ec4fafc46d9551e11dc1858fb7f5dca0748ceb/pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89", size = 1970034, upload-time = "2025-10-14T10:21:30.869Z" },
{ url = "https://files.pythonhosted.org/packages/54/28/d3325da57d413b9819365546eb9a6e8b7cbd9373d9380efd5f74326143e6/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1", size = 2102022, upload-time = "2025-10-14T10:21:32.809Z" },
{ url = "https://files.pythonhosted.org/packages/9e/24/b58a1bc0d834bf1acc4361e61233ee217169a42efbdc15a60296e13ce438/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac", size = 1905495, upload-time = "2025-10-14T10:21:34.812Z" },
{ url = "https://files.pythonhosted.org/packages/fb/a4/71f759cc41b7043e8ecdaab81b985a9b6cad7cec077e0b92cff8b71ecf6b/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554", size = 1956131, upload-time = "2025-10-14T10:21:36.924Z" },
{ url = "https://files.pythonhosted.org/packages/b0/64/1e79ac7aa51f1eec7c4cda8cbe456d5d09f05fdd68b32776d72168d54275/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e", size = 2052236, upload-time = "2025-10-14T10:21:38.927Z" },
{ url = "https://files.pythonhosted.org/packages/e9/e3/a3ffc363bd4287b80f1d43dc1c28ba64831f8dfc237d6fec8f2661138d48/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616", size = 2223573, upload-time = "2025-10-14T10:21:41.574Z" },
{ url = "https://files.pythonhosted.org/packages/28/27/78814089b4d2e684a9088ede3790763c64693c3d1408ddc0a248bc789126/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af", size = 2342467, upload-time = "2025-10-14T10:21:44.018Z" },
{ url = "https://files.pythonhosted.org/packages/92/97/4de0e2a1159cb85ad737e03306717637842c88c7fd6d97973172fb183149/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12", size = 2063754, upload-time = "2025-10-14T10:21:46.466Z" },
{ url = "https://files.pythonhosted.org/packages/0f/50/8cb90ce4b9efcf7ae78130afeb99fd1c86125ccdf9906ef64b9d42f37c25/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d", size = 2196754, upload-time = "2025-10-14T10:21:48.486Z" },
{ url = "https://files.pythonhosted.org/packages/34/3b/ccdc77af9cd5082723574a1cc1bcae7a6acacc829d7c0a06201f7886a109/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad", size = 2137115, upload-time = "2025-10-14T10:21:50.63Z" },
{ url = "https://files.pythonhosted.org/packages/ca/ba/e7c7a02651a8f7c52dc2cff2b64a30c313e3b57c7d93703cecea76c09b71/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a", size = 2317400, upload-time = "2025-10-14T10:21:52.959Z" },
{ url = "https://files.pythonhosted.org/packages/2c/ba/6c533a4ee8aec6b812c643c49bb3bd88d3f01e3cebe451bb85512d37f00f/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025", size = 2312070, upload-time = "2025-10-14T10:21:55.419Z" },
{ url = "https://files.pythonhosted.org/packages/22/ae/f10524fcc0ab8d7f96cf9a74c880243576fd3e72bd8ce4f81e43d22bcab7/pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e", size = 1982277, upload-time = "2025-10-14T10:21:57.474Z" },
{ url = "https://files.pythonhosted.org/packages/b4/dc/e5aa27aea1ad4638f0c3fb41132f7eb583bd7420ee63204e2d4333a3bbf9/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894", size = 2024608, upload-time = "2025-10-14T10:21:59.557Z" },
{ url = "https://files.pythonhosted.org/packages/3e/61/51d89cc2612bd147198e120a13f150afbf0bcb4615cddb049ab10b81b79e/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d", size = 1967614, upload-time = "2025-10-14T10:22:01.847Z" },
{ url = "https://files.pythonhosted.org/packages/0d/c2/472f2e31b95eff099961fa050c376ab7156a81da194f9edb9f710f68787b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da", size = 1876904, upload-time = "2025-10-14T10:22:04.062Z" },
{ url = "https://files.pythonhosted.org/packages/4a/07/ea8eeb91173807ecdae4f4a5f4b150a520085b35454350fc219ba79e66a3/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e", size = 1882538, upload-time = "2025-10-14T10:22:06.39Z" },
{ 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/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/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" },
{ url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" },
{ url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" },
{ url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" },
{ url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" },
{ url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" },
{ url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" },
{ url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" },
{ url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" },
{ url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" },
{ url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" },
{ url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" },
{ url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" },
{ url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" },
{ url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" },
{ url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" },
{ url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" },
{ url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" },
{ url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" },
{ url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" },
{ url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" },
{ url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" },
{ url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" },
{ url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" },
{ url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" },
{ url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" },
{ url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" },
{ url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" },
{ url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" },
{ url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" },
{ url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" },
{ url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" },
{ url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" },
{ url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" },
{ url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" },
{ url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" },
{ url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" },
{ url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" },
{ url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" },
{ url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" },
{ url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" },
{ url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" },
{ url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" },
{ url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" },
{ url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" },
{ url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" },
{ url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" },
{ url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" },
{ url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" },
{ url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" },
{ url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" },
{ url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" },
{ url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" },
{ url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" },
{ url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" },
{ url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" },
{ url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" },
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
{ url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" },
{ url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" },
{ url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" },
{ url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" },
{ url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" },
{ url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" },
{ url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" },
{ url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" },
{ url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" },
{ url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" },
{ url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" },
{ url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" },
{ url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" },
{ url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" },
{ url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" },
{ url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" },
{ url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" },
{ url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" },
]
[[package]]
name = "snakia"
version = "0.4.1"
version = "0.5.0"
source = { editable = "." }
dependencies = [
{ name = "networkx" },
{ name = "annotated-types" },
{ name = "exceptiongroup" },
{ name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "networkx", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "pydantic" },
{ name = "types-networkx" },
{ name = "typing-extensions" },
]
[package.metadata]
requires-dist = [
{ name = "annotated-types", specifier = ">=0.7.0" },
{ name = "exceptiongroup", specifier = ">=1.3.1" },
{ name = "networkx", specifier = ">=3.4.2" },
{ name = "pydantic", specifier = ">=2.12.3" },
{ name = "pydantic", specifier = ">=2.11.10" },
{ name = "types-networkx", specifier = ">=3.5.0.20251106" },
{ name = "typing-extensions", specifier = ">=4.15.0" },
]
[[package]]
name = "types-networkx"
version = "3.5.0.20251106"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/da/0d/0657390fb7e4fffb9e0a20d51b9b63451771c238ad7ca2eb31de12827a87/types_networkx-3.5.0.20251106.tar.gz", hash = "sha256:2f17a53a35cf6dbbb031039c19bf0f127ec1b37f3530c57a517a0dd67a42e343", size = 72307, upload-time = "2025-11-06T03:06:52.263Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/01/f768e7d3662841a5163f261510148a89564ffc4320568dfb98b79f8f6e68/types_networkx-3.5.0.20251106-py3-none-any.whl", hash = "sha256:674c53111d5ca6cf96eb6ccd3152d0d52c2720b8ddea8082c7b0116a21347a30", size = 160776, upload-time = "2025-11-06T03:06:50.89Z" },
]
[[package]]