From 47907e07675fb01f4fc895ae7f7f0d29d78108fb Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:10:40 -0700 Subject: [PATCH] Fix ty 0.0.55 diagnostics and prefab-ui protocol version drift (#4428) --- fastmcp_slim/fastmcp/server/providers/base.py | 8 ++-- .../local_provider/local_provider.py | 8 ++-- .../server/providers/openapi/provider.py | 2 +- .../fastmcp/server/providers/proxy.py | 10 ++--- fastmcp_slim/fastmcp/server/server.py | 8 ++-- fastmcp_slim/fastmcp/utilities/json_schema.py | 4 +- fastmcp_slim/fastmcp/utilities/versions.py | 4 +- pyproject.toml | 2 +- tests/client/test_elicitation.py | 4 +- tests/client/test_elicitation_enums.py | 4 +- tests/client/test_streamable_http.py | 2 +- tests/test_apps_prefab.py | 14 +++---- uv.lock | 42 +++++++++---------- 13 files changed, 56 insertions(+), 56 deletions(-) diff --git a/fastmcp_slim/fastmcp/server/providers/base.py b/fastmcp_slim/fastmcp/server/providers/base.py index 580e2821d..402dff351 100644 --- a/fastmcp_slim/fastmcp/server/providers/base.py +++ b/fastmcp_slim/fastmcp/server/providers/base.py @@ -388,7 +388,7 @@ class Provider: matching = [t for t in matching if version.matches(t.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) async def _list_resources(self) -> Sequence[Resource]: """Return all available resources. @@ -419,7 +419,7 @@ class Provider: matching = [r for r in matching if version.matches(r.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) async def _list_resource_templates(self) -> Sequence[ResourceTemplate]: """Return all available resource templates. @@ -450,7 +450,7 @@ class Provider: matching = [t for t in matching if version.matches(t.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) async def _list_prompts(self) -> Sequence[Prompt]: """Return all available prompts. @@ -481,7 +481,7 @@ class Provider: matching = [p for p in matching if version.matches(p.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) # ------------------------------------------------------------------------- # Task registration diff --git a/fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py b/fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py index 675ff0e63..f8d790a76 100644 --- a/fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py +++ b/fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py @@ -366,7 +366,7 @@ class LocalProvider( matching = [t for t in matching if version.matches(t.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) async def _list_resources(self) -> Sequence[Resource]: """Return all resources.""" @@ -390,7 +390,7 @@ class LocalProvider( matching = [r for r in matching if version.matches(r.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) async def _list_resource_templates(self) -> Sequence[ResourceTemplate]: """Return all resource templates.""" @@ -416,7 +416,7 @@ class LocalProvider( matching = [t for t in matching if version.matches(t.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) async def _list_prompts(self) -> Sequence[Prompt]: """Return all prompts.""" @@ -440,7 +440,7 @@ class LocalProvider( matching = [p for p in matching if version.matches(p.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) # ========================================================================= # Task registration diff --git a/fastmcp_slim/fastmcp/server/providers/openapi/provider.py b/fastmcp_slim/fastmcp/server/providers/openapi/provider.py index dd9a73559..72e811d53 100644 --- a/fastmcp_slim/fastmcp/server/providers/openapi/provider.py +++ b/fastmcp_slim/fastmcp/server/providers/openapi/provider.py @@ -425,7 +425,7 @@ class OpenAPIProvider(Provider): matching = [t for t in matching if version.matches(t.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) async def _list_prompts(self) -> Sequence[Prompt]: """Return empty list - OpenAPI doesn't create prompts.""" diff --git a/fastmcp_slim/fastmcp/server/providers/proxy.py b/fastmcp_slim/fastmcp/server/providers/proxy.py index 1ff4db22e..3b3ccc265 100644 --- a/fastmcp_slim/fastmcp/server/providers/proxy.py +++ b/fastmcp_slim/fastmcp/server/providers/proxy.py @@ -656,7 +656,7 @@ class ProxyProvider(Provider): matching = [t for t in matching if version.matches(t.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) # ------------------------------------------------------------------------- # Resource methods @@ -693,7 +693,7 @@ class ProxyProvider(Provider): matching = [r for r in matching if version.matches(r.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) # ------------------------------------------------------------------------- # Resource template methods @@ -730,7 +730,7 @@ class ProxyProvider(Provider): matching = [t for t in matching if version.matches(t.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) # ------------------------------------------------------------------------- # Prompt methods @@ -767,7 +767,7 @@ class ProxyProvider(Provider): matching = [p for p in matching if version.matches(p.version)] if not matching: return None - return max(matching, key=version_sort_key) # type: ignore[type-var] # ty:ignore[invalid-return-type] + return max(matching, key=version_sort_key) # ------------------------------------------------------------------------- # Task methods @@ -1103,7 +1103,7 @@ class ProxyClient(Client[ClientTransportT]): kwargs["log_handler"] = default_proxy_log_handler if "progress_handler" not in kwargs: kwargs["progress_handler"] = default_proxy_progress_handler - super().__init__(transport=transport, **kwargs) + super().__init__(transport=transport, **kwargs) # ty: ignore[no-matching-overload] # Enable forwarding of inbound HTTP headers (e.g. authorization) to # the upstream server. This is only appropriate for proxy clients, diff --git a/fastmcp_slim/fastmcp/server/server.py b/fastmcp_slim/fastmcp/server/server.py index d1e456e7e..b9ed14d6d 100644 --- a/fastmcp_slim/fastmcp/server/server.py +++ b/fastmcp_slim/fastmcp/server/server.py @@ -780,7 +780,7 @@ class FastMCP( if not authorized: return None - return cast(Tool, max(authorized, key=version_sort_key)) + return max(authorized, key=version_sort_key) async def list_resources( self, *, run_middleware: bool = True @@ -915,7 +915,7 @@ class FastMCP( if not authorized: return None - return cast(Resource, max(authorized, key=version_sort_key)) + return max(authorized, key=version_sort_key) async def list_resource_templates( self, *, run_middleware: bool = True @@ -1051,7 +1051,7 @@ class FastMCP( if not authorized: return None - return cast(ResourceTemplate, max(authorized, key=version_sort_key)) + return max(authorized, key=version_sort_key) async def list_prompts(self, *, run_middleware: bool = True) -> Sequence[Prompt]: """List all enabled prompts from providers. @@ -1173,7 +1173,7 @@ class FastMCP( if not authorized: return None - return cast(Prompt, max(authorized, key=version_sort_key)) + return max(authorized, key=version_sort_key) @overload async def call_tool( diff --git a/fastmcp_slim/fastmcp/utilities/json_schema.py b/fastmcp_slim/fastmcp/utilities/json_schema.py index ad78fdbf2..2cd604e78 100644 --- a/fastmcp_slim/fastmcp/utilities/json_schema.py +++ b/fastmcp_slim/fastmcp/utilities/json_schema.py @@ -531,7 +531,7 @@ def _single_pass_optimize( # this regardless of `in_schema` — a $ref in a user extension # still pins the referenced $def as "used". if prune_defs: - ref = node.get("$ref") # type: ignore + ref = node.get("$ref") if isinstance(ref, str) and ref.startswith("#/$defs/"): referenced_def = ref.split("/")[-1] if current_def_name: @@ -565,7 +565,7 @@ def _single_pass_optimize( if ( prune_additional_properties - and node.get("additionalProperties") is False # type: ignore + and node.get("additionalProperties") is False ): node.pop("additionalProperties") # type: ignore diff --git a/fastmcp_slim/fastmcp/utilities/versions.py b/fastmcp_slim/fastmcp/utilities/versions.py index 028f687b5..a8a7a898b 100644 --- a/fastmcp_slim/fastmcp/utilities/versions.py +++ b/fastmcp_slim/fastmcp/utilities/versions.py @@ -17,7 +17,7 @@ from __future__ import annotations from collections.abc import Callable, Sequence from dataclasses import dataclass from functools import total_ordering -from typing import TYPE_CHECKING, Any, TypeVar, cast +from typing import TYPE_CHECKING, Any, TypeVar from packaging.version import InvalidVersion, Version @@ -325,7 +325,7 @@ def dedupe_with_versions( result: list[C] = [] for versions in by_key.values(): - highest: C = cast(C, max(versions, key=version_sort_key)) + highest: C = max(versions, key=version_sort_key) if any(c.version is not None for c in versions): all_versions = sorted( [c.version for c in versions if c.version is not None], diff --git a/pyproject.toml b/pyproject.toml index 32738a1e5..090e8488b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,7 @@ dev = [ "pytest-timeout>=2.4.0", "pytest-xdist>=3.6.1", "ruff>=0.12.8", - "ty>=0.0.39", + "ty>=0.0.55", "prek>=0.2.12", "loq>=0.1.0a3", "opentelemetry-exporter-otlp-proto-grpc>=1.39.0", diff --git a/tests/client/test_elicitation.py b/tests/client/test_elicitation.py index b5d16bf4d..2318efe98 100644 --- a/tests/client/test_elicitation.py +++ b/tests/client/test_elicitation.py @@ -319,7 +319,7 @@ class TestScalarResponseTypes: result = await context.elicit(message="", response_type=None) assert result.action == "accept" assert isinstance(result, AcceptedElicitation) - accepted = cast(AcceptedElicitation[dict[str, Any]], result) + accepted = result assert isinstance(accepted.data, dict) return accepted.data @@ -341,7 +341,7 @@ class TestScalarResponseTypes: result = await context.elicit(message="", response_type=None) assert result.action == "accept" assert isinstance(result, AcceptedElicitation) - accepted = cast(AcceptedElicitation[dict[str, Any]], result) + accepted = result assert isinstance(accepted.data, dict) return accepted.data diff --git a/tests/client/test_elicitation_enums.py b/tests/client/test_elicitation_enums.py index 6e8b1e7fd..d67e2b4f1 100644 --- a/tests/client/test_elicitation_enums.py +++ b/tests/client/test_elicitation_enums.py @@ -200,7 +200,7 @@ async def test_list_list_multi_select_untitled(): if result.action == "accept": assert isinstance(result, AcceptedElicitation) assert isinstance(result.data, list) - return ",".join(result.data) # type: ignore[no-matching-overload] # ty:ignore[no-matching-overload] + return ",".join(result.data) # type: ignore[no-matching-overload] return "declined" async def elicitation_handler(message, response_type, params, ctx): @@ -238,7 +238,7 @@ async def test_list_dict_multi_select_titled(): if result.action == "accept": assert isinstance(result, AcceptedElicitation) assert isinstance(result.data, list) - return ",".join(result.data) # type: ignore[no-matching-overload] # ty:ignore[no-matching-overload] + return ",".join(result.data) # type: ignore[no-matching-overload] return "declined" async def elicitation_handler(message, response_type, params, ctx): diff --git a/tests/client/test_streamable_http.py b/tests/client/test_streamable_http.py index 388eaaa9b..9d5fd6616 100644 --- a/tests/client/test_streamable_http.py +++ b/tests/client/test_streamable_http.py @@ -31,7 +31,7 @@ def create_test_server() -> FastMCP: result = await ctx.elicit("What is your name?", response_type=str) if result.action == "accept": - return f"You said your name was: {result.data}!" # ty: ignore[unresolved-attribute] + return f"You said your name was: {result.data}!" else: return "No name provided" diff --git a/tests/test_apps_prefab.py b/tests/test_apps_prefab.py index 5f19e1982..72311b614 100644 --- a/tests/test_apps_prefab.py +++ b/tests/test_apps_prefab.py @@ -9,7 +9,7 @@ from __future__ import annotations from typing import Annotated from mcp.types import TextContent -from prefab_ui.app import PrefabApp +from prefab_ui.app import PROTOCOL_VERSION, PrefabApp from prefab_ui.components import Column, Heading, Text from prefab_ui.components.base import Component @@ -39,7 +39,7 @@ class TestConvertResult: assert isinstance(result.content[0], TextContent) assert result.content[0].text == "[Rendered Prefab UI]" assert result.structured_content is not None - assert result.structured_content["$prefab"]["version"] == "0.2" + assert result.structured_content["$prefab"]["version"] == PROTOCOL_VERSION assert result.structured_content["state"] == {"name": "Alice"} # PrefabApp wraps view in a pf-app-root Div root = result.structured_content["view"] @@ -55,7 +55,7 @@ class TestConvertResult: assert isinstance(result, ToolResult) assert result.structured_content is not None - assert result.structured_content["$prefab"]["version"] == "0.2" + assert result.structured_content["$prefab"]["version"] == PROTOCOL_VERSION assert result.structured_content["view"]["type"] == "Div" assert result.structured_content["view"]["children"][0]["type"] == "Heading" @@ -71,7 +71,7 @@ class TestConvertResult: assert isinstance(result.content[0], TextContent) assert result.content[0].text == "Custom fallback text" assert result.structured_content is not None - assert result.structured_content["$prefab"]["version"] == "0.2" + assert result.structured_content["$prefab"]["version"] == PROTOCOL_VERSION assert result.structured_content["view"]["type"] == "Div" assert result.structured_content["view"]["children"][0]["type"] == "Heading" @@ -85,7 +85,7 @@ class TestConvertResult: assert isinstance(result.content[0], TextContent) assert result.content[0].text == "My text" assert result.structured_content is not None - assert result.structured_content["$prefab"]["version"] == "0.2" + assert result.structured_content["$prefab"]["version"] == PROTOCOL_VERSION assert result.structured_content["view"]["type"] == "Div" assert result.structured_content["view"]["children"][0]["type"] == "Heading" @@ -391,7 +391,7 @@ class TestIntegration: result = await client.call_tool("greet", {"name": "Alice"}) assert result.structured_content is not None - assert result.structured_content["$prefab"]["version"] == "0.2" + assert result.structured_content["$prefab"]["version"] == PROTOCOL_VERSION assert result.structured_content["state"] == {"name": "Alice"} async def test_tool_call_with_custom_text(self): @@ -412,7 +412,7 @@ class TestIntegration: "Greeting for Alice" in c.text for c in result.content if hasattr(c, "text") ) assert result.structured_content is not None - assert result.structured_content["$prefab"]["version"] == "0.2" + assert result.structured_content["$prefab"]["version"] == PROTOCOL_VERSION async def test_tools_list_includes_app_meta(self): mcp = FastMCP("test") diff --git a/uv.lock b/uv.lock index 7809504cd..02bfd8913 100644 --- a/uv.lock +++ b/uv.lock @@ -10,7 +10,7 @@ resolution-markers = [ ] [options] -exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values. +exclude-newer = "2026-06-28T22:13:51.808463Z" exclude-newer-span = "P1W" [options.exclude-newer-package] @@ -936,7 +936,7 @@ dev = [ { name = "pytest-timeout", specifier = ">=2.4.0" }, { name = "pytest-xdist", specifier = ">=3.6.1" }, { name = "ruff", specifier = ">=0.12.8" }, - { name = "ty", specifier = ">=0.0.39" }, + { name = "ty", specifier = ">=0.0.55" }, ] [[package]] @@ -3154,27 +3154,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.39" +version = "0.0.55" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/8d/7b5c74dc287fbcb37bae9853cec13bf44717c1735298500e4aeba31579a9/ty-0.0.39.tar.gz", hash = "sha256:f750277e76a01ecd86185960eca73823c26a53c51103568d56d4d904575159fd", size = 5702365, upload-time = "2026-05-22T21:09:56.403Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/17/9b89802c26d12d0f7a27bc25d4066d941d42891e8898f9f26499f0067e32/ty-0.0.39-py3-none-linux_armv6l.whl", hash = "sha256:c1bb7ac70f1f7d70cc6655fd96558039e4562b10f489fa49c7ebfd5fcee73ad1", size = 11360431, upload-time = "2026-05-22T21:09:18.689Z" }, - { url = "https://files.pythonhosted.org/packages/9c/c6/663ded50e823dbf9fb9d002eca46b7cb1fb2c72b744b84f22ce732a0ee0b/ty-0.0.39-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3435b64c1e59c14c9aa39c20cc018823937cd38d55db853e74d95b8f420569b0", size = 11096281, upload-time = "2026-05-22T21:09:15.383Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ae/5d38ba9a6456ff4c78d212cf464fd8b9a25d8118465197b0b2dc891c0b19/ty-0.0.39-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f136377ce46c73677701a9e1ad730bf72f699bcec046e422eb79d0886cac3ab", size = 10529674, upload-time = "2026-05-22T21:09:46.471Z" }, - { url = "https://files.pythonhosted.org/packages/be/6f/43638cb8106445d3c8817256a0731cde9dd7b6a53ae2e881294bc1930ca3/ty-0.0.39-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36b65fb0cc17f03e851d40e210d420be94ab8bc52d041328ad1e45f616036a61", size = 11055561, upload-time = "2026-05-22T21:09:36.981Z" }, - { url = "https://files.pythonhosted.org/packages/91/17/95e62cf4458527ce78dc386eba18f8b10c3fb64cd8c9e7e59b262ff6029d/ty-0.0.39-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4967967bfadf3860ff84c3fccdbaec8edf8aa20d0d727521084733d853de6657", size = 11127185, upload-time = "2026-05-22T21:09:31.395Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c0/93666c213db5c71ab1b1f1a0db5f66bf8c7c0e0b0bf59859f5da8f0b3c36/ty-0.0.39-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e10ecb1297099ddf9a1f054f8bd921d1863ce85fb819a3c96ed27865a1ba6ed", size = 11608459, upload-time = "2026-05-22T21:09:12.862Z" }, - { url = "https://files.pythonhosted.org/packages/79/85/3b26585afc8b50230d6464bb0642feef4fab3f847e38b1f0ffa971a81446/ty-0.0.39-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b19cca70e465d71b0510656343883d62372bbe74b7845cae7c0e701d6d5264b", size = 12177101, upload-time = "2026-05-22T21:09:40.519Z" }, - { url = "https://files.pythonhosted.org/packages/49/4a/1039e4f6afc576dc1c3a4d22a6478904a1ad3766597cd0b93c077ab9dfce/ty-0.0.39-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:56c6704b01b9b3d80ff26b2918423b742516d1e469bef830e9254dcedc9185bf", size = 11827815, upload-time = "2026-05-22T21:09:49.89Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c5/4688652870e350a76a8157f7ffb59ad54f37d5d10725aa7076f66ac94ec8/ty-0.0.39-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b7840ff46764b6a6757f4ade1cd0530fc3e8a0b435ca93e7602360e4cb90b6", size = 11694429, upload-time = "2026-05-22T21:09:21.568Z" }, - { url = "https://files.pythonhosted.org/packages/fc/72/8a1c4e823bb5bdc935a1c8140e100304e36a68a4139592f170aa9736fdb7/ty-0.0.39-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:1c62a3a87ce26b50819f0dbf03bd95f23f19eeb87bbc7aa732ec64277c77f1aa", size = 11869846, upload-time = "2026-05-22T21:09:28.053Z" }, - { url = "https://files.pythonhosted.org/packages/17/9f/cf982457b861ae22d657c5dcdbc631199f7f90264279db1d17230dfbc3ff/ty-0.0.39-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f8c34bc81a9c3516e49904e9d8330aac385377cca98390193ea02b903a40fcf0", size = 11029763, upload-time = "2026-05-22T21:09:06.791Z" }, - { url = "https://files.pythonhosted.org/packages/46/c9/95b64f6d43ae6e8f0b7e13dacf9c196d35819af22b1924171fba31383156/ty-0.0.39-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:66f5ab11586a64e79cb692ad685ee5469325c31b5f30bd3554f52f36dbe28cc4", size = 11146761, upload-time = "2026-05-22T21:09:10.178Z" }, - { url = "https://files.pythonhosted.org/packages/52/69/0a89cfb06f7632a05bf56c78e0affb4a40f81759e275376cea75c9c5abe9/ty-0.0.39-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e8d89732bcbbcb091f439e556dfc4932f198b118b47d5b85212c60662099670e", size = 11281843, upload-time = "2026-05-22T21:09:34.234Z" }, - { url = "https://files.pythonhosted.org/packages/0e/53/64c4a27067a46643fea2b3fcf21a8a2f838d91a65ffdd14f2e82945b9538/ty-0.0.39-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:eceb6c91dcd05a231119f82abdd9aa337513de23ca6ac990bc44f88791dc1799", size = 11792477, upload-time = "2026-05-22T21:09:24.923Z" }, - { url = "https://files.pythonhosted.org/packages/1a/e8/02f4dd4a12bcdbda0006f9c7ff3b99a4be06bd0d257d3bd4a5b66de074e6/ty-0.0.39-py3-none-win32.whl", hash = "sha256:891c3262314dbc80bf3e872634d23dd216306945daa9a9fcc206ce5ed21ac4c9", size = 10615377, upload-time = "2026-05-22T21:09:43.167Z" }, - { url = "https://files.pythonhosted.org/packages/b5/5a/aaeb22faa8d4dae90a287d4c3636c671edcff3b99be5f4fc8b79ad71eef6/ty-0.0.39-py3-none-win_amd64.whl", hash = "sha256:ba7f2d54452535419e90f6f03ff39282999e87b43c21c00559f6d7ad711a36d5", size = 11710711, upload-time = "2026-05-22T21:09:53.179Z" }, - { url = "https://files.pythonhosted.org/packages/a3/17/ae7339651bfcaa5f54698c8c70eaf5031baa400ecb67baec31d03a56cbd4/ty-0.0.39-py3-none-win_arm64.whl", hash = "sha256:eb4cf0fefbbfedf9a352597bb2431ebdcb7eb3a595c0f825f228e897a0ec285d", size = 11081409, upload-time = "2026-05-22T21:09:03.741Z" }, + { url = "https://files.pythonhosted.org/packages/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" }, + { url = "https://files.pythonhosted.org/packages/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" }, + { url = "https://files.pythonhosted.org/packages/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" }, + { url = "https://files.pythonhosted.org/packages/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" }, + { url = "https://files.pythonhosted.org/packages/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" }, + { url = "https://files.pythonhosted.org/packages/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" }, + { url = "https://files.pythonhosted.org/packages/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" }, + { url = "https://files.pythonhosted.org/packages/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" }, + { url = "https://files.pythonhosted.org/packages/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" }, + { url = "https://files.pythonhosted.org/packages/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" }, ] [[package]]