typedframes 0.8.0__tar.gz → 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {typedframes-0.8.0 → typedframes-0.10.0}/PKG-INFO +55 -25
  2. {typedframes-0.8.0 → typedframes-0.10.0}/README.md +54 -24
  3. {typedframes-0.8.0 → typedframes-0.10.0}/pyproject.toml +8 -8
  4. {typedframes-0.8.0 → typedframes-0.10.0}/rust/Cargo.lock +1 -1
  5. {typedframes-0.8.0 → typedframes-0.10.0}/rust/Cargo.toml +1 -1
  6. typedframes-0.10.0/rust/build.rs +35 -0
  7. typedframes-0.10.0/rust/src/apply.rs +852 -0
  8. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/ast_extract.rs +209 -0
  9. typedframes-0.10.0/rust/src/column_consuming_methods.rs +85 -0
  10. typedframes-0.10.0/rust/src/column_usage.rs +1798 -0
  11. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/errors.rs +21 -0
  12. typedframes-0.10.0/rust/src/helpers.rs +309 -0
  13. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/index.rs +512 -20
  14. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/lib.rs +11 -0
  15. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/linter.rs +3419 -533
  16. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/notebook.rs +46 -0
  17. typedframes-0.10.0/rust/src/parallel.rs +107 -0
  18. typedframes-0.10.0/rust/src/pyapi.rs +539 -0
  19. typedframes-0.10.0/rust/src/static_eval.rs +4682 -0
  20. typedframes-0.10.0/rust/src/suggest.rs +1061 -0
  21. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/__init__.py +1 -1
  22. typedframes-0.10.0/src/typedframes/_cache.py +109 -0
  23. typedframes-0.10.0/src/typedframes/_rust_checker.pyi +27 -0
  24. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/cli.py +568 -115
  25. typedframes-0.8.0/rust/src/pyapi.rs +0 -281
  26. typedframes-0.8.0/src/typedframes/_rust_checker.pyi +0 -16
  27. {typedframes-0.8.0 → typedframes-0.10.0}/rust/.cargo/config.toml +0 -0
  28. {typedframes-0.8.0 → typedframes-0.10.0}/rust/README.md +0 -0
  29. {typedframes-0.8.0 → typedframes-0.10.0}/rust/benches/parser_bench.rs +0 -0
  30. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/config.rs +0 -0
  31. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/constants.rs +0 -0
  32. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/contract.rs +0 -0
  33. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/frame_ops.rs +0 -0
  34. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/main.rs +0 -0
  35. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/sql.rs +0 -0
  36. {typedframes-0.8.0 → typedframes-0.10.0}/rust/src/typo.rs +0 -0
  37. {typedframes-0.8.0 → typedframes-0.10.0}/rust/tests/integration_test.rs +0 -0
  38. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/base_schema.py +0 -0
  39. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/column.py +0 -0
  40. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/column_group.py +0 -0
  41. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/column_group_error.py +0 -0
  42. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/column_set.py +0 -0
  43. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/missing_dependency_error.py +0 -0
  44. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/mypy.py +0 -0
  45. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/pandas.py +0 -0
  46. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/pandera.py +0 -0
  47. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/polars.py +0 -0
  48. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/py.typed +0 -0
  49. {typedframes-0.8.0 → typedframes-0.10.0}/src/typedframes/schema_algebra.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typedframes
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -42,7 +42,7 @@ Project-URL: Repository, https://github.com/w-martin/typedframes
42
42
 
43
43
  > ⚠️ **Project Status: Proof of Concept**
44
44
  >
45
- > `typedframes` (v0.8.0) is currently an experimental proof-of-concept. The core static analysis and mypy/Rust
45
+ > `typedframes` (v0.10.0) is currently an experimental proof-of-concept. The core static analysis and mypy/Rust
46
46
  > integrations work, but expect rough edges. The codebase prioritizes demonstrating the viability of static DataFrame
47
47
  > column checking over production-grade stability.
48
48
  >
@@ -504,7 +504,7 @@ repos:
504
504
  name: typedframes check
505
505
  entry: typedframes check . --strict
506
506
  language: python
507
- additional_dependencies: ["typedframes==0.8.0"]
507
+ additional_dependencies: ["typedframes==0.10.0"]
508
508
  types_or: [python, jupyter]
509
509
  pass_filenames: false
510
510
  ```
@@ -553,7 +553,7 @@ The action installs the PyPI wheel into a throwaway virtualenv and runs the chec
553
553
  | Input | Default | |
554
554
  |-------|---------|-|
555
555
  | `path` | `.` | File or directory to check |
556
- | `version` | `latest` | PyPI version to install, e.g. `"0.8.0"` |
556
+ | `version` | `latest` | PyPI version to install, e.g. `"0.10.0"` |
557
557
  | `strict` | `true` | Fail the step on errors — `typedframes check` exits 0 without it |
558
558
  | `coverage-fail-under` | *(unset)* | Minimum DataFrame schema coverage, e.g. `"90"` |
559
559
  | `coverage-detail` | `summary` | Or `term-missing` for the per-file breakdown, `explain` to diagnose a lower-than-expected total |
@@ -667,17 +667,17 @@ plus the requested features, so both halves get checked normally. See
667
667
  Fast feedback reduces development time. The typedframes Rust binary provides near-instant column checking.
668
668
 
669
669
  **Benchmark results** (20 runs, 3 warmup, caches cleared between runs):
670
- *2026-09-28 · Darwin 27.0.0 · arm · CPython 3.14.4 · 64GiB RAM · Great Expectations pinned @ 1.20.0*
670
+ *2026-10-06 · Darwin 27.0.0 · arm · CPython 3.14.4 · 64GiB RAM · Great Expectations pinned @ 1.23.2*
671
671
 
672
- | Tool | Version | What it does | typedframes (13 files) | great_expectations (485 files) |
672
+ | Tool | Version | What it does | typedframes (14 files) | great_expectations (494 files) |
673
673
  |------|---------|--------------|------------------------|--------------------------------|
674
- | typedframes | 0.8.0 | DataFrame column checker | 49ms ±349µs (IQR 513µs) | 258ms ±2ms (IQR 2ms) |
675
- | ruff | 0.16.3 | Linter (no type checking) | 30ms ±981µs (IQR 1ms) | 236ms ±4ms (IQR 6ms) |
676
- | ty | 0.0.72 | Type checker | 72ms ±1ms (IQR 959µs) | 773ms ±8ms (IQR 14ms) |
677
- | pyrefly | 1.2.0 | Type checker | 98ms ±1ms (IQR 1ms) | 273ms ±8ms (IQR 11ms) |
678
- | mypy | 2.3.1 | Type checker (no plugin) | 2.79s ±93ms (IQR 61ms) | 4.14s ±41ms (IQR 52ms) |
679
- | mypy + typedframes | 2.3.1 | Type checker + column checker | 2.78s ±13ms (IQR 14ms) | 4.50s ±26ms (IQR 33ms) |
680
- | pyright | 1.1.411 | Type checker | 841ms ±35ms (IQR 46ms) | 3.30s ±24ms (IQR 32ms) |
674
+ | typedframes | 0.10.0 | DataFrame column checker | 49ms ±618µs (IQR 609µs) | 134ms ±1ms (IQR 2ms) |
675
+ | ruff | 0.16.10 | Linter (no type checking) | 22ms ±940µs (IQR 2ms) | 225ms ±3ms (IQR 5ms) |
676
+ | ty | 0.0.84 | Type checker | 70ms ±1ms (IQR 2ms) | 788ms ±9ms (IQR 9ms) |
677
+ | pyrefly | 1.3.2 | Type checker | 91ms ±863µs (IQR 1ms) | 261ms ±7ms (IQR 11ms) |
678
+ | mypy | 2.4.0 | Type checker (no plugin) | 1.96s ±10ms (IQR 13ms) | 3.27s ±19ms (IQR 20ms) |
679
+ | mypy + typedframes | 2.4.0 | Type checker + column checker | 1.99s ±29ms (IQR 31ms) | 3.67s ±15ms (IQR 15ms) |
680
+ | pyright | 1.1.414 | Type checker | 940ms ±4ms (IQR 6ms) | 4.21s ±35ms (IQR 52ms) |
681
681
 
682
682
  *Run `uv run python benchmarks/benchmark_checkers.py` to reproduce.*
683
683
 
@@ -694,6 +694,35 @@ parsing.
694
694
 
695
695
  **Note:** ty (Astral) does not currently support mypy plugins, so use the standalone binary for column checking with ty.
696
696
 
697
+ ### Index Cache and Parallelism
698
+
699
+ Files are indexed and checked in parallel, one worker per core (up to 16). Set `TYPEDFRAMES_JOBS=1` to run on a single thread, or
700
+ a number to cap the workers.
701
+
702
+ What the checker learns about each file is also cached on disk and reused for every file whose contents have not
703
+ changed, so repeat runs only re-index what you edited. A cached run reports exactly what an uncached run would. The cache
704
+ lives in your per-user cache directory by default (`~/Library/Caches/typedframes` on macOS, `~/.cache/typedframes` on
705
+ Linux), so nothing is added to your repository.
706
+
707
+ ```shell
708
+ typedframes cache path # where is it?
709
+ typedframes cache clear # delete it
710
+ typedframes check src/ --no-cache # skip it for one run
711
+ typedframes check src/ --cache-dir .typedframes_cache # keep it in the project instead
712
+ typedframes cache gitignore # add a project-local cache directory to .gitignore
713
+ ```
714
+
715
+ To set it permanently, use the `TYPEDFRAMES_CACHE_DIR` environment variable or `pyproject.toml`; a relative path is
716
+ relative to the project:
717
+
718
+ ```toml
719
+ [tool.typedframes]
720
+ cache_dir = ".typedframes_cache"
721
+ ```
722
+
723
+ The order of precedence is `--no-cache`, `--cache-dir`, `TYPEDFRAMES_CACHE_DIR`, `cache_dir`, then the default. See
724
+ `typedframes cache --help`.
725
+
697
726
  ---
698
727
 
699
728
  ## Type Safety With Multiple Backends
@@ -893,7 +922,7 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
893
922
 
894
923
  | Feature | typedframes | Pandera | Great Expectations | strictly_typed_pandas | pandas-stubs | dataenforce | pandas-type-checks | StaticFrame | narwhals | dataframely | patito |
895
924
  |---------------------------------|------------------------|-------------|--------------------|-----------------------|--------------|-------------|--------------------|------------------|----------|------------------|------------------|
896
- | **Version tested** | 0.4.1 | 0.32.1 | 1.20.0 | 0.3.7 | 3.0.5 | 0.1.2 | 1.1.3 | 5.1.1 | 2.24.0 | 3.0.0 | 0.8.6 |
925
+ | **Version tested** | 0.10.0 | 0.34.1 | 1.23.2 | 0.3.7 | 3.0.5.260914 | 0.1.2 | 1.1.3 | 5.1.1 | 2.26.0 | 3.1.2 | 0.8.6 |
897
926
  | **Analysis Type** |
898
927
  | When errors are caught | **Static (lint-time)** | Runtime | Runtime | Runtime | Static | Runtime | Runtime | Runtime | Runtime | Runtime | Runtime |
899
928
  | **Static Analysis (our focus)** |
@@ -913,22 +942,23 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
913
942
  | Pandas | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ❌ Own | ✅ Yes | ❌ No | ⚠️ Limited |
914
943
  | Polars | ✅ Yes | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ Own | ✅ Yes | ✅ Yes (only) | ✅ Yes |
915
944
  | DuckDB, cuDF, etc. | ❌ No | ❌ No | ✅ Spark, SQL | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ✅ Yes | ❌ No | ❌ No |
916
- | **Project Status (Aug 2026)** |
945
+ | **Project Status (Oct 2026)** |
917
946
  | Active development | ✅ Yes | ✅ Yes | ✅ Yes | ⚠️ Low | ✅ Yes | ❌ Inactive | ⚠️ Low | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
918
947
 
919
948
  **Legend:** ✅ Full support | ⚠️ Limited/Partial | ❌ Not supported
920
949
 
921
950
  ### Tool Descriptions
922
951
 
923
- - **[Pandera](https://pandera.readthedocs.io/)** (v0.32.1): Excellent runtime validation. Static analysis support exists
952
+ - **[Pandera](https://pandera.readthedocs.io/)** (v0.34.1): Excellent runtime validation. Static analysis support exists
924
953
  but has limitations—column access via `df["column"]` is not validated, and schema mismatches between functions may not
925
954
  be caught.
926
955
 
927
956
  - **[strictly_typed_pandas](https://strictly-typed-pandas.readthedocs.io/)** (v0.3.7): Provides `DataSet[Schema]` type
928
957
  hints for runtime validation via typeguard. Despite documentation implying mypy support, there is no mypy plugin —
929
- column access errors are not caught statically. No standalone checker. No polars support.
958
+ column access errors are not caught statically. No standalone checker. No polars support. Pinned to
959
+ `pandas<=2.2.3` and `pandas-stubs<=2.2.3.250308`, so it can't be used with pandas 3.
930
960
 
931
- - **[pandas-stubs](https://github.com/pandas-dev/pandas-stubs)** (v3.0.5): Official pandas type stubs. Provides
961
+ - **[pandas-stubs](https://github.com/pandas-dev/pandas-stubs)** (v3.0.5.260914): Official pandas type stubs. Provides
932
962
  API-level types but no column-level checking.
933
963
 
934
964
  - **[dataenforce](https://github.com/CedricFR/dataenforce)** (v0.1.2, the only release ever published): Runtime
@@ -943,17 +973,17 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
943
973
  Not compatible with pandas/polars — requires a full rewrite to StaticFrame's own API. Column access is still
944
974
  string-based; mypy does not catch column name typos. Type safety comes from immutability guarantees, not schema checking.
945
975
 
946
- - **[narwhals](https://narwhals-dev.github.io/narwhals/)** (v2.24.0): Compatibility layer that provides a unified API
976
+ - **[narwhals](https://narwhals-dev.github.io/narwhals/)** (v2.26.0): Compatibility layer that provides a unified API
947
977
  across pandas, polars, DuckDB, cuDF, and more. Solves a different problem—write-once-run-anywhere portability, not
948
978
  type safety. See [Why Abstraction Layers Don't Solve Type Safety](#why-abstraction-layers-dont-solve-type-safety)
949
979
  below.
950
980
 
951
- - **[Great Expectations](https://greatexpectations.io/)** (v1.20.0): Comprehensive data quality framework. Defines
981
+ - **[Great Expectations](https://greatexpectations.io/)** (v1.23.2): Comprehensive data quality framework. Defines
952
982
  "expectations" (assertions) about data values, distributions, and schema properties. Excellent for runtime
953
983
  validation, data documentation, and data quality monitoring. No static analysis or column-level type checking in
954
984
  code. Supports pandas, Spark, and SQL backends.
955
985
 
956
- - **[dataframely](https://github.com/Quantco/dataframely)** (v3.0.0): Polars-only runtime validation library from Quantco.
986
+ - **[dataframely](https://github.com/Quantco/dataframely)** (v3.1.2): Polars-only runtime validation library from Quantco.
957
987
  Schemas are defined as classes inheriting `dy.Schema` with typed descriptor fields (`dy.String()`, `dy.Float64()`)
958
988
  and `@dy.rule()` decorators for cross-column and group-level constraints. Returns `dy.DataFrame[Schema]` generic
959
989
  types that give call-site narrowing to type checkers, but does not validate column subscript access inside function
@@ -971,16 +1001,16 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
971
1001
  These are general Python type checkers. They don't validate DataFrame column names, but they can be used alongside
972
1002
  typedframes for comprehensive type checking:
973
1003
 
974
- - **[mypy](https://mypy-lang.org/)** (v2.3.1): The original Python type checker. typedframes provides a mypy plugin for
1004
+ - **[mypy](https://mypy-lang.org/)** (v2.4.0): The original Python type checker. typedframes provides a mypy plugin for
975
1005
  column checking. See [performance benchmarks](#static-analysis-performance).
976
1006
 
977
- - **[ty](https://github.com/astral-sh/ty)** (v0.0.72, Astral): New Rust-based type checker, faster than mypy on
1007
+ - **[ty](https://github.com/astral-sh/ty)** (v0.0.84, Astral): New Rust-based type checker, faster than mypy on
978
1008
  large codebases. Does not support mypy plugins—use typedframes standalone checker.
979
1009
 
980
- - **[pyrefly](https://pyrefly.org/)** (v1.2.0, Meta): Rust-based type checker from Meta, replacement for Pyre. Fast,
1010
+ - **[pyrefly](https://pyrefly.org/)** (v1.3.2, Meta): Rust-based type checker from Meta, replacement for Pyre. Fast,
981
1011
  but no DataFrame column checking.
982
1012
 
983
- - **[pyright](https://github.com/microsoft/pyright)** (v1.1.411, Microsoft): Type checker powering Pylance/VSCode. No
1013
+ - **[pyright](https://github.com/microsoft/pyright)** (v1.1.414, Microsoft): Type checker powering Pylance/VSCode. No
984
1014
  mypy plugin support—use typedframes standalone checker.
985
1015
 
986
1016
  ### Not Directly Comparable
@@ -8,7 +8,7 @@
8
8
 
9
9
  > ⚠️ **Project Status: Proof of Concept**
10
10
  >
11
- > `typedframes` (v0.8.0) is currently an experimental proof-of-concept. The core static analysis and mypy/Rust
11
+ > `typedframes` (v0.10.0) is currently an experimental proof-of-concept. The core static analysis and mypy/Rust
12
12
  > integrations work, but expect rough edges. The codebase prioritizes demonstrating the viability of static DataFrame
13
13
  > column checking over production-grade stability.
14
14
  >
@@ -470,7 +470,7 @@ repos:
470
470
  name: typedframes check
471
471
  entry: typedframes check . --strict
472
472
  language: python
473
- additional_dependencies: ["typedframes==0.8.0"]
473
+ additional_dependencies: ["typedframes==0.10.0"]
474
474
  types_or: [python, jupyter]
475
475
  pass_filenames: false
476
476
  ```
@@ -519,7 +519,7 @@ The action installs the PyPI wheel into a throwaway virtualenv and runs the chec
519
519
  | Input | Default | |
520
520
  |-------|---------|-|
521
521
  | `path` | `.` | File or directory to check |
522
- | `version` | `latest` | PyPI version to install, e.g. `"0.8.0"` |
522
+ | `version` | `latest` | PyPI version to install, e.g. `"0.10.0"` |
523
523
  | `strict` | `true` | Fail the step on errors — `typedframes check` exits 0 without it |
524
524
  | `coverage-fail-under` | *(unset)* | Minimum DataFrame schema coverage, e.g. `"90"` |
525
525
  | `coverage-detail` | `summary` | Or `term-missing` for the per-file breakdown, `explain` to diagnose a lower-than-expected total |
@@ -633,17 +633,17 @@ plus the requested features, so both halves get checked normally. See
633
633
  Fast feedback reduces development time. The typedframes Rust binary provides near-instant column checking.
634
634
 
635
635
  **Benchmark results** (20 runs, 3 warmup, caches cleared between runs):
636
- *2026-09-28 · Darwin 27.0.0 · arm · CPython 3.14.4 · 64GiB RAM · Great Expectations pinned @ 1.20.0*
636
+ *2026-10-06 · Darwin 27.0.0 · arm · CPython 3.14.4 · 64GiB RAM · Great Expectations pinned @ 1.23.2*
637
637
 
638
- | Tool | Version | What it does | typedframes (13 files) | great_expectations (485 files) |
638
+ | Tool | Version | What it does | typedframes (14 files) | great_expectations (494 files) |
639
639
  |------|---------|--------------|------------------------|--------------------------------|
640
- | typedframes | 0.8.0 | DataFrame column checker | 49ms ±349µs (IQR 513µs) | 258ms ±2ms (IQR 2ms) |
641
- | ruff | 0.16.3 | Linter (no type checking) | 30ms ±981µs (IQR 1ms) | 236ms ±4ms (IQR 6ms) |
642
- | ty | 0.0.72 | Type checker | 72ms ±1ms (IQR 959µs) | 773ms ±8ms (IQR 14ms) |
643
- | pyrefly | 1.2.0 | Type checker | 98ms ±1ms (IQR 1ms) | 273ms ±8ms (IQR 11ms) |
644
- | mypy | 2.3.1 | Type checker (no plugin) | 2.79s ±93ms (IQR 61ms) | 4.14s ±41ms (IQR 52ms) |
645
- | mypy + typedframes | 2.3.1 | Type checker + column checker | 2.78s ±13ms (IQR 14ms) | 4.50s ±26ms (IQR 33ms) |
646
- | pyright | 1.1.411 | Type checker | 841ms ±35ms (IQR 46ms) | 3.30s ±24ms (IQR 32ms) |
640
+ | typedframes | 0.10.0 | DataFrame column checker | 49ms ±618µs (IQR 609µs) | 134ms ±1ms (IQR 2ms) |
641
+ | ruff | 0.16.10 | Linter (no type checking) | 22ms ±940µs (IQR 2ms) | 225ms ±3ms (IQR 5ms) |
642
+ | ty | 0.0.84 | Type checker | 70ms ±1ms (IQR 2ms) | 788ms ±9ms (IQR 9ms) |
643
+ | pyrefly | 1.3.2 | Type checker | 91ms ±863µs (IQR 1ms) | 261ms ±7ms (IQR 11ms) |
644
+ | mypy | 2.4.0 | Type checker (no plugin) | 1.96s ±10ms (IQR 13ms) | 3.27s ±19ms (IQR 20ms) |
645
+ | mypy + typedframes | 2.4.0 | Type checker + column checker | 1.99s ±29ms (IQR 31ms) | 3.67s ±15ms (IQR 15ms) |
646
+ | pyright | 1.1.414 | Type checker | 940ms ±4ms (IQR 6ms) | 4.21s ±35ms (IQR 52ms) |
647
647
 
648
648
  *Run `uv run python benchmarks/benchmark_checkers.py` to reproduce.*
649
649
 
@@ -660,6 +660,35 @@ parsing.
660
660
 
661
661
  **Note:** ty (Astral) does not currently support mypy plugins, so use the standalone binary for column checking with ty.
662
662
 
663
+ ### Index Cache and Parallelism
664
+
665
+ Files are indexed and checked in parallel, one worker per core (up to 16). Set `TYPEDFRAMES_JOBS=1` to run on a single thread, or
666
+ a number to cap the workers.
667
+
668
+ What the checker learns about each file is also cached on disk and reused for every file whose contents have not
669
+ changed, so repeat runs only re-index what you edited. A cached run reports exactly what an uncached run would. The cache
670
+ lives in your per-user cache directory by default (`~/Library/Caches/typedframes` on macOS, `~/.cache/typedframes` on
671
+ Linux), so nothing is added to your repository.
672
+
673
+ ```shell
674
+ typedframes cache path # where is it?
675
+ typedframes cache clear # delete it
676
+ typedframes check src/ --no-cache # skip it for one run
677
+ typedframes check src/ --cache-dir .typedframes_cache # keep it in the project instead
678
+ typedframes cache gitignore # add a project-local cache directory to .gitignore
679
+ ```
680
+
681
+ To set it permanently, use the `TYPEDFRAMES_CACHE_DIR` environment variable or `pyproject.toml`; a relative path is
682
+ relative to the project:
683
+
684
+ ```toml
685
+ [tool.typedframes]
686
+ cache_dir = ".typedframes_cache"
687
+ ```
688
+
689
+ The order of precedence is `--no-cache`, `--cache-dir`, `TYPEDFRAMES_CACHE_DIR`, `cache_dir`, then the default. See
690
+ `typedframes cache --help`.
691
+
663
692
  ---
664
693
 
665
694
  ## Type Safety With Multiple Backends
@@ -859,7 +888,7 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
859
888
 
860
889
  | Feature | typedframes | Pandera | Great Expectations | strictly_typed_pandas | pandas-stubs | dataenforce | pandas-type-checks | StaticFrame | narwhals | dataframely | patito |
861
890
  |---------------------------------|------------------------|-------------|--------------------|-----------------------|--------------|-------------|--------------------|------------------|----------|------------------|------------------|
862
- | **Version tested** | 0.4.1 | 0.32.1 | 1.20.0 | 0.3.7 | 3.0.5 | 0.1.2 | 1.1.3 | 5.1.1 | 2.24.0 | 3.0.0 | 0.8.6 |
891
+ | **Version tested** | 0.10.0 | 0.34.1 | 1.23.2 | 0.3.7 | 3.0.5.260914 | 0.1.2 | 1.1.3 | 5.1.1 | 2.26.0 | 3.1.2 | 0.8.6 |
863
892
  | **Analysis Type** |
864
893
  | When errors are caught | **Static (lint-time)** | Runtime | Runtime | Runtime | Static | Runtime | Runtime | Runtime | Runtime | Runtime | Runtime |
865
894
  | **Static Analysis (our focus)** |
@@ -879,22 +908,23 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
879
908
  | Pandas | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ❌ Own | ✅ Yes | ❌ No | ⚠️ Limited |
880
909
  | Polars | ✅ Yes | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ Own | ✅ Yes | ✅ Yes (only) | ✅ Yes |
881
910
  | DuckDB, cuDF, etc. | ❌ No | ❌ No | ✅ Spark, SQL | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ✅ Yes | ❌ No | ❌ No |
882
- | **Project Status (Aug 2026)** |
911
+ | **Project Status (Oct 2026)** |
883
912
  | Active development | ✅ Yes | ✅ Yes | ✅ Yes | ⚠️ Low | ✅ Yes | ❌ Inactive | ⚠️ Low | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
884
913
 
885
914
  **Legend:** ✅ Full support | ⚠️ Limited/Partial | ❌ Not supported
886
915
 
887
916
  ### Tool Descriptions
888
917
 
889
- - **[Pandera](https://pandera.readthedocs.io/)** (v0.32.1): Excellent runtime validation. Static analysis support exists
918
+ - **[Pandera](https://pandera.readthedocs.io/)** (v0.34.1): Excellent runtime validation. Static analysis support exists
890
919
  but has limitations—column access via `df["column"]` is not validated, and schema mismatches between functions may not
891
920
  be caught.
892
921
 
893
922
  - **[strictly_typed_pandas](https://strictly-typed-pandas.readthedocs.io/)** (v0.3.7): Provides `DataSet[Schema]` type
894
923
  hints for runtime validation via typeguard. Despite documentation implying mypy support, there is no mypy plugin —
895
- column access errors are not caught statically. No standalone checker. No polars support.
924
+ column access errors are not caught statically. No standalone checker. No polars support. Pinned to
925
+ `pandas<=2.2.3` and `pandas-stubs<=2.2.3.250308`, so it can't be used with pandas 3.
896
926
 
897
- - **[pandas-stubs](https://github.com/pandas-dev/pandas-stubs)** (v3.0.5): Official pandas type stubs. Provides
927
+ - **[pandas-stubs](https://github.com/pandas-dev/pandas-stubs)** (v3.0.5.260914): Official pandas type stubs. Provides
898
928
  API-level types but no column-level checking.
899
929
 
900
930
  - **[dataenforce](https://github.com/CedricFR/dataenforce)** (v0.1.2, the only release ever published): Runtime
@@ -909,17 +939,17 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
909
939
  Not compatible with pandas/polars — requires a full rewrite to StaticFrame's own API. Column access is still
910
940
  string-based; mypy does not catch column name typos. Type safety comes from immutability guarantees, not schema checking.
911
941
 
912
- - **[narwhals](https://narwhals-dev.github.io/narwhals/)** (v2.24.0): Compatibility layer that provides a unified API
942
+ - **[narwhals](https://narwhals-dev.github.io/narwhals/)** (v2.26.0): Compatibility layer that provides a unified API
913
943
  across pandas, polars, DuckDB, cuDF, and more. Solves a different problem—write-once-run-anywhere portability, not
914
944
  type safety. See [Why Abstraction Layers Don't Solve Type Safety](#why-abstraction-layers-dont-solve-type-safety)
915
945
  below.
916
946
 
917
- - **[Great Expectations](https://greatexpectations.io/)** (v1.20.0): Comprehensive data quality framework. Defines
947
+ - **[Great Expectations](https://greatexpectations.io/)** (v1.23.2): Comprehensive data quality framework. Defines
918
948
  "expectations" (assertions) about data values, distributions, and schema properties. Excellent for runtime
919
949
  validation, data documentation, and data quality monitoring. No static analysis or column-level type checking in
920
950
  code. Supports pandas, Spark, and SQL backends.
921
951
 
922
- - **[dataframely](https://github.com/Quantco/dataframely)** (v3.0.0): Polars-only runtime validation library from Quantco.
952
+ - **[dataframely](https://github.com/Quantco/dataframely)** (v3.1.2): Polars-only runtime validation library from Quantco.
923
953
  Schemas are defined as classes inheriting `dy.Schema` with typed descriptor fields (`dy.String()`, `dy.Float64()`)
924
954
  and `@dy.rule()` decorators for cross-column and group-level constraints. Returns `dy.DataFrame[Schema]` generic
925
955
  types that give call-site narrowing to type checkers, but does not validate column subscript access inside function
@@ -937,16 +967,16 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
937
967
  These are general Python type checkers. They don't validate DataFrame column names, but they can be used alongside
938
968
  typedframes for comprehensive type checking:
939
969
 
940
- - **[mypy](https://mypy-lang.org/)** (v2.3.1): The original Python type checker. typedframes provides a mypy plugin for
970
+ - **[mypy](https://mypy-lang.org/)** (v2.4.0): The original Python type checker. typedframes provides a mypy plugin for
941
971
  column checking. See [performance benchmarks](#static-analysis-performance).
942
972
 
943
- - **[ty](https://github.com/astral-sh/ty)** (v0.0.72, Astral): New Rust-based type checker, faster than mypy on
973
+ - **[ty](https://github.com/astral-sh/ty)** (v0.0.84, Astral): New Rust-based type checker, faster than mypy on
944
974
  large codebases. Does not support mypy plugins—use typedframes standalone checker.
945
975
 
946
- - **[pyrefly](https://pyrefly.org/)** (v1.2.0, Meta): Rust-based type checker from Meta, replacement for Pyre. Fast,
976
+ - **[pyrefly](https://pyrefly.org/)** (v1.3.2, Meta): Rust-based type checker from Meta, replacement for Pyre. Fast,
947
977
  but no DataFrame column checking.
948
978
 
949
- - **[pyright](https://github.com/microsoft/pyright)** (v1.1.411, Microsoft): Type checker powering Pylance/VSCode. No
979
+ - **[pyright](https://github.com/microsoft/pyright)** (v1.1.414, Microsoft): Type checker powering Pylance/VSCode. No
950
980
  mypy plugin support—use typedframes standalone checker.
951
981
 
952
982
  ### Not Directly Comparable
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "typedframes"
7
- version = "0.8.0"
7
+ version = "0.10.0"
8
8
  description = "Static analysis for pandas and polars DataFrames. Catch column errors at lint-time, not runtime."
9
9
  keywords = ["pandas", "polars", "type-checking", "static-analysis", "dataframe", "linter", "mypy-plugin"]
10
10
  readme = "README.md"
@@ -128,7 +128,7 @@ docs = [
128
128
  ]
129
129
  dev = [
130
130
  "pandas>=3.0.5",
131
- "pandera>=0.32.1",
131
+ "pandera>=0.34.1",
132
132
  "pip-audit>=2.10.1",
133
133
  "polars>=1.43.2",
134
134
  "bandit>=1.9.4",
@@ -137,19 +137,19 @@ dev = [
137
137
  "invoke>=3.0.3",
138
138
  "maturin>=1.14.1",
139
139
  "pre-commit>=4.6.1",
140
- "pyrefly>=1.2.0",
141
- "pyright>=1.1.411",
140
+ "pyrefly>=1.3.2",
141
+ "pyright>=1.1.414",
142
142
  "pytest>=9.1.1",
143
143
  "pytest-cov>=7.1.0",
144
144
  "pytest-xdist>=3.8.0",
145
- "ruff>=0.16.3",
146
- "ty>=0.0.72",
145
+ "ruff>=0.16.10",
146
+ "ty>=0.0.84",
147
147
  "openpyxl>=3.1.5",
148
148
  "pyarrow>=25.0.1",
149
149
  "xlsxwriter>=3.2.9",
150
150
  "fastexcel>=0.20.2",
151
- "pandas-stubs>=3.0.5.260730",
152
- "mypy>=2.3.1",
151
+ "pandas-stubs>=3.0.5.260914",
152
+ "mypy>=2.4.0",
153
153
  "trustedlicenses>=0.1.1",
154
154
  ]
155
155
 
@@ -1347,7 +1347,7 @@ checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
1347
1347
 
1348
1348
  [[package]]
1349
1349
  name = "typedframes_checker"
1350
- version = "0.8.0"
1350
+ version = "0.10.0"
1351
1351
  dependencies = [
1352
1352
  "anyhow",
1353
1353
  "criterion",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "typedframes_checker"
3
- version = "0.8.0"
3
+ version = "0.10.0"
4
4
  edition = "2021"
5
5
  description = "A high-performance Rust-based checker for typedframes DataFrame column schemas"
6
6
  license = "MIT"
@@ -0,0 +1,35 @@
1
+ use std::fs;
2
+ use std::path::{Path, PathBuf};
3
+
4
+ // Exposes a content hash of the crate's sources and dependency pins as
5
+ // `TYPEDFRAMES_BUILD_ID`. The index cache keys on it, so a build with different
6
+ // indexing logic never reuses entries an earlier build computed.
7
+ fn main() {
8
+ let mut files = vec![PathBuf::from("Cargo.toml"), PathBuf::from("Cargo.lock")];
9
+ collect(Path::new("src"), &mut files);
10
+ files.sort();
11
+ let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
12
+ for path in &files {
13
+ println!("cargo:rerun-if-changed={}", path.display());
14
+ let bytes = fs::read(path).unwrap_or_default();
15
+ for byte in path.to_string_lossy().bytes().chain(bytes) {
16
+ hash = (hash ^ u64::from(byte)).wrapping_mul(0x0000_0100_0000_01b3);
17
+ }
18
+ }
19
+ println!("cargo:rerun-if-changed=src");
20
+ println!("cargo:rustc-env=TYPEDFRAMES_BUILD_ID={hash:016x}");
21
+ }
22
+
23
+ fn collect(dir: &Path, files: &mut Vec<PathBuf>) {
24
+ let Ok(entries) = fs::read_dir(dir) else {
25
+ return;
26
+ };
27
+ for entry in entries.flatten() {
28
+ let path = entry.path();
29
+ if path.is_dir() {
30
+ collect(&path, files);
31
+ } else {
32
+ files.push(path);
33
+ }
34
+ }
35
+ }