This commit is contained in:
2026-01-10 16:52:32 -06:00
parent b547d69d48
commit 4216e833e5
10 changed files with 90 additions and 133 deletions

View File

@@ -6,10 +6,9 @@ readme = "README.md"
authors = [{ name = "Christian Camper", email = "ccamper7@gmail.com" }] authors = [{ name = "Christian Camper", email = "ccamper7@gmail.com" }]
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = [ dependencies = [
"basedpyright>=1.37.0", "basedpyright>=1.37.1",
"pyyaml>=6.0.3", "pyyaml>=6.0.3",
"ruff>=0.14.10", "ruff>=0.14.11",
"ty>=0.0.10",
] ]
[project.scripts] [project.scripts]
@@ -19,6 +18,6 @@ docker_compose = "docker_compose:main"
requires = ["uv_build>=0.9.17,<0.10.0"] requires = ["uv_build>=0.9.17,<0.10.0"]
build-backend = "uv_build" build-backend = "uv_build"
[tool.pyright] [tools.pyright]
analysis.diagnosticMode = "workspace" analysis.diagnosticMode = 'workspace'
#reportExplicitAny = false

View File

@@ -1,3 +0,0 @@
{
"basedpyright.analysis.diagnosticMode": "workspace"
}

View File

@@ -1,8 +1,10 @@
from collections.abc import Iterator from collections.abc import Iterator
from typing import cast
from docker_compose.cfg import CFG_ROOT, TRAEFIK_PATH from docker_compose.cfg import CFG_ROOT, TRAEFIK_PATH
from docker_compose.compose.net_yaml import NetArgsYaml from docker_compose.compose.net_yaml import NetArgsYaml
from docker_compose.compose.rendered import Rendered from docker_compose.compose.rendered import Rendered
from docker_compose.util.Ts import TypeYamlCompatableDict
from docker_compose.util.yaml_util import to_yaml from docker_compose.util.yaml_util import to_yaml
@@ -29,6 +31,7 @@ if __name__ == "__main__":
data["networks"] = {net: NetArgsYaml(name=f"{net}_proxy") for net in nets} data["networks"] = {net: NetArgsYaml(name=f"{net}_proxy") for net in nets}
cfg = traefik.cfg cfg = traefik.cfg
data["services"]["traefik"]["networks"] = nets data["services"]["traefik"]["networks"] = nets
data = cast(TypeYamlCompatableDict, cast(object, data))
template = cfg.pre_render(to_yaml(data)) template = cfg.pre_render(to_yaml(data))
cfg.src_paths.compose_file.write(template) cfg.src_paths.compose_file.write(template)
cfg.dest_paths.compose_file.write(cfg.render(template)) cfg.dest_paths.compose_file.write(cfg.render(template))

View File

@@ -9,7 +9,7 @@ from docker_compose.cfg.org import App, Org
from docker_compose.cfg.replace import ReplaceDynamic, ReplaceStatic, ReplaceUnique from docker_compose.cfg.replace import ReplaceDynamic, ReplaceStatic, ReplaceUnique
from docker_compose.compose.services_yaml import ServiceYamlRead from docker_compose.compose.services_yaml import ServiceYamlRead
from docker_compose.compose.volume_yaml import VolYaml from docker_compose.compose.volume_yaml import VolYaml
from docker_compose.util.Ts import T_YamlRW from docker_compose.util.Ts import TypeYamlDict
from docker_compose.util.yaml_util import path_to_typed, read_yaml from docker_compose.util.yaml_util import path_to_typed, read_yaml
@@ -46,12 +46,12 @@ class ServicePath:
data_str = f.read() data_str = f.read()
for func in self.pre_render_funcs: for func in self.pre_render_funcs:
data_str = func(data_str) data_str = func(data_str)
data_dict: T_YamlRW = yaml.safe_load(data_str) # pyright: ignore[reportAny] data_dict: TypeYamlDict = yaml.safe_load(data_str) # pyright: ignore[reportAny]
if not isinstance(data_dict, MutableMapping): if not isinstance(data_dict, MutableMapping):
raise TypeError raise TypeError
path_to_typed(ServiceYamlRead, data_dict, self.path) path_to_typed(ServiceYamlRead, data_dict, self.path)
return cast(ServiceYamlRead, cast(object, data_dict)) return cast(ServiceYamlRead, cast(object, data_dict))
@property @property
def pre_render_funcs(self) -> Iterator[Callable[[str], str]]: def pre_render_funcs(self) -> Iterator[Callable[[str], str]]:
yield self.fqdn yield self.fqdn

View File

@@ -5,14 +5,14 @@ from typing import Self, cast, final
from docker_compose.cfg.compose_paths import ServicePath, VolumePath from docker_compose.cfg.compose_paths import ServicePath, VolumePath
from docker_compose.cfg.org_yaml import OrgYaml from docker_compose.cfg.org_yaml import OrgYaml
from docker_compose.util.Ts import T_YamlDict, T_YamlRW from docker_compose.util.Ts import TypeYamlCompatableDict, TypeYamlCompatableRes
from docker_compose.util.yaml_util import read_yaml, write_yaml from docker_compose.util.yaml_util import read_yaml, write_yaml
YAML_EXTS = frozenset((".yml", ".yaml")) YAML_EXTS = frozenset((".yml", ".yaml"))
class ComposeFileTemplate(Path): class ComposeFileTemplate(Path):
def write_dict(self, data: T_YamlDict) -> None: def write_dict(self, data: TypeYamlCompatableDict) -> None:
write_yaml(data, self) write_yaml(data, self)
def write(self, data: str) -> None: def write(self, data: str) -> None:
@@ -68,7 +68,7 @@ class VolumesDir(YamlDir):
class VolumeData(Path): class VolumeData(Path):
def write(self, data: T_YamlRW) -> None: def write(self, data: TypeYamlCompatableRes) -> None:
write_yaml(data, self) write_yaml(data, self)

View File

@@ -1,7 +1,7 @@
from collections.abc import Iterator from collections.abc import Iterator
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Self from typing import Self, cast
from docker_compose.cfg.cfg_paths import CfgData from docker_compose.cfg.cfg_paths import CfgData
from docker_compose.cfg.org import OrgData from docker_compose.cfg.org import OrgData
@@ -10,6 +10,7 @@ from docker_compose.compose.compose_yaml import ComposeYaml
from docker_compose.compose.net import Net from docker_compose.compose.net import Net
from docker_compose.compose.services import Service from docker_compose.compose.services import Service
from docker_compose.compose.volume_yaml import VolYaml from docker_compose.compose.volume_yaml import VolYaml
from docker_compose.util.Ts import TypeYamlCompatableDict
from docker_compose.util.yaml_util import to_yaml from docker_compose.util.yaml_util import to_yaml
@@ -53,17 +54,18 @@ class Compose:
@property @property
def as_dict(self) -> ComposeYaml: def as_dict(self) -> ComposeYaml:
return ComposeYaml( return ComposeYaml(
name=str(OrgData.org_app), name=str(OrgData.org_app.dest),
services={ services={
service.service_name: service.as_dict for service in self.services service.service_name: service.as_dict for service in self.services
}, },
networks=self.networks.as_dict, networks=self.networks.as_dict,
volumes=self.volumes, volumes=self.volumes,
) )
@property @property
def as_template(self) -> str: def as_template(self) -> str:
return self.cfg.pre_render(to_yaml(self.as_dict)) data = cast(TypeYamlCompatableDict, cast(object, self.as_dict))
return self.cfg.pre_render(to_yaml(data))
def write_template(self): def write_template(self):
self.cfg.src_paths.compose_file.write(self.as_template) self.cfg.src_paths.compose_file.write(self.as_template)

View File

@@ -1,3 +1,3 @@
from docker_compose.util.Ts import T_YamlDict from docker_compose.util.Ts import TypeYamlDict
type VolYaml = dict[str, T_YamlDict] type VolYaml = dict[str, TypeYamlDict]

View File

@@ -1,4 +1,4 @@
from collections.abc import ItemsView, Iterator, KeysView, MutableMapping, Set from collections.abc import ItemsView, Iterator, KeysView, MutableMapping, Sequence, Set
from types import GenericAlias, UnionType from types import GenericAlias, UnionType
from typing import ( from typing import (
ClassVar, ClassVar,
@@ -9,61 +9,44 @@ from typing import (
get_origin, get_origin,
) )
#generic nested data
type T_Primitive = None | bool | int | str type T_Primitive = None | bool | int | str
type _PrimIters = Sequence[TypePrim] | Set[TypePrim] | Iterator[TypePrim]
type TypePrimDict = MutableMapping[T_Primitive, TypePrim]
type TypePrim = T_Primitive | _PrimIters | TypePrimDict
# type T_TDict = MutableMapping[T_Primitive, T_Prim] # type T_TDict = MutableMapping[T_Primitive, T_Prim]
type T_TIters = list[T_Prim]
type T_T = T_Primitive | T_TIters | TypedYamlDict
# data going to and from yaml
class TypedYamlDict(Protocol): type TypeYaml = T_Primitive | TypeYamlRes
def __getitem__(self, key: str, /) -> T_T: ... type TypeYamlRes = list[TypeYaml] | TypeYamlDict
class TypeYamlDict(Protocol):
def __getitem__(self, key: str, /) -> object: ...
# def __setitem__(self, key: str, value: V, /) -> V: ... # def __setitem__(self, key: str, value: V, /) -> V: ...
# def __delitem__(self, key: Never | K, /) -> None: ... # def __delitem__(self, key: Never | K, /) -> None: ...
def __contains__(self, key: str, /) -> bool: ... def __contains__(self, key: str, /) -> bool: ...
def __iter__(self) -> Iterator[str]: ... def __iter__(self) -> Iterator[str]: ...
def __len__(self) -> int: ... def __len__(self) -> int: ...
def keys(self) -> KeysView[str]: ... def keys(self) -> KeysView[str]: ...
def items(self) -> ItemsView[str, T_T]: ... def items(self) -> ItemsView[str, object]: ...
# def pop(self, key: Never | K, /) -> V: ... # def pop(self, key: Never | K, /) -> V: ...
# def popitem(self) -> tuple[K, V]: ... # def popitem(self) -> tuple[K, V]: ...
# def clear(self) -> None: ... # def clear(self) -> None: ...
__required_keys__: ClassVar[frozenset[str]] __required_keys__: ClassVar[frozenset[str]]
__optional_keys__: ClassVar[frozenset[str]] __optional_keys__: ClassVar[frozenset[str]]
# class Test(TypedDict): #yaml compatible data
# var: str type TypeYamlCompatableIters = Sequence[TypeYamlCompatable] | Set[TypeYamlCompatable] | Iterator[TypeYamlCompatable]
# type TypeYamlCompatableDict = MutableMapping[str, TypeYamlCompatable]
# type TypeYamlCompatableRes = TypeYamlCompatableIters | TypeYamlCompatableDict
# x = Test(var="test") type TypeYamlCompatable = T_Primitive | TypeYamlCompatableRes
#
#
# def is_typed_dict_test(obj: TypedYamlDict[object, object]) -> None:
# print(obj)
# pass
#
#
# is_typed_dict_test(x)
type T_PrimIters = tuple[T_Prim, ...] | list[T_Prim] | Set[T_Prim] | Iterator[T_Prim] # type T_YamlPostDict = MutableMapping[str, T_YamlPost]
type T_PrimDict = MutableMapping[T_Primitive, T_Prim] # type T_YamlPostRes = Sequence[T_YamlPost] | T_YamlPostDict
type T_Prim = T_Primitive | T_PrimIters | T_PrimDict # type T_YamlPost = T_Primitive | T_YamlPostRes
type T_YamlIters = tuple[T_Yaml, ...] | list[T_Yaml] | Set[T_Yaml] | Iterator[T_Yaml]
type T_YamlDict = MutableMapping[str, T_Yaml]
type T_YamlRW = T_YamlIters | T_YamlDict
type T_Yaml = T_Primitive | T_YamlRW
type T_YamlPostDict = MutableMapping[str, T_YamlPost]
type T_YamlPostRes = tuple[T_YamlPost, ...] | T_YamlPostDict
type T_YamlPost = T_Primitive | T_YamlPostRes
def get_union_types(annotations: UnionType) -> Iterator[type]: def get_union_types(annotations: UnionType) -> Iterator[type]:

View File

@@ -1,5 +1,5 @@
import re import re
from collections.abc import Iterator, MutableMapping, Set from collections.abc import Iterator, MutableMapping, Sequence, Set
from pathlib import Path from pathlib import Path
from typing import ( from typing import (
cast, cast,
@@ -11,13 +11,12 @@ from typing import (
import yaml import yaml
from docker_compose.util.Ts import ( from docker_compose.util.Ts import (
T_YamlDict, TypeYamlCompatable,
T_YamlIters, TypeYamlCompatableDict,
T_YamlPost, TypeYamlCompatableIters,
T_YamlPostDict, TypeYamlCompatableRes,
T_YamlPostRes, TypeYamlDict,
T_YamlRW, TypeYamlRes,
TypedYamlDict,
get_types, get_types,
) )
@@ -46,10 +45,10 @@ class VerboseSafeDumper(yaml.SafeDumper):
return True return True
def yaml_prep(data: T_YamlRW) -> T_YamlPostRes: def yaml_prep(data: TypeYamlCompatableRes) -> TypeYamlCompatableRes:
if isinstance(data, MutableMapping): if isinstance(data, MutableMapping):
return dict_prep(data) return dict_prep(data)
if isinstance(data, (tuple, list)): if isinstance(data, Sequence):
return tuple(list_prep(data)) return tuple(list_prep(data))
res = tuple(list_prep(data)) res = tuple(list_prep(data))
try: try:
@@ -58,7 +57,7 @@ def yaml_prep(data: T_YamlRW) -> T_YamlPostRes:
return res return res
def list_prep(data: T_YamlIters) -> Iterator[T_YamlPost]: def list_prep(data: TypeYamlCompatableIters) -> Iterator[TypeYamlCompatable]:
for v in data: for v in data:
if isinstance(v, (MutableMapping, tuple, list, Set, Iterator)): if isinstance(v, (MutableMapping, tuple, list, Set, Iterator)):
yield yaml_prep(v) yield yaml_prep(v)
@@ -71,22 +70,22 @@ def list_prep(data: T_YamlIters) -> Iterator[T_YamlPost]:
continue continue
def dict_prep(data: T_YamlDict) -> T_YamlPostDict: def dict_prep(data: TypeYamlCompatableDict) -> TypeYamlCompatableDict:
keys = tuple(data.keys()) keys = tuple(data.keys())
for k in keys: for k in keys:
v = data[k] v = data[k]
if isinstance(v, (MutableMapping, tuple, list, Set, Iterator)): if isinstance(v, (MutableMapping, tuple, list, Set, Iterator)):
data[k] = v = yaml_prep(v) # pyright: ignore[reportArgumentType] data[k] = v = yaml_prep(v)
if v: if v:
continue continue
if isinstance(v, bool): if isinstance(v, bool):
continue continue
del data[k] del data[k]
return cast(T_YamlPostDict, cast(object, data)) return data
def to_yaml(data: T_YamlRW) -> str: def to_yaml(data: TypeYamlCompatableRes) -> str:
dict_ = yaml_prep(data) dict_ = yaml_prep(data)
res = yaml.dump(dict_, Dumper=VerboseSafeDumper) res = yaml.dump(dict_, Dumper=VerboseSafeDumper)
res = re.sub(r"(^\s?-)", r" \g<1>", res, flags=re.MULTILINE) res = re.sub(r"(^\s?-)", r" \g<1>", res, flags=re.MULTILINE)
@@ -94,31 +93,31 @@ def to_yaml(data: T_YamlRW) -> str:
def write_yaml( def write_yaml(
data: T_YamlRW, data: TypeYamlCompatableRes,
path: Path, path: Path,
) -> None: ) -> None:
with path.open("wt") as f: with path.open("wt") as f:
_ = f.write(to_yaml(data)) _ = f.write(to_yaml(data))
def read_yaml(path: Path) -> T_YamlPostRes: def read_yaml(path: Path) -> TypeYamlRes:
with path.open("rt") as f: with path.open("rt") as f:
return yaml.safe_load(f) # pyright: ignore[reportAny] return yaml.safe_load(f) # pyright: ignore[reportAny]
def read_typed_yaml[T: TypedYamlDict]( def read_typed_yaml[T: TypeYamlDict](
type_: type[T], type_: type[T],
path: Path, path: Path,
) -> T: ) -> T:
with path.open("rt") as f: with path.open("rt") as f:
data: T_YamlDict = yaml.safe_load(f) # pyright: ignore[reportAny] data: TypeYamlDict = yaml.safe_load(f) # pyright: ignore[reportAny]
path_to_typed(type_, data, path) path_to_typed(type_, data, path)
return cast(T, data) # pyright: ignore[reportInvalidCast] return cast(T, data)
def path_to_typed( def path_to_typed(
type_: type[TypedYamlDict], type_: type[TypeYamlDict],
data: T_YamlDict, data: TypeYamlDict,
path: Path, path: Path,
) -> None: ) -> None:
try: try:
@@ -129,8 +128,8 @@ def path_to_typed(
def validate_typed_dict( def validate_typed_dict(
t: type[TypedYamlDict], t: type[TypeYamlDict],
data: T_YamlDict, data: TypeYamlDict,
) -> None: ) -> None:
keys = frozenset(data.keys()) keys = frozenset(data.keys())
missing = t.__required_keys__.difference(keys) missing = t.__required_keys__.difference(keys)
@@ -143,7 +142,7 @@ def validate_typed_dict(
for key, val in data.items(): for key, val in data.items():
t2 = cast(type, cast(object, hints[key])) t2 = cast(type, cast(object, hints[key]))
if is_typeddict(t2): if is_typeddict(t2):
validate_typed_dict(t2, cast(T_YamlDict, val)) validate_typed_dict(t2, cast(TypeYamlDict, val))
continue continue
# try: # try:

76
uv.lock generated
View File

@@ -4,14 +4,14 @@ requires-python = ">=3.13"
[[package]] [[package]]
name = "basedpyright" name = "basedpyright"
version = "1.37.0" version = "1.37.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "nodejs-wheel-binaries" }, { name = "nodejs-wheel-binaries" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/60/d7/9476af6f45a70e8d23045ec59d99c2698513b7395283cadc75caeeea2b83/basedpyright-1.37.0.tar.gz", hash = "sha256:affbffced97a04a08bfc44aef2da43951a5ab5e2e55921a144ed786c4fd2c6ad", size = 22837441, upload-time = "2026-01-04T09:59:32.652Z" } sdist = { url = "https://files.pythonhosted.org/packages/0c/b0/fbba81ea29eed1274e965cd0445f0d6020b467ff4d3393791e4d6ae02e64/basedpyright-1.37.1.tar.gz", hash = "sha256:1f47bc6f45cbcc5d6f8619d60aa42128e4b38942f5118dcd4bc20c3466c5e02f", size = 25235384, upload-time = "2026-01-08T14:42:46.447Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/e8/63/753918f0bad07a1b24755a540b64bca1388322615025d4c954e3740fcdbe/basedpyright-1.37.0-py3-none-any.whl", hash = "sha256:261a02a8732a19f3f585e2940582147560058626a062a2320724de84fb2dc41b", size = 11884509, upload-time = "2026-01-04T09:59:35.997Z" }, { url = "https://files.pythonhosted.org/packages/ad/d6/6b33bb49f08d761d7c958a1e3cecfb3ffbdcf4ba6bbed65b23ab47516b75/basedpyright-1.37.1-py3-none-any.whl", hash = "sha256:caf3adfe54f51623241712f8b4367adb51ef8a8c2288e3e1ec4118319661340d", size = 12297397, upload-time = "2026-01-08T14:42:50.306Z" },
] ]
[[package]] [[package]]
@@ -22,15 +22,13 @@ dependencies = [
{ name = "basedpyright" }, { name = "basedpyright" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "ruff" }, { name = "ruff" },
{ name = "ty" },
] ]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "basedpyright", specifier = ">=1.37.0" }, { name = "basedpyright", specifier = ">=1.37.1" },
{ name = "pyyaml", specifier = ">=6.0.3" }, { name = "pyyaml", specifier = ">=6.0.3" },
{ name = "ruff", specifier = ">=0.14.10" }, { name = "ruff", specifier = ">=0.14.11" },
{ name = "ty", specifier = ">=0.0.10" },
] ]
[[package]] [[package]]
@@ -87,50 +85,26 @@ wheels = [
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.14.10" version = "0.14.11"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" } sdist = { url = "https://files.pythonhosted.org/packages/d4/77/9a7fe084d268f8855d493e5031ea03fa0af8cc05887f638bf1c4e3363eb8/ruff-0.14.11.tar.gz", hash = "sha256:f6dc463bfa5c07a59b1ff2c3b9767373e541346ea105503b4c0369c520a66958", size = 5993417, upload-time = "2026-01-08T19:11:58.322Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" }, { url = "https://files.pythonhosted.org/packages/f0/a6/a4c40a5aaa7e331f245d2dc1ac8ece306681f52b636b40ef87c88b9f7afd/ruff-0.14.11-py3-none-linux_armv6l.whl", hash = "sha256:f6ff2d95cbd335841a7217bdfd9c1d2e44eac2c584197ab1385579d55ff8830e", size = 12951208, upload-time = "2026-01-08T19:12:09.218Z" },
{ url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" }, { url = "https://files.pythonhosted.org/packages/5c/5c/360a35cb7204b328b685d3129c08aca24765ff92b5a7efedbdd6c150d555/ruff-0.14.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f6eb5c1c8033680f4172ea9c8d3706c156223010b8b97b05e82c59bdc774ee6", size = 13330075, upload-time = "2026-01-08T19:12:02.549Z" },
{ url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" }, { url = "https://files.pythonhosted.org/packages/1b/9e/0cc2f1be7a7d33cae541824cf3f95b4ff40d03557b575912b5b70273c9ec/ruff-0.14.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2fc34cc896f90080fca01259f96c566f74069a04b25b6205d55379d12a6855e", size = 12257809, upload-time = "2026-01-08T19:12:00.366Z" },
{ url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" }, { url = "https://files.pythonhosted.org/packages/a7/e5/5faab97c15bb75228d9f74637e775d26ac703cc2b4898564c01ab3637c02/ruff-0.14.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53386375001773ae812b43205d6064dae49ff0968774e6befe16a994fc233caa", size = 12678447, upload-time = "2026-01-08T19:12:13.899Z" },
{ url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" }, { url = "https://files.pythonhosted.org/packages/1b/33/e9767f60a2bef779fb5855cab0af76c488e0ce90f7bb7b8a45c8a2ba4178/ruff-0.14.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a697737dce1ca97a0a55b5ff0434ee7205943d4874d638fe3ae66166ff46edbe", size = 12758560, upload-time = "2026-01-08T19:11:42.55Z" },
{ url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" }, { url = "https://files.pythonhosted.org/packages/eb/84/4c6cf627a21462bb5102f7be2a320b084228ff26e105510cd2255ea868e5/ruff-0.14.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6845ca1da8ab81ab1dce755a32ad13f1db72e7fba27c486d5d90d65e04d17b8f", size = 13599296, upload-time = "2026-01-08T19:11:30.371Z" },
{ url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" }, { url = "https://files.pythonhosted.org/packages/88/e1/92b5ed7ea66d849f6157e695dc23d5d6d982bd6aa8d077895652c38a7cae/ruff-0.14.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e36ce2fd31b54065ec6f76cb08d60159e1b32bdf08507862e32f47e6dde8bcbf", size = 15048981, upload-time = "2026-01-08T19:12:04.742Z" },
{ url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" }, { url = "https://files.pythonhosted.org/packages/61/df/c1bd30992615ac17c2fb64b8a7376ca22c04a70555b5d05b8f717163cf9f/ruff-0.14.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590bcc0e2097ecf74e62a5c10a6b71f008ad82eb97b0a0079e85defe19fe74d9", size = 14633183, upload-time = "2026-01-08T19:11:40.069Z" },
{ url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" }, { url = "https://files.pythonhosted.org/packages/04/e9/fe552902f25013dd28a5428a42347d9ad20c4b534834a325a28305747d64/ruff-0.14.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53fe71125fc158210d57fe4da26e622c9c294022988d08d9347ec1cf782adafe", size = 14050453, upload-time = "2026-01-08T19:11:37.555Z" },
{ url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" }, { url = "https://files.pythonhosted.org/packages/ae/93/f36d89fa021543187f98991609ce6e47e24f35f008dfe1af01379d248a41/ruff-0.14.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a35c9da08562f1598ded8470fcfef2afb5cf881996e6c0a502ceb61f4bc9c8a3", size = 13757889, upload-time = "2026-01-08T19:12:07.094Z" },
{ url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" }, { url = "https://files.pythonhosted.org/packages/b7/9f/c7fb6ecf554f28709a6a1f2a7f74750d400979e8cd47ed29feeaa1bd4db8/ruff-0.14.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0f3727189a52179393ecf92ec7057c2210203e6af2676f08d92140d3e1ee72c1", size = 13955832, upload-time = "2026-01-08T19:11:55.064Z" },
{ url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" }, { url = "https://files.pythonhosted.org/packages/db/a0/153315310f250f76900a98278cf878c64dfb6d044e184491dd3289796734/ruff-0.14.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eb09f849bd37147a789b85995ff734a6c4a095bed5fd1608c4f56afc3634cde2", size = 12586522, upload-time = "2026-01-08T19:11:35.356Z" },
{ url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" }, { url = "https://files.pythonhosted.org/packages/2f/2b/a73a2b6e6d2df1d74bf2b78098be1572191e54bec0e59e29382d13c3adc5/ruff-0.14.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c61782543c1231bf71041461c1f28c64b961d457d0f238ac388e2ab173d7ecb7", size = 12724637, upload-time = "2026-01-08T19:11:47.796Z" },
{ url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" }, { url = "https://files.pythonhosted.org/packages/f0/41/09100590320394401cd3c48fc718a8ba71c7ddb1ffd07e0ad6576b3a3df2/ruff-0.14.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82ff352ea68fb6766140381748e1f67f83c39860b6446966cff48a315c3e2491", size = 13145837, upload-time = "2026-01-08T19:11:32.87Z" },
{ url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" }, { url = "https://files.pythonhosted.org/packages/3b/d8/e035db859d1d3edf909381eb8ff3e89a672d6572e9454093538fe6f164b0/ruff-0.14.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:728e56879df4ca5b62a9dde2dd0eb0edda2a55160c0ea28c4025f18c03f86984", size = 13850469, upload-time = "2026-01-08T19:12:11.694Z" },
{ url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" }, { url = "https://files.pythonhosted.org/packages/4e/02/bb3ff8b6e6d02ce9e3740f4c17dfbbfb55f34c789c139e9cd91985f356c7/ruff-0.14.11-py3-none-win32.whl", hash = "sha256:337c5dd11f16ee52ae217757d9b82a26400be7efac883e9e852646f1557ed841", size = 12851094, upload-time = "2026-01-08T19:11:45.163Z" },
{ url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" }, { url = "https://files.pythonhosted.org/packages/58/f1/90ddc533918d3a2ad628bc3044cdfc094949e6d4b929220c3f0eb8a1c998/ruff-0.14.11-py3-none-win_amd64.whl", hash = "sha256:f981cea63d08456b2c070e64b79cb62f951aa1305282974d4d5216e6e0178ae6", size = 14001379, upload-time = "2026-01-08T19:11:52.591Z" },
{ url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" }, { url = "https://files.pythonhosted.org/packages/c4/1c/1dbe51782c0e1e9cfce1d1004752672d2d4629ea46945d19d731ad772b3b/ruff-0.14.11-py3-none-win_arm64.whl", hash = "sha256:649fb6c9edd7f751db276ef42df1f3df41c38d67d199570ae2a7bd6cbc3590f0", size = 12938644, upload-time = "2026-01-08T19:11:50.027Z" },
]
[[package]]
name = "ty"
version = "0.0.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b7/85/97b5276baa217e05db2fe3d5c61e4dfd35d1d3d0ec95bfca1986820114e0/ty-0.0.10.tar.gz", hash = "sha256:0a1f9f7577e56cd508a8f93d0be2a502fdf33de6a7d65a328a4c80b784f4ac5f", size = 4892892, upload-time = "2026-01-07T23:00:23.572Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/7a/5a7147ce5231c3ccc55d6f945dabd7412e233e755d28093bfdec988ba595/ty-0.0.10-py3-none-linux_armv6l.whl", hash = "sha256:406a8ea4e648551f885629b75dc3f070427de6ed099af45e52051d4c68224829", size = 9835881, upload-time = "2026-01-07T22:08:17.492Z" },
{ url = "https://files.pythonhosted.org/packages/3e/7d/89f4d2277c938332d047237b47b11b82a330dbff4fff0de8574cba992128/ty-0.0.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d6e0a733e3d6d3bce56d6766bc61923e8b130241088dc2c05e3c549487190096", size = 9696404, upload-time = "2026-01-07T22:08:37.965Z" },
{ url = "https://files.pythonhosted.org/packages/e8/cd/9dd49e6d40e54d4b7d563f9e2a432c4ec002c0673a81266e269c4bc194ce/ty-0.0.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e4832f8879cb95fc725f7e7fcab4f22be0cf2550f3a50641d5f4409ee04176d4", size = 9181195, upload-time = "2026-01-07T22:59:07.187Z" },
{ url = "https://files.pythonhosted.org/packages/d2/b8/3e7c556654ba0569ed5207138d318faf8633d87e194760fc030543817c26/ty-0.0.10-py3-none-manylinux_2_24_aarch64.whl", hash = "sha256:6b58cc78e5865bc908f053559a80bb77cab0dc168aaad2e88f2b47955694b138", size = 9665002, upload-time = "2026-01-07T22:08:30.782Z" },
{ url = "https://files.pythonhosted.org/packages/98/96/410a483321406c932c4e3aa1581d1072b72cdcde3ae83cd0664a65c7b254/ty-0.0.10-py3-none-manylinux_2_24_armv7l.whl", hash = "sha256:83c6a514bb86f05005fa93e3b173ae3fde94d291d994bed6fe1f1d2e5c7331cf", size = 9664948, upload-time = "2026-01-07T23:04:14.655Z" },
{ url = "https://files.pythonhosted.org/packages/1f/5d/cba2ab3e2f660763a72ad12620d0739db012e047eaa0ceaa252bf5e94ebb/ty-0.0.10-py3-none-manylinux_2_24_i686.whl", hash = "sha256:2e43f71e357f8a4f7fc75e4753b37beb2d0f297498055b1673a9306aa3e21897", size = 10125401, upload-time = "2026-01-07T22:08:28.171Z" },
{ url = "https://files.pythonhosted.org/packages/a7/67/29536e0d97f204a2933122239298e754db4564f4ed7f34e2153012b954be/ty-0.0.10-py3-none-manylinux_2_24_ppc64le.whl", hash = "sha256:18be3c679965c23944c8e574be0635504398c64c55f3f0c46259464e10c0a1c7", size = 10714052, upload-time = "2026-01-07T22:08:20.098Z" },
{ url = "https://files.pythonhosted.org/packages/63/c8/82ac83b79a71c940c5dcacb644f526f0c8fdf4b5e9664065ab7ee7c0e4ec/ty-0.0.10-py3-none-manylinux_2_24_s390x.whl", hash = "sha256:5477981681440a35acdf9b95c3097410c547abaa32b893f61553dbc3b0096fff", size = 10395924, upload-time = "2026-01-07T22:08:22.839Z" },
{ url = "https://files.pythonhosted.org/packages/9e/4c/2f9ac5edbd0e67bf82f5cd04275c4e87cbbf69a78f43e5dcf90c1573d44e/ty-0.0.10-py3-none-manylinux_2_24_x86_64.whl", hash = "sha256:e206a23bd887574302138b33383ae1edfcc39d33a06a12a5a00803b3f0287a45", size = 10220096, upload-time = "2026-01-07T22:08:13.171Z" },
{ url = "https://files.pythonhosted.org/packages/04/13/3be2b7bfd53b9952b39b6f2c2ef55edeb1a2fea3bf0285962736ee26731c/ty-0.0.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4e09ddb0d3396bd59f645b85eab20f9a72989aa8b736b34338dcb5ffecfe77b6", size = 9649120, upload-time = "2026-01-07T22:08:34.003Z" },
{ url = "https://files.pythonhosted.org/packages/93/e3/edd58547d9fd01e4e584cec9dced4f6f283506b422cdd953e946f6a8e9f0/ty-0.0.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:139d2a741579ad86a044233b5d7e189bb81f427eebce3464202f49c3ec0eba3b", size = 9686033, upload-time = "2026-01-07T22:08:40.967Z" },
{ url = "https://files.pythonhosted.org/packages/cc/bc/9d2f5fec925977446d577fb9b322d0e7b1b1758709f23a6cfc10231e9b84/ty-0.0.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6bae10420c0abfe4601fbbc6ce637b67d0b87a44fa520283131a26da98f2e74c", size = 9841905, upload-time = "2026-01-07T23:04:21.694Z" },
{ url = "https://files.pythonhosted.org/packages/7c/b8/5acd3492b6a4ef255ace24fcff0d4b1471a05b7f3758d8910a681543f899/ty-0.0.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7358bbc5d037b9c59c3a48895206058bcd583985316c4125a74dd87fd1767adb", size = 10320058, upload-time = "2026-01-07T22:08:25.645Z" },
{ url = "https://files.pythonhosted.org/packages/35/67/5b6906fccef654c7e801d6ac8dcbe0d493e1f04c38127f82a5e6d7e0aa0e/ty-0.0.10-py3-none-win32.whl", hash = "sha256:f51b6fd485bc695d0fdf555e69e6a87d1c50f14daef6cb980c9c941e12d6bcba", size = 9271806, upload-time = "2026-01-07T22:08:10.08Z" },
{ url = "https://files.pythonhosted.org/packages/42/36/82e66b9753a76964d26fd9bc3514ea0abce0a5ba5ad7d5f084070c6981da/ty-0.0.10-py3-none-win_amd64.whl", hash = "sha256:16deb77a72cf93b89b4d29577829613eda535fbe030513dfd9fba70fe38bc9f5", size = 10130520, upload-time = "2026-01-07T23:04:11.759Z" },
{ url = "https://files.pythonhosted.org/packages/63/52/89da123f370e80b587d2db8551ff31562c882d87b32b0e92b59504b709ae/ty-0.0.10-py3-none-win_arm64.whl", hash = "sha256:7495288bca7afba9a4488c9906466d648ffd3ccb6902bc3578a6dbd91a8f05f0", size = 9626026, upload-time = "2026-01-07T23:04:17.91Z" },
] ]