diff --git a/.claude/formatting.md b/.claude/formatting.md index 62aaf61e7..60d98ca02 100644 --- a/.claude/formatting.md +++ b/.claude/formatting.md @@ -2,7 +2,7 @@ Do not suppress errors with workarounds like `# type: ignore`: -1. `make type_check` +1. `make type_check` — runs **ty** static type checker (config in `pyproject.toml` under `[tool.ty]`) 2. `make unit_test_py PY_TEST_FILES="relevant_test.py"` 3. `make integration_test PY_TEST_FILES="relevant_test.py"` (if cross-component behavior changed) 4. `make check_format` (or `make format` to auto-fix) diff --git a/.dockerignore b/.dockerignore index 09ca086aa..5fbd9528f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,6 +4,7 @@ **/*.pyo **/*.pyd **/*.pkl +.ty .mypy_cache _autosummary diff --git a/.gitignore b/.gitignore index 26bb8b2e8..4948fe5f0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ dist/ *.egg-info/ miniconda/ .mypy_cache/ +.ty/ tools/ .metals/ .bloop/ diff --git a/CLAUDE.md b/CLAUDE.md index b0479d7fe..dc016feae 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,7 +32,7 @@ make format_py # Auto-fix Python only make format_scala # Auto-fix Scala only make format_md # Auto-fix Markdown only make check_format # Check without fixing -make type_check # mypy static type checking +make type_check # ty static type checking # Build make compile_protos # Regenerate protobuf code after .proto changes diff --git a/Makefile b/Makefile index dc3accd94..464861512 100644 --- a/Makefile +++ b/Makefile @@ -144,7 +144,7 @@ format_md: format: format_py format_scala format_md type_check: - uv run mypy ${PYTHON_DIRS} --check-untyped-defs + uv run ty check ${PYTHON_DIRS} lint_test: check_format assert_yaml_configs_parse @echo "Lint checks pass!" diff --git a/README.md b/README.md index 0a0789aee..92bbcff63 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ make unit_test_py PY_TEST_FILES="eval_metrics_test.py" # Runs just Scala unit tests make unit_test_scala -# Run the python static type checker `mypy` +# Run the python static type checker `ty` make type_check # Run all formatting/linting tests diff --git a/gigl/distributed/dataset_factory.py b/gigl/distributed/dataset_factory.py index 1f78f83f3..a990e491f 100644 --- a/gigl/distributed/dataset_factory.py +++ b/gigl/distributed/dataset_factory.py @@ -613,7 +613,7 @@ def build_dataset_from_task_config_uri( ) # Need to do this "backwards" so the parent class can be defined first. - # Otherwise, mypy complains that: + # Otherwise, the type checker complains that: # "expression has type "type[DistPartitioner]", variable has type "type[DistRangePartitioner]" if not should_use_range_partitioning: partitioner_class = DistPartitioner diff --git a/gigl/src/common/vertex_ai_launcher.py b/gigl/src/common/vertex_ai_launcher.py index 64aa86a23..f06c1ef99 100644 --- a/gigl/src/common/vertex_ai_launcher.py +++ b/gigl/src/common/vertex_ai_launcher.py @@ -279,7 +279,7 @@ def _build_job_config( if vertex_ai_resource_config.timeout else None, # This should be `aiplatform.gapic.Scheduling.Strategy[inferencer_resource_config.scheduling_strategy]` - # But mypy complains otherwise... + # But the type checker complains otherwise... # gigl/src/inference/v2/glt_inferencer.py:124: error: The type "type[Strategy]" is not generic and not indexable [misc] # TODO(kmonte): Fix this scheduling_strategy=getattr( diff --git a/gigl/src/data_preprocessor/lib/ingest/bigquery.py b/gigl/src/data_preprocessor/lib/ingest/bigquery.py index 3936e976f..63811da72 100644 --- a/gigl/src/data_preprocessor/lib/ingest/bigquery.py +++ b/gigl/src/data_preprocessor/lib/ingest/bigquery.py @@ -44,7 +44,7 @@ def _get_bigquery_ptransform( ) -# Below type ignores are due to mypy star expansion issues: https://github.com/python/mypy/issues/6799 +# Below type ignores are due to star expansion issues with the type checker: https://github.com/python/mypy/issues/6799 @dataclass(frozen=True) class BigqueryNodeDataReference(NodeDataReference): """ diff --git a/gigl/src/data_preprocessor/lib/ingest/reference.py b/gigl/src/data_preprocessor/lib/ingest/reference.py index 78db483f7..d382d855f 100644 --- a/gigl/src/data_preprocessor/lib/ingest/reference.py +++ b/gigl/src/data_preprocessor/lib/ingest/reference.py @@ -7,7 +7,7 @@ from gigl.src.common.types.graph_data import EdgeType, EdgeUsageType, NodeType from gigl.src.data_preprocessor.lib.types import InstanceDictPTransform -# Type hints for abstract dataclasses are currently not supported. https://github.com/python/mypy/issues/5374 +# Type hints for abstract dataclasses may have limited support in type checkers. https://github.com/python/mypy/issues/5374 @dataclass(frozen=True) # type: ignore diff --git a/gigl/types/graph.py b/gigl/types/graph.py index 00ee69e33..d70109637 100644 --- a/gigl/types/graph.py +++ b/gigl/types/graph.py @@ -387,8 +387,8 @@ def select_label_edge_types( str, int, # TODO(kmonte): Add GLT Partition book here - # We cannot at the moment as we mypy ignore GLT - # And adding it as a type here will break mypy. + # We cannot at the moment as we type-ignore GLT + # And adding it as a type here will break the type checker. # PartitionBook ) diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index d488c2a83..000000000 --- a/mypy.ini +++ /dev/null @@ -1,65 +0,0 @@ -# Global options: -[mypy] -python_version = 3.11 - -# Ignore modules that don't have any existing stubs - -[mypy-kfp.*] -ignore_missing_imports = True - -[mypy-kfp_server_api.*] -ignore_missing_imports = True - -[mypy-networkx.*] -ignore_missing_imports = True - -[mypy-setuptools] -ignore_missing_imports = True - -[mypy-tensorflow_data_validation] -ignore_missing_imports = True - -[mypy-tensorflow_metadata.*] -ignore_missing_imports = True - -[mypy-tensorflow.*] -ignore_missing_imports = True - -[mypy-torch_geometric.*] -ignore_missing_imports = True - -[mypy-tfx_bsl.*] -ignore_missing_imports = True - -[mypy-parameterized.*] -ignore_missing_imports = True - -[mypy-pyarrow.*] -ignore_missing_imports = True - -[mypy-matplotlib.*] -ignore_missing_imports = True - -[mypy-msgpack] -ignore_missing_imports = True - -[mypy-networkx] -ignore_missing_imports = True - -[mypy-torch_sparse.*] -ignore_missing_imports = True - -[mypy-absl.*] -ignore_missing_imports = True - -[mypy-graphlearn_torch.*] -ignore_missing_imports = True - -[mypy-google.cloud.storage.*] -ignore_missing_imports = True - -[mypy-torchrec.*] -ignore_missing_imports = True - -[mypy-google_cloud_pipeline_components.*] -ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index b0d0345f2..434feb022 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -140,9 +140,8 @@ lint = [ "ruff==0.15.10", "mdformat==0.7.22", "mdformat_tables==1.0.0", - "mypy==1.8.0", - "mypy-extensions", - "mypy-protobuf==3.3.0", + "ty~=0.0.29", + "mypy-protobuf==3.3.0", # Used for protobuf stub generation (protoc-gen-mypy), not type checking ] @@ -272,3 +271,36 @@ select = ["F401", "I"] [tool.ruff.lint.isort] # Docs: https://docs.astral.sh/ruff/settings/#lint_isort_known-first-party known-first-party = ["gigl", "tests", "snapchat", "scripts"] + +[tool.ty.environment] +python-version = "3.11" + +[tool.ty.src] +exclude = ["*_pb2.py", "*_pb2.pyi", "**/*.ipynb"] + +[tool.ty.analysis] +# Treat all types from unresolved third-party modules as Any instead of Unknown, +# preventing cascading type errors (e.g. unresolved-attribute, invalid-argument-type) +# on values imported from those modules. +# https://docs.astral.sh/ty/reference/configuration/#replace-imports-with-any +replace-imports-with-any = [ + "apache_beam.**", + "graphlearn_torch.**", + "kfp.**", + "tensorflow.**", + "tensorflow_metadata.**", + # TODO: (svij Torch has type information but we are doing some incorrect casting and using old api types thus type check fails + # Although, the code still runs fine. + "torch.**", + "torch_geometric.**", + "torchrec.**", +] + +# Generated protobuf files trigger type errors from the google-protobuf stubs +# (e.g. RegisterMessage argument types). These files are auto-generated by +# protoc and cannot be fixed — suppress all type errors in them. +[[tool.ty.overrides]] +include = ["**/*_pb2.py"] +[tool.ty.overrides.rules] +invalid-argument-type = "ignore" +unresolved-attribute = "ignore" diff --git a/tests/unit/distributed/dataset_input_metadata_translator_test.py b/tests/unit/distributed/dataset_input_metadata_translator_test.py index 4a9e6d0d0..6fa7c61aa 100644 --- a/tests/unit/distributed/dataset_input_metadata_translator_test.py +++ b/tests/unit/distributed/dataset_input_metadata_translator_test.py @@ -293,7 +293,7 @@ def test_translator_correctness(self, _, mocked_dataset_info: MockedDatasetInfo) ].HasField("positive_edge_info"): assert ( seralized_positive_label_info is not None - ) # We use assert instead of self.assertIsNotNone since this allows type narrowing with mypy + ) # We use assert instead of self.assertIsNotNone since this allows type narrowing with the type checker edge_info = preprocessed_metadata_pb_wrapper.preprocessed_metadata_pb.condensed_edge_type_to_preprocessed_metadata[ condensed_edge_type @@ -383,7 +383,7 @@ def test_translator_correctness(self, _, mocked_dataset_info: MockedDatasetInfo) ].HasField("negative_edge_info"): assert ( serialized_negative_label_info is not None - ) # We use assert instead of self.assertIsNotNone since this allows type narrowing with mypy + ) # We use assert instead of self.assertIsNotNone since this allows type narrowing with the type checker edge_info = preprocessed_metadata_pb_wrapper.preprocessed_metadata_pb.condensed_edge_type_to_preprocessed_metadata[ condensed_edge_type diff --git a/tests/unit/src/common/vertex_ai_launcher_test.py b/tests/unit/src/common/vertex_ai_launcher_test.py index 8db251b44..c8a293ef6 100644 --- a/tests/unit/src/common/vertex_ai_launcher_test.py +++ b/tests/unit/src/common/vertex_ai_launcher_test.py @@ -183,7 +183,9 @@ def test_launch_training_graph_store_cuda(self, mock_vertex_ai_service_class): process_command.split(), ) self.assertIsNotNone(compute_job_config.args) - assert compute_job_config.args is not None # Type narrowing for mypy + assert ( + compute_job_config.args is not None + ) # Type narrowing for the type checker self.assertIn(f"--job_name={job_name}", compute_job_config.args) self.assertIn( f"--learning_rate={process_runtime_args['learning_rate']}", @@ -294,7 +296,7 @@ def test_launch_inference_single_pool_cpu(self, mock_vertex_ai_service_class): # Verify command and args self.assertEqual(job_config.command, process_command.split()) self.assertIsNotNone(job_config.args) - assert job_config.args is not None # Type narrowing for mypy + assert job_config.args is not None # Type narrowing for the type checker self.assertIn(f"--job_name={job_name}", job_config.args) self.assertIn(f"--task_config_uri={task_config_uri}", job_config.args) self.assertIn(f"--resource_config_uri={resource_config_uri}", job_config.args) diff --git a/tests/unit/transforms/graph_transformer_test.py b/tests/unit/transforms/graph_transformer_test.py index 25cda0821..18551014b 100644 --- a/tests/unit/transforms/graph_transformer_test.py +++ b/tests/unit/transforms/graph_transformer_test.py @@ -654,7 +654,7 @@ def test_gradient_flow(self): # Check that gradients exist and are not NaN self.assertIsNotNone(sequences.grad) - assert sequences.grad is not None # Type narrowing for mypy + assert sequences.grad is not None # Type narrowing for the type checker self.assertFalse(torch.isnan(sequences.grad).any()) def test_transformer_with_classification_head(self): diff --git a/uv.lock b/uv.lock index 8e369b607..6ff90285b 100644 --- a/uv.lock +++ b/uv.lock @@ -773,8 +773,6 @@ dev = [ { name = "mdformat" }, { name = "mdformat-tables" }, { name = "mistune" }, - { name = "mypy" }, - { name = "mypy-extensions" }, { name = "mypy-protobuf" }, { name = "myst-nb" }, { name = "myst-parser" }, @@ -793,6 +791,7 @@ dev = [ { name = "sphinx-hoverxref" }, { name = "sphinx-rtd-theme" }, { name = "sphinx-tabs" }, + { name = "ty" }, { name = "types-psutil" }, { name = "types-pyyaml" }, { name = "types-requests" }, @@ -818,9 +817,8 @@ docs = [ lint = [ { name = "mdformat" }, { name = "mdformat-tables" }, - { name = "mypy" }, - { name = "mypy-extensions" }, { name = "mypy-protobuf" }, + { name = "ty" }, { name = "ruff" }, ] test = [ @@ -894,8 +892,6 @@ dev = [ { name = "mdformat", specifier = "==0.7.22" }, { name = "mdformat-tables", specifier = "==1.0.0" }, { name = "mistune", specifier = ">=2.0.3" }, - { name = "mypy", specifier = "==1.8.0" }, - { name = "mypy-extensions" }, { name = "mypy-protobuf", specifier = "==3.3.0" }, { name = "myst-nb", specifier = "==1.2.0" }, { name = "myst-parser", specifier = "==2.0.0" }, @@ -914,6 +910,7 @@ dev = [ { name = "sphinx-hoverxref", specifier = "==1.3.0" }, { name = "sphinx-rtd-theme", specifier = "==2.0.0" }, { name = "sphinx-tabs", specifier = "==3.4.5" }, + { name = "ty", specifier = "~=0.0.29" }, { name = "types-psutil", specifier = "==7.0.0.20250401" }, { name = "types-pyyaml", specifier = "~=6.0.12" }, { name = "types-requests", specifier = "==2.31.0.6" }, @@ -939,9 +936,8 @@ docs = [ lint = [ { name = "mdformat", specifier = "==0.7.22" }, { name = "mdformat-tables", specifier = "==1.0.0" }, - { name = "mypy", specifier = "==1.8.0" }, - { name = "mypy-extensions" }, { name = "mypy-protobuf", specifier = "==3.3.0" }, + { name = "ty", specifier = "~=0.0.29" }, { name = "ruff", specifier = "==0.15.10" }, ] test = [{ name = "parameterized", specifier = "==0.9.0" }] @@ -2249,24 +2245,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, ] -[[package]] -name = "mypy" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/16/22/25fac51008f0a4b2186da0dba3039128bd75d3fab8c07acd3ea5894f95cc/mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07", size = 2990299, upload-time = "2023-12-21T16:29:33.134Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d6/c4/2ce11ff9ba6c9c9e89df5049ab2325c85e60274194d6816e352926de5684/mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae", size = 10795101, upload-time = "2023-12-21T16:29:27.049Z" }, - { url = "https://files.pythonhosted.org/packages/bb/b7/882110d1345847ce660c51fc83b3b590b9512ec2ea44e6cfd629a7d66146/mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3", size = 9849744, upload-time = "2023-12-21T16:28:28.884Z" }, - { url = "https://files.pythonhosted.org/packages/19/c6/256f253cb3fc6b30b93a9836cf3c816a3ec09f934f7b567f693e5666d14f/mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817", size = 12391778, upload-time = "2023-12-21T16:28:17.728Z" }, - { url = "https://files.pythonhosted.org/packages/66/19/e0c9373258f3e84e1e24af357e5663e6b0058bb5c307287e9d1a473a9687/mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d", size = 12461242, upload-time = "2023-12-21T16:29:07.651Z" }, - { url = "https://files.pythonhosted.org/packages/a9/d7/a7ee8ca5a963b5bf55a6b4bc579df77c887e7fbc0910047b7d0f7750b048/mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835", size = 9205536, upload-time = "2023-12-21T16:28:56.76Z" }, - { url = "https://files.pythonhosted.org/packages/3a/e3/b582bff8e2fc7056a8a00ec06d2ac3509fc9595af9954099ed70e0418ac3/mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d", size = 2553257, upload-time = "2023-12-21T16:28:20.857Z" }, -] - [[package]] name = "mypy-extensions" version = "1.1.0" @@ -4400,6 +4378,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/39/43325b3b651d50187e591eefa22e236b2981afcebaefd4f2fc0ea99df191/triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467", size = 155531138, upload-time = "2025-07-30T19:58:29.908Z" }, ] +[[package]] +name = "ty" +version = "0.0.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/cc/5ea5d3a72216c8c2bf77d83066dd4f3553532d0aacc03d4a8397dd9845e1/ty-0.0.31.tar.gz", hash = "sha256:4a4094292d9671caf3b510c7edf36991acd9c962bb5d97205374ffed9f541c45", size = 5516619, upload-time = "2026-04-15T15:47:59.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/10/ea805cbbd75d5d50792551a2b383de8521eeab0c44f38c73e12819ced65e/ty-0.0.31-py3-none-linux_armv6l.whl", hash = "sha256:761651dc17ad7bc0abfc1b04b3f0e84df263ed435d34f29760b3da739ab02d35", size = 10834749, upload-time = "2026-04-15T15:48:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/d9/4c/fabf951850401d24d36b21bced088a366c6827e1c37dab4523afff84c4b2/ty-0.0.31-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c529922395a07231c27488f0290651e05d27d149f7e0aa807678f1f7e9c58a5e", size = 10626012, upload-time = "2026-04-15T15:48:22.554Z" }, + { url = "https://files.pythonhosted.org/packages/04/b0/4a5aff88d2544f19514a59c8f693d63144aa7307fe2ee5df608333ab5460/ty-0.0.31-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f345df2b87d747859e72c2cbc9be607ea1bbc8bc93dd32fa3d03ea091cb4fee", size = 10075790, upload-time = "2026-04-15T15:47:46.959Z" }, + { url = "https://files.pythonhosted.org/packages/d5/73/9d4dcad12cd4e85274014f2c0510ef93f590b2a1e5148de3a9f276098dad/ty-0.0.31-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4b207eddcfbafd376132689d3435b14efcb531289cb59cd961c6a611133bd54", size = 10590286, upload-time = "2026-04-15T15:48:06.222Z" }, + { url = "https://files.pythonhosted.org/packages/47/45/fe40adde18692359ded174ae7ddbfac056e876eb0f43b65be74fde7f6072/ty-0.0.31-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:663778b220f357067488ce68bfc52335ccbd161549776f70dcbde6bbde82f77a", size = 10623824, upload-time = "2026-04-15T15:48:12.965Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e8/0ffa2e09b548e6daa9ebc368d68b767dc2405ca4cbeadb7ede0e2cb21059/ty-0.0.31-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3506cfe87dfade0fb2960dd4fffd4fd8089003587b3445c0a1a295c9d83764fb", size = 11156864, upload-time = "2026-04-15T15:48:08.473Z" }, + { url = "https://files.pythonhosted.org/packages/08/e9/fd44c2075115d569593ee9473d7e2a38b750fd7e783421c95eb528c15df5/ty-0.0.31-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b3f3d8492f08e81916026354c1d1599e9ddfa1241804141a74d5662fc710085", size = 11696401, upload-time = "2026-04-15T15:48:17.355Z" }, + { url = "https://files.pythonhosted.org/packages/4e/50/35aad8eadf964d23e2a4faa5b38a206aa85c78833c8ce335dddd2c34ba63/ty-0.0.31-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a97de32ee6a619393a4c495e056a1c547de7877510f3152e61345c71d774d2d0", size = 11374903, upload-time = "2026-04-15T15:47:55.893Z" }, + { url = "https://files.pythonhosted.org/packages/c8/37/01eccd25d23f5aaa7f7ff1a87b5b215469f6b202cf689a1812b71c1e7f6b/ty-0.0.31-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c906354ce441e342646582bc9b8f48a676f79f3d061e25de15ff870e015ca14e", size = 11206624, upload-time = "2026-04-15T15:47:51.778Z" }, + { url = "https://files.pythonhosted.org/packages/f4/70/baad2914cb097453f127a221f8addb2b41926098059cd773c75e6a662fc4/ty-0.0.31-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:275bb7c82afcbf89fe2dbef1b2692f2bc98451f1ee2c8eb809ddd91317822388", size = 10575089, upload-time = "2026-04-15T15:47:49.448Z" }, + { url = "https://files.pythonhosted.org/packages/83/12/bae3a7bba2e785eb72ce00f9da70eedcb8c5e8299efecbd16e6e436abd82/ty-0.0.31-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:405da247027c6efd1e264886b6ac4a86ab3a4f09200b02e33630efe85f119e53", size = 10642315, upload-time = "2026-04-15T15:48:19.661Z" }, + { url = "https://files.pythonhosted.org/packages/93/9e/cad04d5d839bc60355cea98c7e09d724ea65f47184def0fae8b90dc54591/ty-0.0.31-py3-none-musllinux_1_2_i686.whl", hash = "sha256:54d9835608eed196853d6643f645c50ce83bcc7fe546cdb3e210c1bcf7c58c09", size = 10834473, upload-time = "2026-04-15T15:48:02.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ba/84112d280182d37690d3d2b4018b2667e42bc281585e607015635310016a/ty-0.0.31-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ee11be9b07e8c0c6b455ff075a0abe4f194de9476f57624db98eec9df618355", size = 11315785, upload-time = "2026-04-15T15:48:10.754Z" }, + { url = "https://files.pythonhosted.org/packages/50/9f/ac42dc223d7e0950e97a1854567a8b3e7fe09ad7375adbf91bfb43290482/ty-0.0.31-py3-none-win32.whl", hash = "sha256:7286587aacf3eef0956062d6492b893b02f82b0f22c5e230008e13ff0d216a8b", size = 10187657, upload-time = "2026-04-15T15:48:04.264Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/57ba7ea7ecb2f4751644ba91756e2be70e33ef5952c0c41a256a0e4c2437/ty-0.0.31-py3-none-win_amd64.whl", hash = "sha256:81134e25d2a2562ab372f24de8f9bd05034d27d30377a5d7540f259791c6234c", size = 11205258, upload-time = "2026-04-15T15:47:53.759Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/bca669095ccf0a400af941fdf741578d4c2d6719f1b7f10e6dbec10aa862/ty-0.0.31-py3-none-win_arm64.whl", hash = "sha256:e9cb15fad26545c6a608f40f227af3a5513cb376998ca6feddd47ca7d93ffafa", size = 10590392, upload-time = "2026-04-15T15:47:57.968Z" }, +] + [[package]] name = "types-protobuf" version = "6.32.1.20250918"