diff --git a/AGENTS.md b/AGENTS.md index 264ee03..8726366 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -213,6 +213,15 @@ When user requests commit message generation: - The current MSRV and pinned contributor/CI toolchain are Rust 1.98.0. Keep `Cargo.toml`, `rust-toolchain.toml`, and `clippy.toml` aligned when that baseline changes deliberately. +- Rust's `f64::algebraic_*` operations are forbidden in all repository-owned + Rust code, including tests, examples, and benchmarks. + Their unspecified reassociation, precision, and special-value behavior can + invalidate defined operation order, error bounds, non-finite classification, + exact fallbacks, and reproducibility. Ordinary operators remain allowed. + Deliberate fused multiply-add through `f64::mul_add` is explicitly allowed; + existing numerical kernels and error bounds may rely on its single-rounding + evaluation. Any fast-math design requires a separate issue, opt-in contract, + correctness analysis, and benchmark evidence. - Prefer borrowed APIs by default: take references (`&T`, `&mut T`, `&[T]`) as arguments and return borrowed views (`&T`, `&[T]`) when possible. Only take ownership or return `Vec`/allocated data when required. diff --git a/Cargo.lock b/Cargo.lock index a7ae053..26897d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,7 +91,7 @@ checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1204,7 +1204,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 7cc77a7..8ec78c7 100644 --- a/README.md +++ b/README.md @@ -42,22 +42,28 @@ With `features = ["exact"]`, stored binary64 inputs are lifted losslessly to rationals for exact determinant signs, determinant values, and solves. Exactness starts at the stored values and cannot recover information rounded away before construction. See the -[mathematical basis](https://github.com/acgetchell/la-stack/blob/main/docs/mathematical_basis.md) +[mathematical basis](https://github.com/acgetchell/la-stack/blob/v0.4.5/docs/mathematical_basis.md) for the algorithms, validity boundaries, and supporting references. ## ✨ Design goals -- ✅ `Copy` types where possible -- ✅ Const-generic storage (no dynamically sized matrix or vector representation) - ✅ `const fn` where possible (compile-time evaluation of determinants, dot products, etc.) -- ✅ Explicit algorithms (LU, solve, determinant) +- ✅ Const-generic storage (no dynamically sized matrix or vector representation) +- ✅ `Copy` types where possible +- ✅ Defined binary64 arithmetic semantics: Rust's `f64::algebraic_*` + operations are forbidden because their unspecified reassociation, precision, + and special-value behavior is incompatible with the crate's error bounds, + non-finite classification, exact fallbacks, and reproducibility contract; + deliberate `f64::mul_add` remains allowed for its defined single-rounding + semantics - ✅ Error-bounded f64 determinant filtering plus optional exact signs (`det_errbound`, `det_sign_exact`) - ✅ Exact determinant values and linear solves via optional arbitrary-precision arithmetic (`det_exact`, `solve_exact`, strict/rounded f64 conversions) -- ✅ No runtime dependencies by default (optional features may add deps) +- ✅ Explicit algorithms (LU, solve, determinant) - ✅ Inline, stack-backed storage for core types; optional arbitrary-precision exact values allocate as required +- ✅ No runtime dependencies by default (optional features may add deps) - ✅ `unsafe` forbidden See [CHANGELOG.md](https://github.com/acgetchell/la-stack/blob/v0.4.5/CHANGELOG.md) @@ -67,11 +73,14 @@ for current release planning. ## 🚫 Anti-goals -- Bare-metal performance: see [`blas-src`](https://crates.io/crates/blas-src), - [`lapack-src`](https://crates.io/crates/lapack-src), or [`openblas-src`](https://crates.io/crates/openblas-src) +- Alternate floating-point scalar families: `la-stack` supports `f64` and optional exact arithmetic, not `f32` / `f16` APIs +- Bare-metal performance: use [`blas`](https://crates.io/crates/blas) or + [`lapack`](https://crates.io/crates/lapack) with a native backend selected + through [`blas-src`](https://crates.io/crates/blas-src), + [`lapack-src`](https://crates.io/crates/lapack-src), or + [`openblas-src`](https://crates.io/crates/openblas-src) - Broad general-purpose linear algebra: use [`nalgebra`](https://crates.io/crates/nalgebra) - Large matrices/dimensions with parallelism: use [`faer`](https://crates.io/crates/faer) -- Alternate floating-point scalar families: `la-stack` supports `f64` and optional exact arithmetic, not `f32` / `f16` APIs ## ✅ Use this crate when @@ -98,6 +107,8 @@ cases better served by broader linear-algebra libraries. ## 🚀 Quickstart +The minimum supported Rust version (MSRV) is 1.98.0. + Add this to your `Cargo.toml`: ```toml @@ -445,7 +456,7 @@ fn main() -> Result<(), LaError> { The error coefficients (`ERR_COEFF_2`, `ERR_COEFF_3`, `ERR_COEFF_4`) are conservative, dimension-specific constants, not caller-tunable tolerances. The -[mathematical basis](https://github.com/acgetchell/la-stack/blob/main/docs/mathematical_basis.md#determinants-and-certified-sign-filtering) +[mathematical basis](https://github.com/acgetchell/la-stack/blob/v0.4.5/docs/mathematical_basis.md#determinants-and-certified-sign-filtering) documents the bound and states its range preconditions. The constants are explicit crate-root exports for advanced users who want to compose the same bound: `use la_stack::{ERR_COEFF_2, ERR_COEFF_3, ERR_COEFF_4};`. They intentionally stay diff --git a/justfile b/justfile index 3cfa34b..d4b21c0 100644 --- a/justfile +++ b/justfile @@ -23,10 +23,10 @@ cargo_machete_version := "0.9.2" cargo_nextest_version := "0.9.143" cargo_update_version := "22.1.1" clippy_sarif_version := "0.8.0" -dprint_version := "0.56.0" +dprint_version := "0.56.1" git_cliff_version := "2.13.1" just_version := "1.58.0" -rumdl_version := "0.2.58" +rumdl_version := "0.2.60" sarif_fmt_version := "0.8.0" taplo_version := "0.10.0" typos_version := "1.49.0" diff --git a/pyproject.toml b/pyproject.toml index 5de644d..ceb2add 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,6 +155,6 @@ dev = [ "semgrep==1.174.0", "shellcheck-py==0.11.0.1", "shfmt-py==4.0.0", - "ty==0.0.73", + "ty==0.0.74", "yamllint==1.38.0", ] diff --git a/scripts/subprocess_utils.py b/scripts/subprocess_utils.py index 0045afd..e816a76 100644 --- a/scripts/subprocess_utils.py +++ b/scripts/subprocess_utils.py @@ -331,8 +331,8 @@ def run_git_command_with_input( """ git_path = get_safe_executable("git") run_kwargs = _build_run_kwargs("run_git_command_with_input", **kwargs) - encoding: str = run_kwargs.get("encoding") or "utf-8" - errors: str = run_kwargs.get("errors") or "strict" + encoding = cast("str", run_kwargs.get("encoding") or "utf-8") + errors = cast("str", run_kwargs.get("errors") or "strict") payload = input_data if isinstance(input_data, bytes) else input_data.encode(encoding, errors) with tempfile.TemporaryFile() as stdin: stdin.write(payload) diff --git a/semgrep.yaml b/semgrep.yaml index aef8835..ec654ab 100644 --- a/semgrep.yaml +++ b/semgrep.yaml @@ -100,15 +100,36 @@ rules: - "/src/**/*.rs" - "/examples/**/*.rs" - "/benches/**/*.rs" - # Test-only violation fixture: `just semgrep` excludes it, while + # Includes integration/property tests and the deliberate violation + # fixture. `just semgrep` excludes that fixture, while # `just semgrep-test` scans it directly. - - "/tests/semgrep/src/project_rules/algebraic_float.rs" + - "/tests/**/*.rs" pattern-either: - pattern: $VALUE.algebraic_add(...) - pattern: $VALUE.algebraic_sub(...) - pattern: $VALUE.algebraic_mul(...) - pattern: $VALUE.algebraic_div(...) - pattern: $VALUE.algebraic_rem(...) + - patterns: + - pattern: ::$METHOD(...) + - metavariable-regex: + metavariable: $METHOD + regex: ^algebraic_(?:add|sub|mul|div|rem)$ + - patterns: + - pattern: ::$METHOD + - metavariable-regex: + metavariable: $METHOD + regex: ^algebraic_(?:add|sub|mul|div|rem)$ + - pattern-not-inside: ::$METHOD(...) + - patterns: + - pattern: $FLOAT::$METHOD + - metavariable-regex: + metavariable: $FLOAT + regex: ^f64$ + - metavariable-regex: + metavariable: $METHOD + regex: ^algebraic_(?:add|sub|mul|div|rem)$ + - pattern-not-inside: $FLOAT::$METHOD(...) - id: la-stack.rust.no-public-infallible-raw-f64-constructors languages: diff --git a/tests/semgrep/src/project_rules/algebraic_float.rs b/tests/semgrep/src/project_rules/algebraic_float.rs index 5fec58b..4c1fdde 100644 --- a/tests/semgrep/src/project_rules/algebraic_float.rs +++ b/tests/semgrep/src/project_rules/algebraic_float.rs @@ -13,14 +13,29 @@ pub fn forbidden_f64_operations(left: f64, right: f64) -> [f64; 5] { ] } -pub fn forbidden_f32_associated_operation(left: f32, right: f32) -> f32 { +pub fn forbidden_f64_associated_operation(left: f64, right: f64) -> f64 { // ruleid: la-stack.rust.no-algebraic-float-operations - f32::algebraic_add(left, right) + f64::algebraic_add(left, right) } -pub fn forbidden_f64_associated_operation(left: f64, right: f64) -> f64 { +pub fn forbidden_f64_function_item() -> fn(f64, f64) -> f64 { // ruleid: la-stack.rust.no-algebraic-float-operations - f64::algebraic_add(left, right) + f64::algebraic_sub +} + +pub fn forbidden_f64_qualified_call(left: f64, right: f64) -> f64 { + // ruleid: la-stack.rust.no-algebraic-float-operations + ::algebraic_add(left, right) +} + +pub fn forbidden_f64_qualified_function_item() -> fn(f64, f64) -> f64 { + // ruleid: la-stack.rust.no-algebraic-float-operations + ::algebraic_sub +} + +pub fn forbidden_f64_reduction(values: &[f64]) -> Option { + // ruleid: la-stack.rust.no-algebraic-float-operations + values.iter().copied().reduce(f64::algebraic_mul) } pub fn permitted_f64_operations(left: f64, right: f64) -> [f64; 6] { @@ -39,3 +54,24 @@ pub fn permitted_f64_operations(left: f64, right: f64) -> [f64; 6] { left.mul_add(right, 1.0), ] } + +pub fn permitted_f64_function_item() -> fn(f64, f64) -> f64 { + fn add(left: f64, right: f64) -> f64 { + left + right + } + + // ok: la-stack.rust.no-algebraic-float-operations + add +} + +pub fn permitted_f64_qualified_fma( + left: f64, + right: f64, +) -> (f64, fn(f64, f64, f64) -> f64) { + ( + // ok: la-stack.rust.no-algebraic-float-operations + ::mul_add(left, right, 1.0), + // ok: la-stack.rust.no-algebraic-float-operations + ::mul_add, + ) +} diff --git a/uv.lock b/uv.lock index 83fe52f..61799f2 100644 --- a/uv.lock +++ b/uv.lock @@ -478,7 +478,7 @@ dev = [ { name = "semgrep", specifier = "==1.174.0" }, { name = "shellcheck-py", specifier = "==0.11.0.1" }, { name = "shfmt-py", specifier = "==4.0.0" }, - { name = "ty", specifier = "==0.0.73" }, + { name = "ty", specifier = "==0.0.74" }, { name = "yamllint", specifier = "==1.38.0" }, ] @@ -1177,27 +1177,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.73" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e5/90/c4e1bb4cead3b644c3e258a27f9b05c7dc5eb0ec96a4f5282194edae9e0d/ty-0.0.73.tar.gz", hash = "sha256:823d4ce0d237bfc7eb6bcee70842f2c0706113813a16951077840743712f4b74", size = 6712739, upload-time = "2026-08-19T03:12:43.381Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/0f/f5e1801e55cc631f2db193276675b30561b963a2403da832bffb5d100267/ty-0.0.73-py3-none-linux_armv6l.whl", hash = "sha256:90a946082bf9bc446b5e72973d9f4ff1222a240b2ca4c9e6eed61eb913e30810", size = 12715452, upload-time = "2026-08-19T03:12:06.673Z" }, - { url = "https://files.pythonhosted.org/packages/54/32/515dd05074c213b433524ab97eb003b0132ae7e358e0d75633ba7a314ed8/ty-0.0.73-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b7d6b5c6a6db7ea95fbbc16af514ef44a27a29a2fe1dc798900790364d170209", size = 12301870, upload-time = "2026-08-19T03:12:08.924Z" }, - { url = "https://files.pythonhosted.org/packages/50/4d/085b4889f0d4bbe4af8b96242d4a1cb209fff95967cfa239ea141983719b/ty-0.0.73-py3-none-macosx_11_0_arm64.whl", hash = "sha256:dd6f657f463e01372d8688f235be164750c8db722c97da27fa4903aa8d40b203", size = 12111741, upload-time = "2026-08-19T03:12:11.067Z" }, - { url = "https://files.pythonhosted.org/packages/95/f6/d6ec277cadfecf03ad4c18551b67c4c6eb7807a0560d801db14be99d7a89/ty-0.0.73-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2de468e33fd44c9ff1c43473a7316f4289480f5cba8995a67b6d22aee39ca9", size = 12196124, upload-time = "2026-08-19T03:12:13.14Z" }, - { url = "https://files.pythonhosted.org/packages/75/b7/ce78d8707563af9cae9bbd25328bfbc4931035085bd20089adf0c418f70e/ty-0.0.73-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2942fa0ef795a66034cdc8d75a72f453442f3b58ff2f69b4da05b7b954765b55", size = 12488557, upload-time = "2026-08-19T03:12:15.252Z" }, - { url = "https://files.pythonhosted.org/packages/d8/e8/329b9851b23502758c5c98e8cc875ea2a1b4c9674b4ca3a86da56a5063d3/ty-0.0.73-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e0f1ef14f642e18ac4e7a616a2796dcf7a5d82e28cd17f9796494acc7c4aabb", size = 13215606, upload-time = "2026-08-19T03:12:17.225Z" }, - { url = "https://files.pythonhosted.org/packages/36/38/67fedfd2cb77516ef0066b1642f487dba0eb3006493cf3475b15f5b8b228/ty-0.0.73-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16981e15fdceedb37d0aff76c5ac25914595dfee2675af95335550064251ad22", size = 13665497, upload-time = "2026-08-19T03:12:19.286Z" }, - { url = "https://files.pythonhosted.org/packages/8e/b3/154f4dd48ec5eebc186ab4b822c6e62f982fc5ddfd262d6e3903c2acba44/ty-0.0.73-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644b2bec8a2e2e4957a942ae81d6cff5571c489bb5a8675e4d3886de537a694d", size = 13351231, upload-time = "2026-08-19T03:12:21.353Z" }, - { url = "https://files.pythonhosted.org/packages/35/5f/d462496903fbe453fb76363f8478be929c8e6ff21e6928c57dcd7e5fa21f/ty-0.0.73-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:338d565be3186f50ff8e9d10483685549c2d23f0754485d5ede3b54f4319188a", size = 12782586, upload-time = "2026-08-19T03:12:23.667Z" }, - { url = "https://files.pythonhosted.org/packages/87/52/ec6d24b74abe3ec324204c1c71e6d0c6c76a17ffc15fd51d603b0a302abe/ty-0.0.73-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:11c7b6d839309d2c102cb3a4c03d817176bbfab5b2fccc95a75ec5c9597421c9", size = 13247134, upload-time = "2026-08-19T03:12:25.956Z" }, - { url = "https://files.pythonhosted.org/packages/26/20/cc74650fec56a54786c6d7c89e09576fcad3092be34cf21715d39a406a9b/ty-0.0.73-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:488572db7ff97fb50ea36a76250f2d617c9727d143da6c7bf0623276eb0fc507", size = 12309344, upload-time = "2026-08-19T03:12:28.122Z" }, - { url = "https://files.pythonhosted.org/packages/89/bd/4b0a9087f4315d7fbadf77a3ce44c816cc9ffabed1ced06cc5be81fbc414/ty-0.0.73-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1b958ebceefbbf594e59eb8d3d55bbd033ce634026fcba3e4bc3179e78e45bb7", size = 12502319, upload-time = "2026-08-19T03:12:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/11/80/0a925074911fe111912ea29d9eed309bcc183f43d2fb3eef07db056a0beb/ty-0.0.73-py3-none-musllinux_1_2_i686.whl", hash = "sha256:91a32993b3c34e42c3f323ad6c0399cb596bd1c27e9b7f20db7cd64c1067b68e", size = 12753688, upload-time = "2026-08-19T03:12:32.433Z" }, - { url = "https://files.pythonhosted.org/packages/24/6b/aeccaf89efbc2e112bd415340a22e2669ec998aa397242503e747b712ca4/ty-0.0.73-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bab8a19fbf51f479bddb2a12c5fabfe52f918a5590362321ed5d89b44eb62c15", size = 13069050, upload-time = "2026-08-19T03:12:35.398Z" }, - { url = "https://files.pythonhosted.org/packages/d7/3e/eae485fd86c1585943fd4e1746b0757b2da01e2c43136ebe8c686fe1c7f1/ty-0.0.73-py3-none-win32.whl", hash = "sha256:03347a612f0fa020b19bfd8dbd521db6ecc75d377a3e4d4f6e6c2e62871da4cc", size = 12053187, upload-time = "2026-08-19T03:12:37.565Z" }, - { url = "https://files.pythonhosted.org/packages/a7/01/9b8b983786e3ce34924e372e8b76b92b508273ab65c589fc7e88cc03ee17/ty-0.0.73-py3-none-win_amd64.whl", hash = "sha256:cedd05122ded0b5dcc55431a370e974b747f99c41c290a3d2ab8c1867f197519", size = 12693838, upload-time = "2026-08-19T03:12:39.483Z" }, - { url = "https://files.pythonhosted.org/packages/ea/88/25333bbfea6a5dc064371d2002d3d4807db90b84d5448f9106b2712b0fbc/ty-0.0.73-py3-none-win_arm64.whl", hash = "sha256:e47068f8369dea5d641a26a2ad0a947a320b02ff87099b07e95de0323245a4dc", size = 12443573, upload-time = "2026-08-19T03:12:41.449Z" }, +version = "0.0.74" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/0f/c767853e88567a2ec7e996dd95e3105b1bc62c95d103689311ef0f4a603c/ty-0.0.74.tar.gz", hash = "sha256:da14344fc8625fc9ff359bafb856ad575636ea86d9bb6a629b146bff27b380e6", size = 6786318, upload-time = "2026-08-22T15:05:54.054Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/95/6ded58bc97885c6d88fa1f9cd815031489200738f961cbf0466663213f80/ty-0.0.74-py3-none-linux_armv6l.whl", hash = "sha256:8969ef4e508debf00cf58f9ea85a539f799b1732c59cdfcecd037630b9755b30", size = 12790043, upload-time = "2026-08-22T15:05:05.015Z" }, + { url = "https://files.pythonhosted.org/packages/d9/8a/5e323603b6ab8731144421877ee8a0f8ac5a5511e67857127caa09f6730e/ty-0.0.74-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:51fb6cf5b98e1e1140825b2430943f78d744876a735231656eafbb4c3f7eca3c", size = 12371748, upload-time = "2026-08-22T15:05:08.609Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/ee72e08cb705281e8d8c42917dd577aa598a8a098008495fda5176ee3f6e/ty-0.0.74-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8ebe60b1f0a948c793d6c77fc9e9ddda599e4f023c04ab16e8e03bcb428c3fa0", size = 12282403, upload-time = "2026-08-22T15:05:11.448Z" }, + { url = "https://files.pythonhosted.org/packages/da/b3/fd935b694ff68bc278af50f7ad04770b36ce6306399baef7e1847b553a9d/ty-0.0.74-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa97f407a695c890a53615966a663c7d2167e2cabe88db7ca1a24d62635cdfc8", size = 12345164, upload-time = "2026-08-22T15:05:14.19Z" }, + { url = "https://files.pythonhosted.org/packages/54/5c/5b5825268e029ebb164c909780103dbbae367f069801410068bf1cef29b3/ty-0.0.74-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:673ddb733d4a0db31385ba1ed9ff1f6bd9dc5565413ce57b1ca5ac4c7803da5d", size = 12556646, upload-time = "2026-08-22T15:05:16.994Z" }, + { url = "https://files.pythonhosted.org/packages/56/e7/515914e571d62ce0101744fed3f881936eeb1b30dc37beb72b4f7ca1e289/ty-0.0.74-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1028e7c6b4f6145e9704552f43a5fffdcd51b42263ffdcd9c9677762bc395a4a", size = 13311653, upload-time = "2026-08-22T15:05:20.254Z" }, + { url = "https://files.pythonhosted.org/packages/b0/07/d1452babb6f9266c2122cabc095180b70ed306fb770b2996753814d2237d/ty-0.0.74-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79841a8890493021fb308772474983316eb91f7b56cb227a6a05a06b262a36f0", size = 13768284, upload-time = "2026-08-22T15:05:23.197Z" }, + { url = "https://files.pythonhosted.org/packages/b1/60/8d4a2fc7842a47210a1cb0a16a187d9de39ad5d509a00fb74c1c073afcde/ty-0.0.74-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94859d321f3c6a6c8f7bfc3f40e8319cda7e6e012e613440f3dfd145d5010e2e", size = 13422306, upload-time = "2026-08-22T15:05:26.248Z" }, + { url = "https://files.pythonhosted.org/packages/de/76/ebbc269a8c4efcc4d44624993bd188145f20d60ebda9680b15aaec42cc50/ty-0.0.74-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:970a8b2c09ff3be04c8a1c6767332d861be4fce85efe7bb205e4ade7c8655274", size = 12970637, upload-time = "2026-08-22T15:05:29.15Z" }, + { url = "https://files.pythonhosted.org/packages/9e/dd/b99f7236acbf856780ca1779a48143d2d9f2c24d7f531a0ce15a022b8a87/ty-0.0.74-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:795f763b3ded85574c2c2846a6fb8acf2aa76e9e83d761143e92b1f0c7ffa2cd", size = 13344891, upload-time = "2026-08-22T15:05:32.033Z" }, + { url = "https://files.pythonhosted.org/packages/0b/d7/9ff7449a4c7e6428f2c6f298e74cf24b70668f29d45c249507a723ff3782/ty-0.0.74-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:dc086db5367d912c31c0cc872deb7387290e779a4b9b54fcb944673a7cd52c7b", size = 12395272, upload-time = "2026-08-22T15:05:34.702Z" }, + { url = "https://files.pythonhosted.org/packages/b1/dd/b23a5b6b35d37df89dc8dc5daa09efd9245a668b50c4c81c25de21567dc1/ty-0.0.74-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0314d7b391cf684e47c2fa093d2ce4c597cfc9b01d9a315fe204aed6359b271b", size = 12573079, upload-time = "2026-08-22T15:05:37.683Z" }, + { url = "https://files.pythonhosted.org/packages/23/c5/ccba16239d6129533c8b3603458d0f4dd2ba69478e47059073968e74261d/ty-0.0.74-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4a45dd2e991e8bdae82ba78c8cd051b253f60bc71a6536598fa3ef580b4fc9b", size = 12832506, upload-time = "2026-08-22T15:05:40.505Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1c/2390912634dff4f341f97b397f2aee341ff062be0a66cda37d59375454f2/ty-0.0.74-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:210e2eac6b018fb934e2b8dac3956a0ba076a3fb1fa6f135058c825e5b759b81", size = 13154752, upload-time = "2026-08-22T15:05:43.355Z" }, + { url = "https://files.pythonhosted.org/packages/c4/33/a8c12188227e6f74f91853a7374e01ed81d6ad21c16c8b70e92dbebfe46a/ty-0.0.74-py3-none-win32.whl", hash = "sha256:db0bb6a8f098ef9bd1be861f73b4f7c0320d40d4c05c7ae0a8677d4e7aa4f6e5", size = 12130002, upload-time = "2026-08-22T15:05:46.058Z" }, + { url = "https://files.pythonhosted.org/packages/21/5c/064f28ccb9c234cfce5a2f7aa69a256663d5ae5bb0290b3a9706cc4d1e4c/ty-0.0.74-py3-none-win_amd64.whl", hash = "sha256:bebff181515255b3c78bd2e7693ae66fab6064ad4feea2065c68bc01022aa678", size = 12771435, upload-time = "2026-08-22T15:05:48.811Z" }, + { url = "https://files.pythonhosted.org/packages/fe/06/d6becdaca0315346c26b6df97cb0eafa81de4f870945d6989e88704374ed/ty-0.0.74-py3-none-win_arm64.whl", hash = "sha256:1a3469eaaf8c85b1c0a15bede25d36daea4b09fce1d913e965b24e24b3f1d6c6", size = 12558299, upload-time = "2026-08-22T15:05:51.543Z" }, ] [[package]]