diff --git a/.github/workflows/pyharp.yml b/.github/workflows/harp.yml similarity index 56% rename from .github/workflows/pyharp.yml rename to .github/workflows/harp.yml index ee11652..74ad5a5 100644 --- a/.github/workflows/pyharp.yml +++ b/.github/workflows/harp.yml @@ -1,10 +1,10 @@ -name: pyharp test suite +name: harp on: pull_request: push: branches: - - master + - main release: types: [published] workflow_dispatch: @@ -38,8 +38,8 @@ jobs: - name: Run ruff check run: uv run ruff check - - name: Run ty - run: uv run ty check + - name: Run pyright + run: uv run pyright - name: Run pytest run: uv run pytest --cov harp @@ -47,49 +47,33 @@ jobs: - name: Build run: uv build --all-packages - prepare-release: + # ---- Release build ---- + build-release: runs-on: ubuntu-latest - name: Set version from release tag + name: Build release distributions needs: tests if: github.event_name == 'release' && github.event.action == 'published' - outputs: - version: ${{ steps.get_version.outputs.version }} steps: - uses: actions/checkout@v7 - with: - fetch-depth: 0 - ref: master - uses: astral-sh/setup-uv@v8.3.2 with: enable-cache: true - - name: Extract version from tag - id: get_version - shell: bash - run: | - version=$(echo "${{ github.event.release.tag_name }}" | sed 's/^v//') - echo "version=$version" >> $GITHUB_OUTPUT - echo "Setting version to: $version" - - - name: Validate version format - run: uv version ${{ steps.get_version.outputs.version }} --dry-run + - name: Build + env: + SETUPTOOLS_SCM_PRETEND_VERSION: ${{ github.event.release.tag_name }} + run: uv build --all-packages - - name: Set version across all workspace packages + - name: Verify setuptools-scm applied the release tag shell: bash run: | - version="${{ steps.get_version.outputs.version }}" - # Discover every workspace member (root + src/packages/*) by its declared - # package name so all versions move in lock-step with the release tag. - mapfile -t pkgs < <(grep -h '^name = ' pyproject.toml src/packages/*/pyproject.toml | sed -E 's/^name = "(.*)"/\1/') - for pkg in "${pkgs[@]}"; do - echo "Setting $pkg -> $version" - uv version --package "$pkg" "$version" - done - - - name: Build - run: uv build --all-packages + ls -1 dist/ + if ls dist/*-0.0.0.tar.gz >/dev/null 2>&1; then + echo "::error::Built version 0.0.0, so setuptools-scm did not apply the tag. Check that every pyproject.toml still declares a tool.setuptools_scm table." + exit 1 + fi - name: Remove internal-only packages from dist shell: bash @@ -101,27 +85,17 @@ jobs: name: dist path: dist/ - # This step seems a bit complicated, but it allows for idempotent commits, - # so that if the release tag is moved to a new commit, - # the version will be updated and committed again. - - name: Commit version changes - shell: bash - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git add . - git diff --cached --quiet && echo "No changes to commit" && exit 0 - git commit -m "Set version ${{ steps.get_version.outputs.version }} [skip ci]" - git push origin master - # Move the release tag to point to this new commit. - git tag -fa "${{ github.event.release.tag_name }}" -m "${{ github.event.release.tag_name }}" - git push origin "${{ github.event.release.tag_name }}" --force - # ---- Publish to PyPI ---- publish-to-pypi: runs-on: ubuntu-latest name: Publish to PyPI - needs: prepare-release + needs: build-release + environment: pypi + permissions: + # uv publish exchanges this token with PyPI trusted publishing. + id-token: write + # action-gh-release attaches the distributions to the release. + contents: write steps: - name: Download wheels artifact uses: actions/download-artifact@v8 @@ -134,7 +108,7 @@ jobs: enable-cache: true - name: Publish to PyPI - run: uv publish --token ${{ secrets.PYPI_TOKEN }} + run: uv publish - name: Upload wheels to GitHub release uses: softprops/action-gh-release@v3 @@ -145,13 +119,13 @@ jobs: build-docs: name: Build and deploy documentation to GitHub Pages runs-on: ubuntu-latest - needs: prepare-release + needs: build-release if: github.event_name == 'release' && !github.event.release.prerelease steps: - name: Checkout uses: actions/checkout@v7 with: - ref: master + fetch-depth: 0 - name: Install uv uses: astral-sh/setup-uv@v8.3.2 diff --git a/pyproject.toml b/pyproject.toml index f01826f..c7d9169 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harp" -version = "0.0.0" +dynamic = ["version"] description = "Library for data acquisition and control of devices implementing the Harp protocol." authors = [{ name = "harp-tech", email = "contact@harp-tech.org" }] license = "MIT" @@ -44,13 +44,14 @@ Documentation = "https://harp-tech.org/pyharp/" Changelog = "https://github.com/harp-tech/pyharp/releases" [build-system] -requires = ["uv_build>=0.9.5"] -build-backend = "uv_build" +requires = ["setuptools>=77", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" -[tool.uv.build-backend] -module-name = "harp" -module-root = "src" -namespace = true +[tool.setuptools_scm] + +[tool.setuptools.packages.find] +where = ["src"] +include = ["harp*"] [tool.uv.sources] harp-protocol = { workspace = true } @@ -77,10 +78,10 @@ docs = [ dev = [ "codespell>=2.3.0", + "pyright>=1.1.411", "pytest>=8.3.5", "pytest-cov>=6.1.1", "ruff>=0.11.0", - "ty>=0.0.0", "harp-protocol", "harp-device", "harp-serial", @@ -109,11 +110,9 @@ python_files = [ line-length = 100 target-version = "py311" -[tool.ty.environment] -python-version = "3.11" -extra-paths = ["tests"] - -[tool.ty.src] +[tool.pyright] +pythonVersion = "3.11" +typeCheckingMode = "standard" include = [ "src/packages/harp-protocol/src", "src/packages/harp-device/src", @@ -121,5 +120,26 @@ include = [ "src/packages/harp-data/src", ] exclude = [ + "**/node_modules", + "**/__pycache__", + "**/.*", + ".venv", "tests", -] \ No newline at end of file +] +venvPath = "." +venv = ".venv" +reportImportCycles = "error" +reportUnusedImport = "error" +reportUnusedClass = "error" +reportUnusedFunction = "error" +reportUnusedVariable = "error" +reportDuplicateImport = "error" +reportWildcardImportFromLibrary = "error" +reportCallInDefaultInitializer = "error" +reportUnnecessaryIsInstance = "error" +reportUnnecessaryCast = "error" +reportUnnecessaryContains = "error" +reportAssertAlwaysTrue = "error" +reportSelfClsParameterName = "error" +reportUnusedExpression = "error" +reportMatchNotExhaustive = "error" diff --git a/src/packages/harp-benchmarks/pyproject.toml b/src/packages/harp-benchmarks/pyproject.toml index 9caa8f0..7ff7391 100644 --- a/src/packages/harp-benchmarks/pyproject.toml +++ b/src/packages/harp-benchmarks/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harp-benchmarks" -version = "0.1.0" +dynamic = ["version"] description = "Internal parsing-speed benchmarks for the Harp register/payload API." requires-python = ">=3.11" # INTERNAL ONLY — never published to PyPI. The "Private :: Do Not Upload" trove @@ -19,9 +19,12 @@ harp-benchmark = "harp.benchmarks.benchmark:main" harp-benchmark-generate = "harp.benchmarks.generate:main" [build-system] -requires = ["uv_build>=0.9.5"] -build-backend = "uv_build" +requires = ["setuptools>=77", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" -[tool.uv.build-backend] -module-name = "harp.benchmarks" -module-root = "src" +[tool.setuptools_scm] +root = "../../.." + +[tool.setuptools.packages.find] +where = ["src"] +include = ["harp*"] diff --git a/src/packages/harp-data/pyproject.toml b/src/packages/harp-data/pyproject.toml index 38f8fab..47bdfff 100644 --- a/src/packages/harp-data/pyproject.toml +++ b/src/packages/harp-data/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harp-data" -version = "0.1.0" +dynamic = ["version"] description = "Load Harp device data into pandas DataFrames" requires-python = ">=3.11" dependencies = [ @@ -10,9 +10,12 @@ dependencies = [ ] [build-system] -requires = ["uv_build>=0.9.5"] -build-backend = "uv_build" +requires = ["setuptools>=77", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" -[tool.uv.build-backend] -module-name = "harp.data" -module-root = "src" +[tool.setuptools_scm] +root = "../../.." + +[tool.setuptools.packages.find] +where = ["src"] +include = ["harp*"] diff --git a/src/packages/harp-device/pyproject.toml b/src/packages/harp-device/pyproject.toml index 3101f3c..54e8578 100644 --- a/src/packages/harp-device/pyproject.toml +++ b/src/packages/harp-device/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harp-device" -version = "0.1.0" +dynamic = ["version"] description = "Transport-agnostic Harp device protocol layer" requires-python = ">=3.11" dependencies = [ @@ -8,9 +8,12 @@ dependencies = [ ] [build-system] -requires = ["uv_build>=0.9.5"] -build-backend = "uv_build" +requires = ["setuptools>=77", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" -[tool.uv.build-backend] -module-name = "harp.device" -module-root = "src" \ No newline at end of file +[tool.setuptools_scm] +root = "../../.." + +[tool.setuptools.packages.find] +where = ["src"] +include = ["harp*"] diff --git a/src/packages/harp-protocol/pyproject.toml b/src/packages/harp-protocol/pyproject.toml index f129a69..c903d03 100644 --- a/src/packages/harp-protocol/pyproject.toml +++ b/src/packages/harp-protocol/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harp-protocol" -version = "0.4.0" +dynamic = ["version"] description = "Library with the base types for Harp protocol usage." authors = [{ name = "harp-tech", email = "contact@harp-tech.org" }] license = "MIT" @@ -12,9 +12,12 @@ dependencies = [ ] [build-system] -requires = ["uv_build>=0.9.5"] -build-backend = "uv_build" +requires = ["setuptools>=77", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" -[tool.uv.build-backend] -module-name = "harp.protocol" -module-root = "src" +[tool.setuptools_scm] +root = "../../.." + +[tool.setuptools.packages.find] +where = ["src"] +include = ["harp*"] diff --git a/src/packages/harp-protocol/src/harp/protocol/_payload.py b/src/packages/harp-protocol/src/harp/protocol/_payload.py index 9960532..22e5c74 100644 --- a/src/packages/harp-protocol/src/harp/protocol/_payload.py +++ b/src/packages/harp-protocol/src/harp/protocol/_payload.py @@ -146,7 +146,7 @@ def __get__(self, obj: "PayloadBase | None", owner: object = None) -> Any: if self._mask is not None: raw = (obj._arr[self._slot] & self._mask) >> self._shift return self._converter.decode_scalar(self._converter.dtype.type(raw)) - return self._converter.decode_scalar(obj._arr[self._slot]) # ty: ignore[invalid-argument-type] + return self._converter.decode_scalar(obj._arr[self._slot]) # pyright: ignore[reportArgumentType] def _to_batch(self) -> "_FieldBatch[T]": """Returns the metadata for the corresponding Batch type""" @@ -599,7 +599,7 @@ def _build_struct_dtype( else: itemsize = max(slot.byte_offset + slot.dtype.itemsize for slot in slots.values()) _validate_no_overlap(cls, slots, itemsize) - return np.dtype( # ty: ignore[no-matching-overload] + return np.dtype( { "names": list(slots), "formats": [slot.dtype for slot in slots.values()], @@ -671,7 +671,7 @@ def __init__(self, *args: object, **kwargs: object) -> None: for attr_name, value in kwargs.items(): desc = cls._mro_descriptor(attr_name) if isinstance(desc, Field) and desc._mask is None: # whole-element Field - desc._converter.encode_into(arr[desc._slot], value) # ty: ignore[invalid-argument-type] + desc._converter.encode_into(arr[desc._slot], value) elif isinstance(desc, (GroupMask, BitMask, Field)): # masked sub-field -> encode, shift, merge into the shared slot mask = desc._mask @@ -951,7 +951,7 @@ def __init_subclass__( f"multi-field payloads." ) cls._root = True - super().__init_subclass__(**kwargs) # ty: ignore[invalid-argument-type] + super().__init_subclass__(**kwargs) # pyright: ignore[reportArgumentType] return # Raw scalar slot required, unless a Batch twin / array concrete supplies dtype. if scalar_dtype is None and "_batch_of" not in kwargs and "dtype" not in cls.__dict__: @@ -963,7 +963,7 @@ def __init_subclass__( if scalar_dtype is not None: cls.dtype = np.dtype(scalar_dtype) cls._repr_fields = () - super().__init_subclass__(**kwargs) # ty: ignore[invalid-argument-type] + super().__init_subclass__(**kwargs) # pyright: ignore[reportArgumentType] def __init__(self, value: object = _MISSING_INIT, /, **kwargs: object) -> None: # type: ignore[override] if type(self)._root: diff --git a/src/packages/harp-protocol/src/harp/protocol/_payload_converters.py b/src/packages/harp-protocol/src/harp/protocol/_payload_converters.py index 809fac5..31d530a 100644 --- a/src/packages/harp-protocol/src/harp/protocol/_payload_converters.py +++ b/src/packages/harp-protocol/src/harp/protocol/_payload_converters.py @@ -181,7 +181,7 @@ def decode_batch(self, view: NDArray[np.generic]) -> Any: [bytes(row).rstrip(b"\x00").decode(self._encoding) for row in view], dtype=object, ) - return view.reshape(-1, self._length).view(f"S{self._length}").reshape(-1).astype(str) # ty: ignore[no-matching-overload] + return view.reshape(-1, self._length).view(f"S{self._length}").reshape(-1).astype(str) def encode_into(self, view: NDArray[np.generic], value: str) -> None: encoded = value.encode(self._encoding)[: self._length] diff --git a/src/packages/harp-serial/pyproject.toml b/src/packages/harp-serial/pyproject.toml index 4bc7480..9799e3d 100644 --- a/src/packages/harp-serial/pyproject.toml +++ b/src/packages/harp-serial/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "harp-serial" -version = "0.1.0" +dynamic = ["version"] description = "Serial transport for Harp devices" requires-python = ">=3.11" dependencies = [ @@ -10,9 +10,12 @@ dependencies = [ ] [build-system] -requires = ["uv_build>=0.9.5"] -build-backend = "uv_build" +requires = ["setuptools>=77", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" -[tool.uv.build-backend] -module-name = "harp.serial" -module-root = "src" +[tool.setuptools_scm] +root = "../../.." + +[tool.setuptools.packages.find] +where = ["src"] +include = ["harp*"] diff --git a/uv.lock b/uv.lock index d465ba6..a3002e6 100644 --- a/uv.lock +++ b/uv.lock @@ -304,7 +304,6 @@ wheels = [ [[package]] name = "harp" -version = "0.0.0" source = { editable = "." } dependencies = [ { name = "harp-data" }, @@ -321,10 +320,10 @@ dev = [ { name = "harp-device" }, { name = "harp-protocol" }, { name = "harp-serial" }, + { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "ruff" }, - { name = "ty" }, { name = "typing-extensions" }, ] docs = [ @@ -353,10 +352,10 @@ dev = [ { name = "harp-device", editable = "src/packages/harp-device" }, { name = "harp-protocol", editable = "src/packages/harp-protocol" }, { name = "harp-serial", editable = "src/packages/harp-serial" }, + { name = "pyright", specifier = ">=1.1.411" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "pytest-cov", specifier = ">=6.1.1" }, { name = "ruff", specifier = ">=0.11.0" }, - { name = "ty", specifier = ">=0.0.0" }, { name = "typing-extensions", specifier = ">=4.15.0" }, ] docs = [ @@ -371,7 +370,6 @@ docs = [ [[package]] name = "harp-benchmarks" -version = "0.1.0" source = { editable = "src/packages/harp-benchmarks" } dependencies = [ { name = "harp-data" }, @@ -391,7 +389,6 @@ requires-dist = [ [[package]] name = "harp-data" -version = "0.1.0" source = { editable = "src/packages/harp-data" } dependencies = [ { name = "harp-protocol" }, @@ -409,7 +406,6 @@ requires-dist = [ [[package]] name = "harp-device" -version = "0.1.0" source = { editable = "src/packages/harp-device" } dependencies = [ { name = "harp-protocol" }, @@ -420,7 +416,6 @@ requires-dist = [{ name = "harp-protocol", editable = "src/packages/harp-protoco [[package]] name = "harp-protocol" -version = "0.4.0" source = { editable = "src/packages/harp-protocol" } dependencies = [ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, @@ -436,7 +431,6 @@ requires-dist = [ [[package]] name = "harp-serial" -version = "0.1.0" source = { editable = "src/packages/harp-serial" } dependencies = [ { name = "harp-device" }, @@ -725,6 +719,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/fc/10ab7e80650a9c9e8f4f1105f8c8e73567f88ed0c06ada589ab81d38687c/mkdocstrings_python-2.0.5-py3-none-any.whl", hash = "sha256:30c837bbff016549f659fcba6539ac351303f0fd7e713c89a040611072236e9d", size = 104951, upload-time = "2026-06-19T10:41:07.378Z" }, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + [[package]] name = "numpy" version = "2.4.6" @@ -996,6 +999,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d6/54/da572c98c0b77626a91b5d3b89f0231d8bff5125c225420908632f8b342d/pymdown_extensions-11.0.1-py3-none-any.whl", hash = "sha256:db3943a62bab7e03af1364f0c4083e64b91fb097675a4b6cceccfbe9a77e5eb2", size = 269455, upload-time = "2026-07-02T17:59:21.271Z" }, ] +[[package]] +name = "pyright" +version = "1.1.411" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/ab/265f7dc69d28113ebba19092e57b075f41543b2ed048429c5f56e2b88eac/pyright-1.1.411.tar.gz", hash = "sha256:d885a0551f2e763b089a02702174e7f4ba77548cddabc972ab86d1f7f1b0f998", size = 4112861, upload-time = "2026-06-25T02:14:06.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/49/385be530a6a5b78d1cbcd5c2e38debc8959a2fc6bdb716f4e581002979fc/pyright-1.1.411-py3-none-any.whl", hash = "sha256:dc7c72a8e2700c55baa127554040e067041ea53ccfd50bf96308cc4291c7d5d9", size = 6181526, upload-time = "2026-06-25T02:14:04.691Z" }, +] + [[package]] name = "pyserial" version = "3.5" @@ -1217,31 +1233,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, ] -[[package]] -name = "ty" -version = "0.0.60" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/6e/1ab6f2727622d38ddfb2a7f994209b3087190b76885e2f754dbb6e58e0c9/ty-0.0.60.tar.gz", hash = "sha256:ebd7517d1aa8d8c3793cbf03c263679a42b939eca650df583234f92a5eb5837a", size = 6189323, upload-time = "2026-07-16T10:18:14.124Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/fc/c82d30b753dfb9d83ac34568478d9487bc42e1e79241a22b57f4b28fabee/ty-0.0.60-py3-none-linux_armv6l.whl", hash = "sha256:0842270c2e10d8416ca9f8aca1357c827efc5212300fbbc709e186e66fc7f9da", size = 11831443, upload-time = "2026-07-16T10:17:34.912Z" }, - { url = "https://files.pythonhosted.org/packages/47/d4/09e386d816076d1d90c57baee7f607e1475b625bb9f9279b28302da22f18/ty-0.0.60-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f49d024571cd7e569081638ab0ccc9e6795d642c6b9208addecc5e487364f0a3", size = 11624536, upload-time = "2026-07-16T10:17:37.445Z" }, - { url = "https://files.pythonhosted.org/packages/f2/1e/e527e29fd9a3d41ddc419a43b37927c42a0d8ee4ca8a148ce4e022328f2f/ty-0.0.60-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5b006c9a793d735ff808999775bbc0f07f91de50e931799f17322440cd9d055f", size = 11032920, upload-time = "2026-07-16T10:17:39.695Z" }, - { url = "https://files.pythonhosted.org/packages/79/e0/0f294bf2521f2836ba1a4682d4301c2038e4dfedee3a70df8f6005694978/ty-0.0.60-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:233033de40a0722420380d40f0eeebe8eb0d3afeeab3a6e7b1388e2caf1a017c", size = 11575334, upload-time = "2026-07-16T10:17:41.644Z" }, - { url = "https://files.pythonhosted.org/packages/0c/8c/682e46e29165d9c94829576687092a8ea4556dd5882b11edd32bc7f4b534/ty-0.0.60-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61bcbd67f653ae2966d0e423879127907c6f8b567d64b6db93cb8f6cca7045ed", size = 11659354, upload-time = "2026-07-16T10:17:43.815Z" }, - { url = "https://files.pythonhosted.org/packages/2c/d3/6baf7eb3cf9459416032285a10ffe1309f7e255e3f1974372bc41d44cfd3/ty-0.0.60-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b72e44a0be08ad778693d49b00b03bb784c1465d8265b3b392b355b34257a437", size = 12293710, upload-time = "2026-07-16T10:17:46.032Z" }, - { url = "https://files.pythonhosted.org/packages/5c/f7/b287d78c93449fc5153891ad040be55e0718711ae7c0f2c54d3b88dd4d00/ty-0.0.60-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13b89f03a76f149350617345ae33bd332d2c6023dc5d560dd10a4f0210fba13a", size = 12837636, upload-time = "2026-07-16T10:17:48.453Z" }, - { url = "https://files.pythonhosted.org/packages/e6/f2/3d8c77ca5f836fb6280231030df5a4504476a38d4554330d2be42125888e/ty-0.0.60-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30db8ebf6715da30afac62311ac773bf436ed503ed989cc9d4be4d3bdc3cbd43", size = 12383469, upload-time = "2026-07-16T10:17:50.567Z" }, - { url = "https://files.pythonhosted.org/packages/e8/98/958047501d74f9d0df357df28fea1de2afa18f1e6b2dc4a3ea4975c83e14/ty-0.0.60-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ecd6dc686f90c3bfe22c9b435e4a5325f5066f706bfe95c1616ef425b1f0654", size = 12132334, upload-time = "2026-07-16T10:17:52.718Z" }, - { url = "https://files.pythonhosted.org/packages/8c/8f/b2853634da28aac01ea0ba8ef7695dc3d6c9aaf9c63dfdcf7c7435bc6c25/ty-0.0.60-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2292124558d839e31afd9829b11dda726ce65096b1feb80d14a1ddc7ccb0f28", size = 12359945, upload-time = "2026-07-16T10:17:55.143Z" }, - { url = "https://files.pythonhosted.org/packages/bd/ae/bfecc71fcf1b833117bfb0efb76e14f5772874775274115ac94f64c5ba7a/ty-0.0.60-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:78812ec03da2c3141030ee1717fb09f0845eb552a77e79680c1cca598861ddaf", size = 11534812, upload-time = "2026-07-16T10:17:57.165Z" }, - { url = "https://files.pythonhosted.org/packages/d6/23/1236c1ab9cd6b2daba9c558c9dcf04644f7b636f3bab3c15e74c7f80f5e1/ty-0.0.60-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a92c1dbc48f8b414ddcf199c1119054de00f8ee995528fa9d9c2e4ddd13cd0c1", size = 11677591, upload-time = "2026-07-16T10:17:59.747Z" }, - { url = "https://files.pythonhosted.org/packages/49/49/86c9230d9a8bf1755ca4b0165e7cb6bdfa2e495f0a105b24c6a27b5a542c/ty-0.0.60-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2c367d5ffe545004f0603e5599614062230678eeab980acd32850a7c19d60fc9", size = 11901279, upload-time = "2026-07-16T10:18:02.301Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0a/0c2e9904362d9c63700116feb192a3e10122eef49a417cde84068b2562fd/ty-0.0.60-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a94b1f99aa8f8ca5879341a5351af708b85ce19c7494db320c719a1e129d08e8", size = 12230659, upload-time = "2026-07-16T10:18:04.384Z" }, - { url = "https://files.pythonhosted.org/packages/84/4d/39c414f6b4bc944b2bac19c181438ee393c8eb34e9e21d97e5a148745f95/ty-0.0.60-py3-none-win32.whl", hash = "sha256:5ea2c85249581fb0060b9ff63b5313330f48930b2e50f6c85a5a322701626cc5", size = 11175286, upload-time = "2026-07-16T10:18:06.523Z" }, - { url = "https://files.pythonhosted.org/packages/a7/08/fdf4072d96c71b65fe98c9ca2ea5b3d2d0f6e20873bb2639e8c7827bd5f6/ty-0.0.60-py3-none-win_amd64.whl", hash = "sha256:15d83c3d793cb07840b8888c084cc2c49eb8acc29456a2fcdfbfd509c82526e5", size = 12249240, upload-time = "2026-07-16T10:18:08.701Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a2/83a496d717f712f894cf26690bcd9465ca23cd1c9139cac669d9cca45d14/ty-0.0.60-py3-none-win_arm64.whl", hash = "sha256:32e63228c3d21ddb192385aaf54501f19478be7b764f7572014d5110e53a9085", size = 11620140, upload-time = "2026-07-16T10:18:11.316Z" }, -] - [[package]] name = "typing-extensions" version = "4.16.0"