diff --git a/.github/actions/setup-python-env/action.yml b/.github/actions/setup-python-env/action.yml index f44036ed..436906c9 100644 --- a/.github/actions/setup-python-env/action.yml +++ b/.github/actions/setup-python-env/action.yml @@ -1,4 +1,5 @@ name: "Set Up Python Environment" +description: "Set up a Python environment using uv" inputs: python-version: @@ -13,10 +14,11 @@ runs: with: python-version: ${{ inputs.python-version }} - name: Install uv - uses: astral-sh/setup-uv@v7.1.6 + uses: astral-sh/setup-uv@v7.2.0 with: + python-version: ${{ inputs.python-version }} version-file: "pyproject.toml" - enable-cache: true + enable-cache: "auto" ignore-nothing-to-cache: true - name: Install project and dependencies run: uv sync --frozen --dev diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 138c91b5..522ad043 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -80,14 +80,20 @@ jobs: run: | uv run python -m pytest tests --verbose style: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12"] + # TODO: also run on macos-latest pending docker/colima issue + os: [ubuntu-latest] steps: - name: Check out repository uses: actions/checkout@v6 - name: Set up Python uses: ./.github/actions/setup-python-env with: - python-version: "3.11" + python-version: ${{ matrix.python-version }} - name: Run formatter run: | uv run python -m ruff format --diff colandr @@ -95,14 +101,20 @@ jobs: run: | uv run python -m ruff check --exit-zero colandr types: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12"] + # TODO: also run on macos-latest pending docker/colima issue + os: [ubuntu-latest] steps: - name: Check out repository uses: actions/checkout@v6 - name: Set up Python uses: ./.github/actions/setup-python-env with: - python-version: "3.11" + python-version: ${{ matrix.python-version }} - name: Check types with ty run: | uv run python -m ty check diff --git a/colandr/api/v1/authn.py b/colandr/api/v1/authn.py index bcd38074..1897fafc 100644 --- a/colandr/api/v1/authn.py +++ b/colandr/api/v1/authn.py @@ -14,7 +14,7 @@ # TODO: we should use redis for this # import celery # import redis.client -# _JWT_BLOCKLIST = celery.current_app.backend.client # type: ignore +# _JWT_BLOCKLIST = celery.current_app.backend.client _JWT_BLOCKLIST = set() diff --git a/colandr/api/v1/authz.py b/colandr/api/v1/authz.py index e6e1008b..b2a50cf7 100644 --- a/colandr/api/v1/authz.py +++ b/colandr/api/v1/authz.py @@ -28,6 +28,7 @@ def user_is_allowed_for_review( True if user is allowed to access review; False otherwise """ review = db.session.get(models.Review, review_id) + review_user_assoc = ( sa.select(models.ReviewUserAssoc) .where(models.ReviewUserAssoc.user_id == user.id) @@ -44,7 +45,7 @@ def user_is_allowed_for_review( or db.session.execute(review_user_assoc).scalar_one_or_none() is not None ) # allowed is conditional on review being frozen or not - and (if_frozen is True or review.status != "frozen") # type: ignore + and (if_frozen is True or getattr(review, "status", None) != "frozen") ) diff --git a/colandr/api/v1/routes/citation_imports.py b/colandr/api/v1/routes/citation_imports.py index 394a4626..3f860d7a 100644 --- a/colandr/api/v1/routes/citation_imports.py +++ b/colandr/api/v1/routes/citation_imports.py @@ -149,7 +149,7 @@ def post(self, files_data, query_data): if data_source is None: data_source = models.DataSource( source_type=source_type, source_name=source_name, source_url=source_url - ) # type: ignore + ) db.session.add(data_source) db.session.commit() @@ -211,7 +211,7 @@ def post(self, files_data, query_data): record_type="citation", num_records=n_citations, status=status, - ) # type: ignore + ) db.session.add(citations_import) db.session.commit() current_app.logger.info( diff --git a/colandr/api/v1/routes/citation_screenings.py b/colandr/api/v1/routes/citation_screenings.py index b8d563d0..75afab45 100644 --- a/colandr/api/v1/routes/citation_screenings.py +++ b/colandr/api/v1/routes/citation_screenings.py @@ -123,7 +123,7 @@ def post(self, id, json_data): stage="citation", status=json_data["status"], exclude_reasons=json_data["exclude_reasons"], - ) # type: ignore + ) study.screenings.add(screening) db.session.commit() diff --git a/colandr/api/v1/routes/citations.py b/colandr/api/v1/routes/citations.py index 815600e2..b57c5891 100644 --- a/colandr/api/v1/routes/citations.py +++ b/colandr/api/v1/routes/citations.py @@ -193,7 +193,7 @@ def post(self, json_data, query_data): if data_source is None: data_source = models.DataSource( source_type=source_type, source_name=source_name, source_url=source_url - ) # type: ignore + ) db.session.add(data_source) db.session.commit() current_app.logger.info("%s inserted %s", current_user, data_source) @@ -206,7 +206,7 @@ def post(self, json_data, query_data): data_source_id=data_source.id, # citation=citation, citation=json_data, - ) # type: ignore + ) if status is not None: study.citation_status = status db.session.add(study) diff --git a/colandr/api/v1/routes/data_extractions.py b/colandr/api/v1/routes/data_extractions.py index 8187782c..7a3a50c0 100644 --- a/colandr/api/v1/routes/data_extractions.py +++ b/colandr/api/v1/routes/data_extractions.py @@ -108,7 +108,7 @@ def put(self, id, json_data): ) labels_map = { - item["label"]: (item["field_type"], set(item.get("allowed_values", []))) # type: ignore + item["label"]: (item["field_type"], set(item.get("allowed_values", []))) for item in data_extraction_form[0] } # manually validate inputs, given data extraction form specification diff --git a/colandr/api/v1/routes/exports.py b/colandr/api/v1/routes/exports.py index 73e74cfd..40c3198d 100644 --- a/colandr/api/v1/routes/exports.py +++ b/colandr/api/v1/routes/exports.py @@ -370,7 +370,7 @@ def get(self, query_data): row.citation_status: row.count for row in db.session.execute(n_citations_by_status_stmt) } - n_citations_screened = sum(n_citations_by_status.values()) # type: ignore + n_citations_screened = sum(n_citations_by_status.values()) n_citations_excluded = n_citations_by_status.get("excluded", 0) n_fulltexts_by_status_stmt = ( @@ -383,7 +383,7 @@ def get(self, query_data): row.fulltext_status: row.count for row in db.session.execute(n_fulltexts_by_status_stmt) } - n_fulltexts_screened = sum(n_fulltexts_by_status.values()) # type: ignore + n_fulltexts_screened = sum(n_fulltexts_by_status.values()) n_fulltexts_excluded = n_fulltexts_by_status.get("excluded", 0) results = db.session.execute( diff --git a/colandr/api/v1/routes/fulltext_screenings.py b/colandr/api/v1/routes/fulltext_screenings.py index 6944b0c4..4ca29eda 100644 --- a/colandr/api/v1/routes/fulltext_screenings.py +++ b/colandr/api/v1/routes/fulltext_screenings.py @@ -142,7 +142,7 @@ def post(self, id, json_data): stage="fulltext", status=json_data["status"], exclude_reasons=json_data["exclude_reasons"], - ) # type: ignore + ) study.screenings.add(screening) db.session.commit() diff --git a/colandr/api/v1/routes/health.py b/colandr/api/v1/routes/health.py index 4420a7bf..45332353 100644 --- a/colandr/api/v1/routes/health.py +++ b/colandr/api/v1/routes/health.py @@ -23,7 +23,7 @@ class HealthAPI(MethodView): ) @bp.output({"message": af.fields.String()}) def get(self): - redis_conn = celery.current_app.backend.client # type: ignore + redis_conn = celery.current_app.backend.client assert isinstance(redis_conn, redis.client.Redis) # type guard try: _ = redis_conn.ping() diff --git a/colandr/api/v1/routes/reviews.py b/colandr/api/v1/routes/reviews.py index 00a0520b..81b5a6fe 100644 --- a/colandr/api/v1/routes/reviews.py +++ b/colandr/api/v1/routes/reviews.py @@ -161,7 +161,7 @@ def post(self, json_data): "pct": 100, } ] - review = models.Review(name=name, **json_data) # type: ignore + review = models.Review(name=name, **json_data) # TODO: do we want to allow admins to set other users as owners? review.review_user_assoc.append( models.ReviewUserAssoc(review, current_user, "owner") diff --git a/colandr/lib/fileio/ris.py b/colandr/lib/fileio/ris.py index 58bad1b8..85c917fb 100644 --- a/colandr/lib/fileio/ris.py +++ b/colandr/lib/fileio/ris.py @@ -90,7 +90,7 @@ def read(path_or_stream: t.BinaryIO | pathlib.Path) -> list[dict]: def parse(data: str) -> list[dict]: return rispy.loads( data, - implementation=rispy.parser.RisParser, # type: ignore + implementation=rispy.parser.RisParser, skip_unknown_tags=False, ) diff --git a/colandr/lib/models/deduper_v2.py b/colandr/lib/models/deduper_v2.py index 58ad93f9..391762ef 100644 --- a/colandr/lib/models/deduper_v2.py +++ b/colandr/lib/models/deduper_v2.py @@ -332,7 +332,7 @@ def _estimate_prob_two_records_match(self) -> None: splink.block_on("title", "author"), ] self.model.training.estimate_probability_two_random_records_match( - deterministic_rules, # type: ignore + deterministic_rules, recall=0.7, ) @@ -341,7 +341,7 @@ def _estimate_u_parameters( ) -> None: self.model.training.estimate_u_using_random_sampling( max_pairs=max_pairs, - seed=seed, # type: ignore + seed=seed, ) def _estimate_m_parameters(self) -> None: diff --git a/colandr/models.py b/colandr/models.py index abda2844..982bc74d 100644 --- a/colandr/models.py +++ b/colandr/models.py @@ -259,7 +259,7 @@ def num_citations_by_status(self, statuses: str | list[str]) -> dict[str, int]: ) # ensure every status is in result, with default value (0) result = {status: 0 for status in statuses} - result |= {row.citation_status: row.count for row in db.session.execute(stmt)} # type: ignore + result |= {row.citation_status: row.count for row in db.session.execute(stmt)} return result def num_fulltexts_by_status(self, statuses: str | list[str]) -> dict[str, int]: @@ -273,7 +273,7 @@ def num_fulltexts_by_status(self, statuses: str | list[str]) -> dict[str, int]: ) # ensure every status is in result, with default value (0) result = {status: 0 for status in statuses} - result |= {row.fulltext_status: row.count for row in db.session.execute(stmt)} # type: ignore + result |= {row.fulltext_status: row.count for row in db.session.execute(stmt)} return result @@ -769,7 +769,7 @@ def __repr__(self): @sa_event.listens_for(Review, "after_insert") def insert_review_plan(mapper, connection, target): - review_plan = ReviewPlan(id=target.id) # type: ignore + review_plan = ReviewPlan(id=target.id) connection.execute(sa.insert(ReviewPlan).values(id=target.id)) LOGGER.info("inserted %s and %s", target, review_plan) diff --git a/colandr/tasks.py b/colandr/tasks.py index fe41fa12..aa5c1785 100644 --- a/colandr/tasks.py +++ b/colandr/tasks.py @@ -31,7 +31,7 @@ def _get_redis_lock(lock_id: str, timeout: int = 120) -> redis.lock.Lock: def _get_redis_conn() -> redis.client.Redis: - redis_conn = current_celery_app.backend.client # type: ignore + redis_conn = current_celery_app.backend.client assert isinstance(redis_conn, redis.client.Redis) # type guard return redis_conn @@ -149,7 +149,7 @@ def deduplicate_citations(review_id: int): LOGGER.info( ": found %s duplicate clusters", review_id, - len(clustered_dupes), # type: ignore + len(clustered_dupes), ) # get *all* citation ids for this review, as well as included/excluded diff --git a/pyproject.toml b/pyproject.toml index 3a1a1811..14d46091 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ dev = [ "pytest-postgresql~=7.0", "SQLAlchemy-Utils~=0.42.0", # TODO: update ty once officially out of beta - "ty~=0.0.7", + "ty~=0.0.11", "ruff~=0.14.0", ] diff --git a/uv.lock b/uv.lock index 7024dd98..8c2d5d58 100644 --- a/uv.lock +++ b/uv.lock @@ -551,7 +551,7 @@ dev = [ { name = "pytest-postgresql", specifier = "~=7.0" }, { name = "ruff", specifier = "~=0.14.0" }, { name = "sqlalchemy-utils", specifier = "~=0.42.0" }, - { name = "ty", specifier = "~=0.0.7" }, + { name = "ty", specifier = "~=0.0.11" }, ] [[package]] @@ -2842,27 +2842,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b3/43/8be3ec2e2ce6119cff9ee3a207fae0cb4f2b4f8ed6534175130a32be24a7/ty-0.0.7.tar.gz", hash = "sha256:90e53b20b86c418ee41a8385f17da44cc7f916f96f9eee87593423ce8292ca72", size = 4826677, upload-time = "2025-12-24T21:28:49.136Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/56/fafa123acf955089306372add312f16e97aba61f7c4daf74e2bb9c350d23/ty-0.0.7-py3-none-linux_armv6l.whl", hash = "sha256:b30105bd9a0b064497111c50c206d5b6a032f29bcf39f09a12085c3009d72784", size = 9862360, upload-time = "2025-12-24T21:28:36.762Z" }, - { url = "https://files.pythonhosted.org/packages/71/f4/9c30ff498d9a60e24f16d26c0cf93cd03a119913ffa720a77149f02df06e/ty-0.0.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b4df20889115f3d5611a9d9cdedc222e3fd82b5fe87bb0a9f7246e53a23becc7", size = 9712866, upload-time = "2025-12-24T21:28:25.926Z" }, - { url = "https://files.pythonhosted.org/packages/43/84/e06a4a6e4011890027ffee41efbf261b1335103d09009d625ace7f1a60eb/ty-0.0.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f699589d8511e1e17c5a7edfc5f4a4e80f2a6d4a3932a0e9e3422fd32d731472", size = 9221692, upload-time = "2025-12-24T21:28:29.649Z" }, - { url = "https://files.pythonhosted.org/packages/7a/e9/ebb4192d3627730125d40ee403a17dc91bab59d69c3eff286453b3218d01/ty-0.0.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3eaec2d8aa153ee4bcc43b17a384d0f9e66177c8c8127be3358b6b8348b9e3b", size = 9710340, upload-time = "2025-12-24T21:28:55.148Z" }, - { url = "https://files.pythonhosted.org/packages/8f/4a/ec144458a9cfb324d5cb471483094e62e74d73179343dff262a5cca1a1e1/ty-0.0.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:177d160295e6a56bdf0b61f6120bc4502fff301d4d10855ba711c109aa7f37fb", size = 9670317, upload-time = "2025-12-24T21:28:43.096Z" }, - { url = "https://files.pythonhosted.org/packages/b6/94/fe7106fd5e2ac06b81fba7b785a6216774618edc3fda9e17f58efe3cede6/ty-0.0.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30518b95ab5cc83615794cca765a5fb86df39a0d9c3dadc0ab2d787ab7830008", size = 10096517, upload-time = "2025-12-24T21:28:23.667Z" }, - { url = "https://files.pythonhosted.org/packages/45/d9/db96ccfd663c96bdd4bb63db72899198c01445012f939477a5318a563f14/ty-0.0.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7867b3f75c2d9602cc6fb3b6d462580b707c2d112d4b27037142b0d01f8bfd03", size = 10996406, upload-time = "2025-12-24T21:28:39.134Z" }, - { url = "https://files.pythonhosted.org/packages/94/da/103915c08c3e6a14f95959614646fcdc9a240cd9a039fadbdcd086c819ee/ty-0.0.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878d45858e209b7904753fbc5155f4cb75dadc20a26bbb77614bfef31580f9ae", size = 10712829, upload-time = "2025-12-24T21:28:27.745Z" }, - { url = "https://files.pythonhosted.org/packages/47/c0/d9be417bc8e459e13e9698978579eec9868f91f4c5d6ef663249967fec8b/ty-0.0.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:651820b193901825afce40ae68f6a51cd64dbfa4b81a45db90061401261f25e4", size = 10486541, upload-time = "2025-12-24T21:28:45.17Z" }, - { url = "https://files.pythonhosted.org/packages/ad/09/d1858c66620d8ae566e021ad0d7168914b1568841f8fe9e439116ce6b440/ty-0.0.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f56a5a0c1c045863b1b70c358a392b3f73b8528c5c571d409f19dd465525e116", size = 10255312, upload-time = "2025-12-24T21:28:53.17Z" }, - { url = "https://files.pythonhosted.org/packages/b6/0a/78f75089db491fd5fcc13d2845a0b2771b7f7d377450c64c6616e9c227bc/ty-0.0.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:748218fbc1f7b7f1b9d14e77d4f3d7fec72af794417e26b0185bdb94153afe1c", size = 9696201, upload-time = "2025-12-24T21:28:57.345Z" }, - { url = "https://files.pythonhosted.org/packages/01/9e/b26e94832fd563fef6f77a4487affc77a027b0e53106422c66aafb37fa01/ty-0.0.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1ff80f3985a52a7358b9069b4a8d223e92cf312544a934a062d6d3a4fb6876b3", size = 9688907, upload-time = "2025-12-24T21:28:59.485Z" }, - { url = "https://files.pythonhosted.org/packages/5a/8f/cc48601fb92c964cf6c34277e0d947076146b7de47aa11b5dbae45e01ce7/ty-0.0.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a808910ce672ba4446699f4c021283208f58f988bcfc3bdbdfc6e005819d9ee0", size = 9829982, upload-time = "2025-12-24T21:28:34.429Z" }, - { url = "https://files.pythonhosted.org/packages/b5/af/7fa9c2bfa25865968bded637f7e71f1a712f4fbede88f487b6a9101ab936/ty-0.0.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2718fea5f314eda01703fb406ec89b1fc8710b3fc6a09bbd6f7a4f3502ddc889", size = 10361037, upload-time = "2025-12-24T21:28:47.027Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5b/1a6ff1495975cd1c02aa8d03bc5c9d8006eaeb8bf354446f88d70f0518fd/ty-0.0.7-py3-none-win32.whl", hash = "sha256:ae89bb8dc50deb66f34eab3113aa61ac5d7f85ecf16279e5918548085a89021c", size = 9295092, upload-time = "2025-12-24T21:28:51.041Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f6/47e9364635d048002354f84d2d0d6dfc9eb166dc67850739f88e1fec4fc5/ty-0.0.7-py3-none-win_amd64.whl", hash = "sha256:25bd20e3d4d0f07b422f9b42711ba24d28116031273bd23dbda66cec14df1c06", size = 10162816, upload-time = "2025-12-24T21:28:41.006Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f4/c4fc28410c4493982b7481fb23f62bacb02fd2912ebec3b9bc7de18bebb8/ty-0.0.7-py3-none-win_arm64.whl", hash = "sha256:c87d27484dba9fca0053b6a9eee47eecc760aab2bbb8e6eab3d7f81531d1ad0c", size = 9653112, upload-time = "2025-12-24T21:28:31.562Z" }, +version = "0.0.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/45/5ae578480168d4b3c08cf8e5eac3caf8eb7acdb1a06a9bed7519564bd9b4/ty-0.0.11.tar.gz", hash = "sha256:ebcbc7d646847cb6610de1da4ffc849d8b800e29fd1e9ebb81ba8f3fbac88c25", size = 4920340, upload-time = "2026-01-09T21:06:01.592Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/34/b1d05cdcd01589a8d2e63011e0a1e24dcefdc2a09d024fee3e27755963f6/ty-0.0.11-py3-none-linux_armv6l.whl", hash = "sha256:68f0b8d07b0a2ea7ec63a08ba2624f853e4f9fa1a06fce47fb453fa279dead5a", size = 9521748, upload-time = "2026-01-09T21:06:13.221Z" }, + { url = "https://files.pythonhosted.org/packages/43/21/f52d93f4b3784b91bfbcabd01b84dc82128f3a9de178536bcf82968f3367/ty-0.0.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cbf82d7ef0618e9ae3cc3c37c33abcfa302c9b3e3b8ff11d71076f98481cb1a8", size = 9454903, upload-time = "2026-01-09T21:06:42.363Z" }, + { url = "https://files.pythonhosted.org/packages/ad/01/3a563dba8b1255e474c35e1c3810b7589e81ae8c41df401b6a37c8e2cde9/ty-0.0.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:121987c906e02264c3b511b95cb9f8a3cdd66f3283b8bbab678ca3525652e304", size = 8823417, upload-time = "2026-01-09T21:06:26.315Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b1/99b87222c05d3a28fb7bbfb85df4efdde8cb6764a24c1b138f3a615283dd/ty-0.0.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:999390b6cc045fe5e1b3da1c2c9ae8e8c0def23b69455e7c9191ba9ffd747023", size = 9290785, upload-time = "2026-01-09T21:05:59.028Z" }, + { url = "https://files.pythonhosted.org/packages/3d/9f/598809a8fff2194f907ba6de07ac3d7b7788342592d8f8b98b1b50c2fb49/ty-0.0.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed504d78eb613c49be3c848f236b345b6c13dc6bcfc4b202790a60a97e1d8f35", size = 9359392, upload-time = "2026-01-09T21:06:37.459Z" }, + { url = "https://files.pythonhosted.org/packages/71/3e/aeea2a97b38f3dcd9f8224bf83609848efa4bc2f484085508165567daa7b/ty-0.0.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fedc8b43cc8a9991e0034dd205f957a8380dd29bfce36f2a35b5d321636dfd9", size = 9852973, upload-time = "2026-01-09T21:06:21.245Z" }, + { url = "https://files.pythonhosted.org/packages/72/40/86173116995e38f954811a86339ac4c00a2d8058cc245d3e4903bc4a132c/ty-0.0.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:0808bdfb7efe09881bf70249b85b0498fb8b75fbb036ce251c496c20adb10075", size = 10796113, upload-time = "2026-01-09T21:06:16.034Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/97c92c401dacae9baa3696163ebe8371635ebf34ba9fda781110d0124857/ty-0.0.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07185b3e38b18c562056dfbc35fb51d866f872977ea1ebcd64ca24a001b5b4f1", size = 10432137, upload-time = "2026-01-09T21:06:07.498Z" }, + { url = "https://files.pythonhosted.org/packages/18/10/9ab43f3cfc5f7792f6bc97620f54d0a0a81ef700be84ea7f6be330936a99/ty-0.0.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5c72f1ada8eb5be984502a600f71d1a3099e12fb6f3c0607aaba2f86f0e9d80", size = 10240520, upload-time = "2026-01-09T21:06:34.823Z" }, + { url = "https://files.pythonhosted.org/packages/74/18/8dd4fe6df1fd66f3e83b4798eddb1d8482d9d9b105f25099b76703402ebb/ty-0.0.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25f88e8789072830348cb59b761d5ced70642ed5600673b4bf6a849af71eca8b", size = 9973340, upload-time = "2026-01-09T21:06:39.657Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0b/fb2301450cf8f2d7164944d6e1e659cac9ec7021556cc173d54947cf8ef4/ty-0.0.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f370e1047a62dcedcd06e2b27e1f0b16c7f8ea2361d9070fcbf0d0d69baaa192", size = 9262101, upload-time = "2026-01-09T21:06:28.989Z" }, + { url = "https://files.pythonhosted.org/packages/f7/8c/d6374af023541072dee1c8bcfe8242669363a670b7619e6fffcc7415a995/ty-0.0.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:52be34047ed6177bfcef9247459a767ec03d775714855e262bca1fb015895e8a", size = 9382756, upload-time = "2026-01-09T21:06:24.097Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/edd1e63ffa8d49d720c475c2c1c779084e5efe50493afdc261938705d10a/ty-0.0.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b9e5762ccb3778779378020b8d78f936b3f52ea83f18785319cceba3ae85d8e6", size = 9553944, upload-time = "2026-01-09T21:06:18.426Z" }, + { url = "https://files.pythonhosted.org/packages/35/cd/4afdb0d182d23d07ff287740c4954cc6dde5c3aed150ec3f2a1d72b00f71/ty-0.0.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9334646ee3095e778e3dbc45fdb2bddfc16acc7804283830ad84991ece16dd7", size = 10060365, upload-time = "2026-01-09T21:06:45.083Z" }, + { url = "https://files.pythonhosted.org/packages/d1/94/a009ad9d8b359933cfea8721c689c0331189be28650d74dcc6add4d5bb09/ty-0.0.11-py3-none-win32.whl", hash = "sha256:44cfb7bb2d6784bd7ffe7b5d9ea90851d9c4723729c50b5f0732d4b9a2013cfc", size = 9040448, upload-time = "2026-01-09T21:06:32.241Z" }, + { url = "https://files.pythonhosted.org/packages/df/04/5a5dfd0aec0ea99ead1e824ee6e347fb623c464da7886aa1e3660fb0f36c/ty-0.0.11-py3-none-win_amd64.whl", hash = "sha256:1bb205db92715d4a13343bfd5b0c59ce8c0ca0daa34fb220ec9120fc66ccbda7", size = 9780112, upload-time = "2026-01-09T21:06:04.69Z" }, + { url = "https://files.pythonhosted.org/packages/ad/07/47d4fccd7bcf5eea1c634d518d6cb233f535a85d0b63fcd66815759e2fa0/ty-0.0.11-py3-none-win_arm64.whl", hash = "sha256:4688bd87b2dc5c85da277bda78daba14af2e66f3dda4d98f3604e3de75519eba", size = 9194038, upload-time = "2026-01-09T21:06:10.152Z" }, ] [[package]]