From 73ea0df0257f595564a6e45ed226eb1192de40eb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 14:28:36 +0000 Subject: [PATCH] Upgrade ty to 0.0.63 and fix new MRO-based method-override diagnostics ty 0.0.62 added MRO-based checking for inherited method conflicts, which surfaced 51 invalid-method-override diagnostics in plain-postgres and 6 in plain-email. For plain-postgres, fix the actual signature mismatches in the shared base classes (RegisterLookupMixin.get_lookup/get_transform, BaseExpression. get_transform, Lookup.resolve_expression) rather than suppressing per subclass: rename parameters to match, and correct get_transform's return type to Callable[..., Transform] | None to reflect that it can genuinely return a KeyTransformFactory instance alongside Transform subclasses. This also lets an existing invalid-return-type ignore in expressions.py be removed since the types now agree. For plain-email, add ty: ignore[invalid-method-override] on the three SafeMIME* classes, consistent with the existing ignore already used on set_payload in the same file, since MIMEMixin intentionally diverges from stdlib email.message.Message's as_bytes/as_string signature. --- plain-email/plain/email/message.py | 6 +-- plain-postgres/plain/postgres/expressions.py | 4 +- .../plain/postgres/fields/encrypted.py | 10 ++--- plain-postgres/plain/postgres/fields/json.py | 8 ++-- .../plain/postgres/fields/reverse_related.py | 4 +- plain-postgres/plain/postgres/lookups.py | 4 +- plain-postgres/plain/postgres/query_utils.py | 14 +++--- pyproject.toml | 2 +- uv.lock | 44 +++++++++---------- 9 files changed, 45 insertions(+), 51 deletions(-) diff --git a/plain-email/plain/email/message.py b/plain-email/plain/email/message.py index 53e6499548..8017575dd6 100644 --- a/plain-email/plain/email/message.py +++ b/plain-email/plain/email/message.py @@ -163,14 +163,14 @@ def as_bytes(self, unixfrom: bool = False, linesep: str = "\n") -> bytes: return fp.getvalue() -class SafeMIMEMessage(MIMEMixin, MIMEMessage): +class SafeMIMEMessage(MIMEMixin, MIMEMessage): # ty: ignore[invalid-method-override] def __setitem__(self, name: str, val: str) -> None: # message/rfc822 attachments must be ASCII name, val = _forbid_multi_line_headers(name, val, "ascii") MIMEMessage.__setitem__(self, name, val) -class SafeMIMEText(MIMEMixin, MIMEText): +class SafeMIMEText(MIMEMixin, MIMEText): # ty: ignore[invalid-method-override] def __init__( self, _text: str, _subtype: str = "plain", _charset: str | None = None ) -> None: @@ -195,7 +195,7 @@ def set_payload( # ty: ignore[invalid-method-override] MIMEText.set_payload(self, payload, charset=charset) -class SafeMIMEMultipart(MIMEMixin, MIMEMultipart): +class SafeMIMEMultipart(MIMEMixin, MIMEMultipart): # ty: ignore[invalid-method-override] def __init__( self, _subtype: str = "mixed", diff --git a/plain-postgres/plain/postgres/expressions.py b/plain-postgres/plain/postgres/expressions.py index 5c53106207..d351fa8de3 100644 --- a/plain-postgres/plain/postgres/expressions.py +++ b/plain-postgres/plain/postgres/expressions.py @@ -429,8 +429,8 @@ def convert_value(self) -> Callable[[Any, Any, Any], Any]: def get_lookup(self, lookup: str) -> type[Lookup] | None: return self.output_field.get_lookup(lookup) - def get_transform(self, name: str) -> type[Transform] | None: - return self.output_field.get_transform(name) # ty: ignore[invalid-return-type] + def get_transform(self, name: str) -> Callable[..., Transform] | None: + return self.output_field.get_transform(name) def relabeled_clone(self, change_map: dict[str, str]) -> Self: clone = self.copy() diff --git a/plain-postgres/plain/postgres/fields/encrypted.py b/plain-postgres/plain/postgres/fields/encrypted.py index 6d45a0509b..b1b39cc80e 100644 --- a/plain-postgres/plain/postgres/fields/encrypted.py +++ b/plain-postgres/plain/postgres/fields/encrypted.py @@ -125,15 +125,13 @@ class EncryptedFieldMixin: name: str model: Any - def get_lookup(self, lookup_name: str) -> type[Lookup] | None: - if lookup_name not in _ALLOWED_LOOKUPS: + def get_lookup(self, lookup: str) -> type[Lookup] | None: + if lookup not in _ALLOWED_LOOKUPS: return None get_lookup = getattr(super(), "get_lookup") - return get_lookup(lookup_name) + return get_lookup(lookup) - def get_transform( - self, lookup_name: str - ) -> type[Transform] | Callable[..., Any] | None: + def get_transform(self, name: str) -> Callable[..., Transform] | None: return None def _check_encrypted_constraints(self) -> list[PreflightResult]: diff --git a/plain-postgres/plain/postgres/fields/json.py b/plain-postgres/plain/postgres/fields/json.py index 3b27e6248e..4d871534c7 100644 --- a/plain-postgres/plain/postgres/fields/json.py +++ b/plain-postgres/plain/postgres/fields/json.py @@ -90,14 +90,12 @@ def get_db_prep_save(self, value: Any, connection: DatabaseConnection) -> Any: return value return self.get_db_prep_value(value, connection) - def get_transform( - self, lookup_name: str - ) -> type[Transform] | Callable[..., Any] | None: + def get_transform(self, name: str) -> Callable[..., Transform] | None: # Always returns a transform (never None in practice) - transform = super().get_transform(lookup_name) + transform = super().get_transform(name) if transform: return transform - return KeyTransformFactory(lookup_name) + return KeyTransformFactory(name) def validate(self, value: Any, model_instance: Any) -> None: super().validate(value, model_instance) diff --git a/plain-postgres/plain/postgres/fields/reverse_related.py b/plain-postgres/plain/postgres/fields/reverse_related.py index 931c1af729..60d2ab858a 100644 --- a/plain-postgres/plain/postgres/fields/reverse_related.py +++ b/plain-postgres/plain/postgres/fields/reverse_related.py @@ -100,8 +100,8 @@ def related_model(self) -> type[Model]: ) return self.field.model - def get_lookup(self, lookup_name: str) -> type[Lookup] | None: - return self.field.get_lookup(lookup_name) + def get_lookup(self, lookup: str) -> type[Lookup] | None: + return self.field.get_lookup(lookup) def __repr__(self) -> str: return f"<{type(self).__name__}: {self.related_model.model_options.package_label}.{self.related_model.model_options.model_name}>" diff --git a/plain-postgres/plain/postgres/lookups.py b/plain-postgres/plain/postgres/lookups.py index 01b5a2ce2a..f337ad25dd 100644 --- a/plain-postgres/plain/postgres/lookups.py +++ b/plain-postgres/plain/postgres/lookups.py @@ -5,7 +5,7 @@ import math from collections.abc import Sequence from functools import cached_property -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Self from plain.postgres.dialect import ( OPERATORS, @@ -187,7 +187,7 @@ def resolve_expression( reuse: Any = None, summarize: bool = False, for_save: bool = False, - ) -> Lookup: + ) -> Self: c = self.copy() c.is_summary = summarize c.lhs = self.lhs.resolve_expression( diff --git a/plain-postgres/plain/postgres/query_utils.py b/plain-postgres/plain/postgres/query_utils.py index a738d2f6c7..5f93f57555 100644 --- a/plain-postgres/plain/postgres/query_utils.py +++ b/plain-postgres/plain/postgres/query_utils.py @@ -220,28 +220,26 @@ def get_instance_lookups(self) -> dict[str, type[Lookup | Transform]]: get_class_lookups ) - def get_lookup(self, lookup_name: str) -> type[Lookup] | None: + def get_lookup(self, lookup: str) -> type[Lookup] | None: from plain.postgres.lookups import Lookup - found = self._get_lookup(lookup_name) + found = self._get_lookup(lookup) if found is None: # output_field is a Field which inherits from RegisterLookupMixin if output_field := getattr(self, "output_field", None): - return output_field.get_lookup(lookup_name) + return output_field.get_lookup(lookup) if found is not None and not issubclass(found, Lookup): return None return found - def get_transform( - self, lookup_name: str - ) -> type[Transform] | Callable[..., Any] | None: + def get_transform(self, name: str) -> Callable[..., Transform] | None: from plain.postgres.lookups import Transform - found = self._get_lookup(lookup_name) + found = self._get_lookup(name) if found is None: # output_field is a Field which inherits from RegisterLookupMixin if output_field := getattr(self, "output_field", None): - return output_field.get_transform(lookup_name) + return output_field.get_transform(name) if found is not None and not issubclass(found, Transform): return None return found diff --git a/pyproject.toml b/pyproject.toml index 5988d903ae..53b9aabb67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dev = [ "plain-tunnel", "plain-vendor", # Type checking and better dev experience - "ty>=0.0.61", + "ty>=0.0.63", "psycopg[binary]>=3.2.12", ] diff --git a/uv.lock b/uv.lock index 36dddd9f90..a36090e602 100644 --- a/uv.lock +++ b/uv.lock @@ -1635,7 +1635,7 @@ dev = [ { name = "plain-tunnel", editable = "plain-tunnel" }, { name = "plain-vendor", editable = "plain-vendor" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.2.12" }, - { name = "ty", specifier = ">=0.0.61" }, + { name = "ty", specifier = ">=0.0.63" }, ] [[package]] @@ -2119,27 +2119,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.61" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/92/63/6944925d0fe9a4bb9cc744e6c045a42bbd2ee4654c103190674577a36c3f/ty-0.0.61.tar.gz", hash = "sha256:acbf0d914cc7e2e57ccc440036af36114819e2a604a5ffb554e72e4ca7dd65a2", size = 6234957, upload-time = "2026-07-18T01:39:54.696Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/cf/044f31523e2768e3e64b0ca2ec32f70b3a731d4a2caa6ea110baf26e251c/ty-0.0.61-py3-none-linux_armv6l.whl", hash = "sha256:148779b8675eac93f40ec58bd70037fe67537117f20a23272264f8f136d41336", size = 11891448, upload-time = "2026-07-18T01:39:18.449Z" }, - { url = "https://files.pythonhosted.org/packages/d2/55/558cfe76b65d91d1854bbfac336020bd42fd887caa632d845d13c0c539eb/ty-0.0.61-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:08217382b3385808ee7288501ea3214b32631b08d1fd091ece6799b0c95264c5", size = 11602442, upload-time = "2026-07-18T01:39:20.914Z" }, - { url = "https://files.pythonhosted.org/packages/27/be/78c0ae6634cd606a68e5b46b338db427a48a1800c96a749b2d2f7a702e03/ty-0.0.61-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d99c729011b47dec20e78a32ac9c8f6defd4cf62f7bb851bbccf70dde6cee50", size = 11125286, upload-time = "2026-07-18T01:39:22.893Z" }, - { url = "https://files.pythonhosted.org/packages/a4/18/a40793962f1b6337938ddb0bca7496b54e70879e23b4d2cc8dfd7e5d1af3/ty-0.0.61-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cda607978ae271b77e51c947663218bce635c3507e256865444b10c37cdb60d", size = 11663403, upload-time = "2026-07-18T01:39:25.017Z" }, - { url = "https://files.pythonhosted.org/packages/98/c1/7879244da5b30407dc368946d36be5024380073408b079f144ffe034030e/ty-0.0.61-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d78f160a0f9434d570cdcdbc4dafba1f6aac3c47a32f9f63995b3cb55ffe4b6", size = 11715250, upload-time = "2026-07-18T01:39:27.045Z" }, - { url = "https://files.pythonhosted.org/packages/35/c4/8a4637cd58abd37f315dd515e24c582986cb1bfdf2edc4786882f5a4f69a/ty-0.0.61-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09aeab4800b36e93e4ce918699004da642d74988cac920b7592a6a2b9be6611c", size = 12393876, upload-time = "2026-07-18T01:39:29.197Z" }, - { url = "https://files.pythonhosted.org/packages/27/4b/27e7c640b1272743503229aa17ae2167a538040c4716a2fa1777c2b34fea/ty-0.0.61-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dccc8136df44142a109953a168be17b4915c99876b047d0b6672c31dae939bdf", size = 12958187, upload-time = "2026-07-18T01:39:31.308Z" }, - { url = "https://files.pythonhosted.org/packages/3a/f5/70eaaefb6081fb0a8115cff66fbfaa20dafac8c646df2477adad95a59de2/ty-0.0.61-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:220760c2d13a887d027ee1093172c24ac35b6e634805329c93a30908ae4d3f5c", size = 12560101, upload-time = "2026-07-18T01:39:33.35Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5a/17bae3b6429b5c479dc6c1e344d34e1f79efbc27531f15f3ee5b5da63745/ty-0.0.61-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:effefbb89da7128d18059529d1c2ea390fe7f1f3882690d257ca2143d49a0c34", size = 12225389, upload-time = "2026-07-18T01:39:35.436Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0e/2ac380ba20d6395542c8df1d6fa4f00e2aead784c2e6aaefa1e02ed0610c/ty-0.0.61-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ba8b28a5ef811d5bb6461e37d76110c06fd20487474865c323d3d18b08b972b2", size = 12548403, upload-time = "2026-07-18T01:39:37.556Z" }, - { url = "https://files.pythonhosted.org/packages/b0/e5/7da4b73e825e1a9808c26d68b0156e9a37aede1846191210dfffb8c64042/ty-0.0.61-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:88ecd6d9b05e8174b1860dac9bd3e188d6cef5702b0d3239fd9f94f6ac73a29d", size = 11621813, upload-time = "2026-07-18T01:39:39.919Z" }, - { url = "https://files.pythonhosted.org/packages/9b/3c/5b58015e998cd0d89b17a463b6321421457d86d987574e8dac65ddfceba3/ty-0.0.61-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fb0cdfe4c48542ffb9a1139825dfa3d4aae49e96e966682ef7da762ab97831ff", size = 11734101, upload-time = "2026-07-18T01:39:42.097Z" }, - { url = "https://files.pythonhosted.org/packages/a6/21/294f4cc819b7b12ed659fd860e5cdfbd592d4c768c8f23596685dbc43e6b/ty-0.0.61-py3-none-musllinux_1_2_i686.whl", hash = "sha256:dff03873c0c3d0b44738f8b6d403b0756a31cf54c65136397df7624c6159b1f0", size = 11988401, upload-time = "2026-07-18T01:39:44.183Z" }, - { url = "https://files.pythonhosted.org/packages/2e/26/0f96f79fdac118521a9771e9eef3f9b3f447d647b2c77953e80a1715c7e8/ty-0.0.61-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a9210e80e3d41c1dfc751e9e8e0980272f475031fafd0fb0f48aee233c78da03", size = 12330624, upload-time = "2026-07-18T01:39:46.662Z" }, - { url = "https://files.pythonhosted.org/packages/e3/08/1e62d1bca5c0cebdc7a34db1f4b61557aab85961cedd56953dd2c32d3e66/ty-0.0.61-py3-none-win32.whl", hash = "sha256:e3e1fe06f49a5492a922a5df2739834aa5ee978c7dd10414119dc8755cc40c9c", size = 11313991, upload-time = "2026-07-18T01:39:48.761Z" }, - { url = "https://files.pythonhosted.org/packages/26/f1/d8e33b3aeb36b73d81ae34d10e46ec4abf506d68f4e0a1491a76a593dd42/ty-0.0.61-py3-none-win_amd64.whl", hash = "sha256:25f2291169e0298fcdbba1b1fea64f8207a6c1908dddef32346fd5e3e6ac9221", size = 12311717, upload-time = "2026-07-18T01:39:50.881Z" }, - { url = "https://files.pythonhosted.org/packages/e1/14/7caec26d93a943c0e7d15eb7374644508d08cbd387d112b722b12d14e044/ty-0.0.61-py3-none-win_arm64.whl", hash = "sha256:3e496f7698bc4b5bbb1eb66d8b5799ba87596d88d36604ca359083893fa2fc49", size = 11693485, upload-time = "2026-07-18T01:39:52.73Z" }, +version = "0.0.63" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/ce/cbeaa5c7576fec643609dfbf200d59493523b1cc0481d4e7a5effcbf0630/ty-0.0.63.tar.gz", hash = "sha256:c2f66439393b3acac69306c117d4ae44638ce5fffa4a20c21046e85bd473359f", size = 6280695, upload-time = "2026-07-23T11:41:39.845Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/e4/d17a8e113ab15c692fe6fb9c422112d4bee7e829d80ced35826b45d98d98/ty-0.0.63-py3-none-linux_armv6l.whl", hash = "sha256:9a4ef7782e3af314fb63d006bec5ac3025bd70fee774b4dcfb5e44e8564f1994", size = 12056302, upload-time = "2026-07-23T11:41:03.5Z" }, + { url = "https://files.pythonhosted.org/packages/be/0b/357234c815dc4bfcc88c3f860aa0983fe4228c8aa2a5bb16c35ee08a94af/ty-0.0.63-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:01eab0ab70d51ad10298aa2d4b058b387a1fe93e5ee52d2a1ee23e9c69ba8354", size = 11737674, upload-time = "2026-07-23T11:41:05.862Z" }, + { url = "https://files.pythonhosted.org/packages/1c/13/193d9aeeb6774690351cff9fafabd3ae9b54cc225d125e07fa004ce23bdc/ty-0.0.63-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a671b61eaad16178389e05b9c108c9cb75ae8d84968fe55906484f55d6268338", size = 11264191, upload-time = "2026-07-23T11:41:07.963Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b7/c5843e16759a2b25fc8a7197473474038e585ac9fdaa6fa16aeb6384bf6a/ty-0.0.63-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b255cc83d95c51bb9ee4931303fdbece8cb1d6d7c655eb77a3f2c8f349fb64d6", size = 11818890, upload-time = "2026-07-23T11:41:09.885Z" }, + { url = "https://files.pythonhosted.org/packages/06/20/adf83ae1fc570d9bb449605e1eeeaa523ad2329ca838a636698b5c135d11/ty-0.0.63-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0da1e8183aa4f893421a173904478021498f542ee41273660538da6649a9631c", size = 11853141, upload-time = "2026-07-23T11:41:12.151Z" }, + { url = "https://files.pythonhosted.org/packages/ea/90/f8effd846e3ee13486ea08257c13094d58b9f188c5641bf609d6a7d5c09f/ty-0.0.63-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128f38eb5a67199e3811426386f7ec96f41251ddf45e04ddc0bcbb29d4853245", size = 12545184, upload-time = "2026-07-23T11:41:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/b8/76/aac3a30d40431eb1d329acea016b248f5b6255e30ef3d28e19447e344be2/ty-0.0.63-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bedf34aff8b0557f2a7119b314a68a9c9e58c1d21554d0e2748f2c5fe6a1f638", size = 13062375, upload-time = "2026-07-23T11:41:16.441Z" }, + { url = "https://files.pythonhosted.org/packages/62/3d/0158733932893e17f6008dadd74c8d7aed422fefc66e47fe77888014fe8c/ty-0.0.63-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7328d63c34587606dce02935a25404f54cd0161bfe413b8f7fc21d174aead612", size = 12619461, upload-time = "2026-07-23T11:41:18.538Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/e280ad095b050778f16f493d49a073fa5f6d8f301d3e2e59be6a672ba05c/ty-0.0.63-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504c4457f3a62afe836c1f26a2c9a12549299095f9cc4146778558df51a7515c", size = 12376402, upload-time = "2026-07-23T11:41:20.482Z" }, + { url = "https://files.pythonhosted.org/packages/57/57/619788bf335cb86b090470b557ae1eed33613dc24e12142505d32b462e58/ty-0.0.63-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:7394ed39424b027c89d5f0c818871c791e33958b88f5bb13e14bd4a12a3ca631", size = 12671377, upload-time = "2026-07-23T11:41:22.489Z" }, + { url = "https://files.pythonhosted.org/packages/07/a2/0aff2cdd3c98e2c4729337542b8da0ce1980790e1600e0c4a717a1f46293/ty-0.0.63-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:94c3f155230490f8fb505911655e940c630c25cc0c35a76690cb413254fb4437", size = 11768090, upload-time = "2026-07-23T11:41:24.558Z" }, + { url = "https://files.pythonhosted.org/packages/43/4a/cdb5f1d26154144dfee08e1bd671daf827238170f7cee9dad43990bb2016/ty-0.0.63-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c16e1d8b4f0ae99106d5f13a894034a202baf6cdab61e2bb1a239de82904f839", size = 11867747, upload-time = "2026-07-23T11:41:26.794Z" }, + { url = "https://files.pythonhosted.org/packages/7e/26/ecc09ecb70bc9fbfdf42fa57ff29568f173e8200df575a0d72dc2b8486f9/ty-0.0.63-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b66293dad89eaed4b9fbf3b661614dbeb85b59ba037178a3f9a0adedf264da5c", size = 12120000, upload-time = "2026-07-23T11:41:28.731Z" }, + { url = "https://files.pythonhosted.org/packages/14/07/862822f9c2c397785b69b25cf79b4dfc3c0d55684b9adf11ac194450f8e1/ty-0.0.63-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8b29cc832e2ec73502c97dd7325d6ae0e085b34a33529a0f785192317d9862fc", size = 12477862, upload-time = "2026-07-23T11:41:30.847Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c2/50b08b641578d6f48e8aa28a91d0de32c91aa24dd926ed199e8afd2d37ea/ty-0.0.63-py3-none-win32.whl", hash = "sha256:f0a8fbfd1f990c0c5d85cce018d438969ac79e49247e2192c9745394bb7d17ab", size = 11433155, upload-time = "2026-07-23T11:41:33.28Z" }, + { url = "https://files.pythonhosted.org/packages/92/2d/d422a5568f0d1f317186be22241288dc6e08b71dc24aca223f22038ac2d2/ty-0.0.63-py3-none-win_amd64.whl", hash = "sha256:2aa2370bdd6f42e9f37518c812379bef70b9162f9e5aad511495229b0b71cb93", size = 12450036, upload-time = "2026-07-23T11:41:35.559Z" }, + { url = "https://files.pythonhosted.org/packages/35/97/2c9748e28ead0650c7ad3e5f74f178832ceabd7cb5c272a882f29eb32ee4/ty-0.0.63-py3-none-win_arm64.whl", hash = "sha256:95ac1a62162c3c7ac204731e95ebf766d62a46a7bfa238a6acbe923fcb772cb1", size = 11826243, upload-time = "2026-07-23T11:41:37.749Z" }, ] [[package]]