typedframes 0.9.0__tar.gz → 0.11.1__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 (54) hide show
  1. typedframes-0.11.1/LICENSE +21 -0
  2. {typedframes-0.9.0 → typedframes-0.11.1}/PKG-INFO +58 -26
  3. {typedframes-0.9.0 → typedframes-0.11.1}/README.md +55 -25
  4. {typedframes-0.9.0 → typedframes-0.11.1}/pyproject.toml +10 -8
  5. {typedframes-0.9.0 → typedframes-0.11.1}/rust/Cargo.lock +1 -1
  6. {typedframes-0.9.0 → typedframes-0.11.1}/rust/Cargo.toml +1 -1
  7. typedframes-0.11.1/rust/build.rs +35 -0
  8. typedframes-0.11.1/rust/src/apply.rs +854 -0
  9. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/ast_extract.rs +184 -0
  10. typedframes-0.11.1/rust/src/column_consuming_methods.rs +90 -0
  11. typedframes-0.11.1/rust/src/column_usage.rs +1798 -0
  12. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/errors.rs +23 -0
  13. typedframes-0.11.1/rust/src/helpers.rs +309 -0
  14. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/index.rs +547 -20
  15. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/lib.rs +15 -0
  16. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/linter.rs +3122 -689
  17. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/notebook.rs +49 -0
  18. typedframes-0.11.1/rust/src/parallel.rs +107 -0
  19. typedframes-0.11.1/rust/src/phrase.rs +233 -0
  20. typedframes-0.11.1/rust/src/pyapi.rs +572 -0
  21. typedframes-0.11.1/rust/src/report.rs +1229 -0
  22. typedframes-0.11.1/rust/src/report_names.rs +631 -0
  23. typedframes-0.11.1/rust/src/static_eval.rs +4682 -0
  24. typedframes-0.11.1/rust/src/suggest.rs +1287 -0
  25. typedframes-0.11.1/rust/src/words.txt +2048 -0
  26. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/__init__.py +1 -1
  27. typedframes-0.11.1/src/typedframes/_cache.py +134 -0
  28. typedframes-0.11.1/src/typedframes/_rust_checker.pyi +31 -0
  29. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/cli.py +698 -110
  30. typedframes-0.9.0/rust/src/pyapi.rs +0 -281
  31. typedframes-0.9.0/src/typedframes/_rust_checker.pyi +0 -16
  32. {typedframes-0.9.0 → typedframes-0.11.1}/rust/.cargo/config.toml +0 -0
  33. {typedframes-0.9.0 → typedframes-0.11.1}/rust/README.md +0 -0
  34. {typedframes-0.9.0 → typedframes-0.11.1}/rust/benches/parser_bench.rs +0 -0
  35. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/config.rs +0 -0
  36. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/constants.rs +0 -0
  37. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/contract.rs +0 -0
  38. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/frame_ops.rs +0 -0
  39. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/main.rs +0 -0
  40. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/sql.rs +0 -0
  41. {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/typo.rs +0 -0
  42. {typedframes-0.9.0 → typedframes-0.11.1}/rust/tests/integration_test.rs +0 -0
  43. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/base_schema.py +0 -0
  44. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column.py +0 -0
  45. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column_group.py +0 -0
  46. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column_group_error.py +0 -0
  47. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column_set.py +0 -0
  48. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/missing_dependency_error.py +0 -0
  49. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/mypy.py +0 -0
  50. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/pandas.py +0 -0
  51. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/pandera.py +0 -0
  52. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/polars.py +0 -0
  53. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/py.typed +0 -0
  54. {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/schema_algebra.py +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 William Martin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typedframes
3
- Version: 0.9.0
3
+ Version: 0.11.1
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Classifier: Programming Language :: Python :: 3.13
12
12
  Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Programming Language :: Python :: 3.15
13
14
  Classifier: Topic :: Software Development :: Quality Assurance
14
15
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
16
  Requires-Dist: mypy ; extra == 'mypy'
@@ -20,6 +21,7 @@ Provides-Extra: mypy
20
21
  Provides-Extra: pandas
21
22
  Provides-Extra: pandera
22
23
  Provides-Extra: polars
24
+ License-File: LICENSE
23
25
  Summary: Static analysis for pandas and polars DataFrames. Catch column errors at lint-time, not runtime.
24
26
  Keywords: pandas,polars,type-checking,static-analysis,dataframe,linter,mypy-plugin
25
27
  Home-Page: https://github.com/w-martin/typedframes
@@ -42,7 +44,7 @@ Project-URL: Repository, https://github.com/w-martin/typedframes
42
44
 
43
45
  > ⚠️ **Project Status: Proof of Concept**
44
46
  >
45
- > `typedframes` (v0.9.0) is currently an experimental proof-of-concept. The core static analysis and mypy/Rust
47
+ > `typedframes` (v0.11.1) is currently an experimental proof-of-concept. The core static analysis and mypy/Rust
46
48
  > integrations work, but expect rough edges. The codebase prioritizes demonstrating the viability of static DataFrame
47
49
  > column checking over production-grade stability.
48
50
  >
@@ -504,7 +506,7 @@ repos:
504
506
  name: typedframes check
505
507
  entry: typedframes check . --strict
506
508
  language: python
507
- additional_dependencies: ["typedframes==0.9.0"]
509
+ additional_dependencies: ["typedframes==0.11.1"]
508
510
  types_or: [python, jupyter]
509
511
  pass_filenames: false
510
512
  ```
@@ -553,7 +555,7 @@ The action installs the PyPI wheel into a throwaway virtualenv and runs the chec
553
555
  | Input | Default | |
554
556
  |-------|---------|-|
555
557
  | `path` | `.` | File or directory to check |
556
- | `version` | `latest` | PyPI version to install, e.g. `"0.9.0"` |
558
+ | `version` | `latest` | PyPI version to install, e.g. `"0.11.1"` |
557
559
  | `strict` | `true` | Fail the step on errors — `typedframes check` exits 0 without it |
558
560
  | `coverage-fail-under` | *(unset)* | Minimum DataFrame schema coverage, e.g. `"90"` |
559
561
  | `coverage-detail` | `summary` | Or `term-missing` for the per-file breakdown, `explain` to diagnose a lower-than-expected total |
@@ -667,17 +669,17 @@ plus the requested features, so both halves get checked normally. See
667
669
  Fast feedback reduces development time. The typedframes Rust binary provides near-instant column checking.
668
670
 
669
671
  **Benchmark results** (20 runs, 3 warmup, caches cleared between runs):
670
- *2026-09-30 · Darwin 27.0.0 · arm · CPython 3.14.4 · 64GiB RAM · Great Expectations pinned @ 1.20.0*
672
+ *2026-10-10 · Darwin 27.0.0 · arm · CPython 3.15.0 · 64GiB RAM · Great Expectations pinned @ 1.23.2*
671
673
 
672
- | Tool | Version | What it does | typedframes (13 files) | great_expectations (485 files) |
674
+ | Tool | Version | What it does | typedframes (14 files) | great_expectations (494 files) |
673
675
  |------|---------|--------------|------------------------|--------------------------------|
674
- | typedframes | 0.9.0 | DataFrame column checker | 51ms ±613µs (IQR 997µs) | 268ms ±4ms (IQR 5ms) |
675
- | ruff | 0.16.3 | Linter (no type checking) | 31ms ±735µs (IQR 882µs) | 237ms ±4ms (IQR 6ms) |
676
- | ty | 0.0.72 | Type checker | 74ms ±2ms (IQR 3ms) | 782ms ±12ms (IQR 17ms) |
677
- | pyrefly | 1.2.0 | Type checker | 98ms ±1ms (IQR 2ms) | 276ms ±8ms (IQR 14ms) |
678
- | mypy | 2.3.1 | Type checker (no plugin) | 2.73s ±37ms (IQR 43ms) | 4.30s ±72ms (IQR 106ms) |
679
- | mypy + typedframes | 2.3.1 | Type checker + column checker | 2.72s ±19ms (IQR 23ms) | 4.61s ±22ms (IQR 23ms) |
680
- | pyright | 1.1.411 | Type checker | 826ms ±11ms (IQR 11ms) | 3.34s ±26ms (IQR 40ms) |
676
+ | typedframes | 0.11.1 | DataFrame column checker | 45ms ±407µs (IQR 721µs) | 127ms ±1ms (IQR 1ms) |
677
+ | ruff | 0.16.10 | Linter (no type checking) | 21ms ±670µs (IQR 1ms) | 196ms ±2ms (IQR 3ms) |
678
+ | ty | 0.0.84 | Type checker | 70ms ±2ms (IQR 1ms) | 765ms ±9ms (IQR 12ms) |
679
+ | pyrefly | 1.3.2 | Type checker | 91ms ±2ms (IQR 3ms) | 269ms ±8ms (IQR 13ms) |
680
+ | mypy | 2.4.0 | Type checker (no plugin) | 1.96s ±43ms (IQR 72ms) | 3.12s ±37ms (IQR 59ms) |
681
+ | mypy + typedframes | 2.4.0 | Type checker + column checker | 1.89s ±10ms (IQR 15ms) | 3.53s ±39ms (IQR 67ms) |
682
+ | pyright | 1.1.414 | Type checker | 917ms ±10ms (IQR 13ms) | 4.03s ±36ms (IQR 61ms) |
681
683
 
682
684
  *Run `uv run python benchmarks/benchmark_checkers.py` to reproduce.*
683
685
 
@@ -694,6 +696,35 @@ parsing.
694
696
 
695
697
  **Note:** ty (Astral) does not currently support mypy plugins, so use the standalone binary for column checking with ty.
696
698
 
699
+ ### Index Cache and Parallelism
700
+
701
+ 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
702
+ a number to cap the workers.
703
+
704
+ What the checker learns about each file is also cached on disk and reused for every file whose contents have not
705
+ changed, so repeat runs only re-index what you edited. A cached run reports exactly what an uncached run would. The cache
706
+ lives in your per-user cache directory by default (`~/Library/Caches/typedframes` on macOS, `~/.cache/typedframes` on
707
+ Linux), so nothing is added to your repository.
708
+
709
+ ```shell
710
+ typedframes cache path # where is it?
711
+ typedframes cache clear # delete it
712
+ typedframes check src/ --no-cache # skip it for one run
713
+ typedframes check src/ --cache-dir .typedframes_cache # keep it in the project instead
714
+ typedframes cache gitignore # add a project-local cache directory to .gitignore
715
+ ```
716
+
717
+ To set it permanently, use the `TYPEDFRAMES_CACHE_DIR` environment variable or `pyproject.toml`; a relative path is
718
+ relative to the project:
719
+
720
+ ```toml
721
+ [tool.typedframes]
722
+ cache_dir = ".typedframes_cache"
723
+ ```
724
+
725
+ The order of precedence is `--no-cache`, `--cache-dir`, `TYPEDFRAMES_CACHE_DIR`, `cache_dir`, then the default. See
726
+ `typedframes cache --help`.
727
+
697
728
  ---
698
729
 
699
730
  ## Type Safety With Multiple Backends
@@ -893,7 +924,7 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
893
924
 
894
925
  | Feature | typedframes | Pandera | Great Expectations | strictly_typed_pandas | pandas-stubs | dataenforce | pandas-type-checks | StaticFrame | narwhals | dataframely | patito |
895
926
  |---------------------------------|------------------------|-------------|--------------------|-----------------------|--------------|-------------|--------------------|------------------|----------|------------------|------------------|
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 |
927
+ | **Version tested** | 0.11.1 | 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
928
  | **Analysis Type** |
898
929
  | When errors are caught | **Static (lint-time)** | Runtime | Runtime | Runtime | Static | Runtime | Runtime | Runtime | Runtime | Runtime | Runtime |
899
930
  | **Static Analysis (our focus)** |
@@ -913,27 +944,28 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
913
944
  | Pandas | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ❌ Own | ✅ Yes | ❌ No | ⚠️ Limited |
914
945
  | Polars | ✅ Yes | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ Own | ✅ Yes | ✅ Yes (only) | ✅ Yes |
915
946
  | DuckDB, cuDF, etc. | ❌ No | ❌ No | ✅ Spark, SQL | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ✅ Yes | ❌ No | ❌ No |
916
- | **Project Status (Aug 2026)** |
947
+ | **Project Status (Oct 2026)** |
917
948
  | Active development | ✅ Yes | ✅ Yes | ✅ Yes | ⚠️ Low | ✅ Yes | ❌ Inactive | ⚠️ Low | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
918
949
 
919
950
  **Legend:** ✅ Full support | ⚠️ Limited/Partial | ❌ Not supported
920
951
 
921
952
  ### Tool Descriptions
922
953
 
923
- - **[Pandera](https://pandera.readthedocs.io/)** (v0.32.1): Excellent runtime validation. Static analysis support exists
954
+ - **[Pandera](https://pandera.readthedocs.io/)** (v0.34.1): Excellent runtime validation. Static analysis support exists
924
955
  but has limitations—column access via `df["column"]` is not validated, and schema mismatches between functions may not
925
956
  be caught.
926
957
 
927
958
  - **[strictly_typed_pandas](https://strictly-typed-pandas.readthedocs.io/)** (v0.3.7): Provides `DataSet[Schema]` type
928
959
  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.
960
+ column access errors are not caught statically. No standalone checker. No polars support. Pinned to
961
+ `pandas<=2.2.3` and `pandas-stubs<=2.2.3.250308`, so it can't be used with pandas 3.
930
962
 
931
- - **[pandas-stubs](https://github.com/pandas-dev/pandas-stubs)** (v3.0.5): Official pandas type stubs. Provides
963
+ - **[pandas-stubs](https://github.com/pandas-dev/pandas-stubs)** (v3.0.5.260914): Official pandas type stubs. Provides
932
964
  API-level types but no column-level checking.
933
965
 
934
966
  - **[dataenforce](https://github.com/CedricFR/dataenforce)** (v0.1.2, the only release ever published): Runtime
935
967
  validation via decorator. Appears inactive/abandoned. Broken on every currently-supported Python version
936
- (3.11 through 3.14) due to removal of internal typing APIs (`typing._TypingEmpty`) it depends on — confirmed
968
+ (3.11 through 3.15) due to removal of internal typing APIs (`typing._TypingEmpty`) it depends on — confirmed
937
969
  working only as far back as Python 3.9.
938
970
 
939
971
  - **[pandas-type-checks](https://pypi.org/project/pandas-type-checks/)** (v1.1.3): Runtime validation decorator. No
@@ -943,17 +975,17 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
943
975
  Not compatible with pandas/polars — requires a full rewrite to StaticFrame's own API. Column access is still
944
976
  string-based; mypy does not catch column name typos. Type safety comes from immutability guarantees, not schema checking.
945
977
 
946
- - **[narwhals](https://narwhals-dev.github.io/narwhals/)** (v2.24.0): Compatibility layer that provides a unified API
978
+ - **[narwhals](https://narwhals-dev.github.io/narwhals/)** (v2.26.0): Compatibility layer that provides a unified API
947
979
  across pandas, polars, DuckDB, cuDF, and more. Solves a different problem—write-once-run-anywhere portability, not
948
980
  type safety. See [Why Abstraction Layers Don't Solve Type Safety](#why-abstraction-layers-dont-solve-type-safety)
949
981
  below.
950
982
 
951
- - **[Great Expectations](https://greatexpectations.io/)** (v1.20.0): Comprehensive data quality framework. Defines
983
+ - **[Great Expectations](https://greatexpectations.io/)** (v1.23.2): Comprehensive data quality framework. Defines
952
984
  "expectations" (assertions) about data values, distributions, and schema properties. Excellent for runtime
953
985
  validation, data documentation, and data quality monitoring. No static analysis or column-level type checking in
954
986
  code. Supports pandas, Spark, and SQL backends.
955
987
 
956
- - **[dataframely](https://github.com/Quantco/dataframely)** (v3.0.0): Polars-only runtime validation library from Quantco.
988
+ - **[dataframely](https://github.com/Quantco/dataframely)** (v3.1.2): Polars-only runtime validation library from Quantco.
957
989
  Schemas are defined as classes inheriting `dy.Schema` with typed descriptor fields (`dy.String()`, `dy.Float64()`)
958
990
  and `@dy.rule()` decorators for cross-column and group-level constraints. Returns `dy.DataFrame[Schema]` generic
959
991
  types that give call-site narrowing to type checkers, but does not validate column subscript access inside function
@@ -971,16 +1003,16 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
971
1003
  These are general Python type checkers. They don't validate DataFrame column names, but they can be used alongside
972
1004
  typedframes for comprehensive type checking:
973
1005
 
974
- - **[mypy](https://mypy-lang.org/)** (v2.3.1): The original Python type checker. typedframes provides a mypy plugin for
1006
+ - **[mypy](https://mypy-lang.org/)** (v2.4.0): The original Python type checker. typedframes provides a mypy plugin for
975
1007
  column checking. See [performance benchmarks](#static-analysis-performance).
976
1008
 
977
- - **[ty](https://github.com/astral-sh/ty)** (v0.0.72, Astral): New Rust-based type checker, faster than mypy on
1009
+ - **[ty](https://github.com/astral-sh/ty)** (v0.0.84, Astral): New Rust-based type checker, faster than mypy on
978
1010
  large codebases. Does not support mypy plugins—use typedframes standalone checker.
979
1011
 
980
- - **[pyrefly](https://pyrefly.org/)** (v1.2.0, Meta): Rust-based type checker from Meta, replacement for Pyre. Fast,
1012
+ - **[pyrefly](https://pyrefly.org/)** (v1.3.2, Meta): Rust-based type checker from Meta, replacement for Pyre. Fast,
981
1013
  but no DataFrame column checking.
982
1014
 
983
- - **[pyright](https://github.com/microsoft/pyright)** (v1.1.411, Microsoft): Type checker powering Pylance/VSCode. No
1015
+ - **[pyright](https://github.com/microsoft/pyright)** (v1.1.414, Microsoft): Type checker powering Pylance/VSCode. No
984
1016
  mypy plugin support—use typedframes standalone checker.
985
1017
 
986
1018
  ### Not Directly Comparable
@@ -8,7 +8,7 @@
8
8
 
9
9
  > ⚠️ **Project Status: Proof of Concept**
10
10
  >
11
- > `typedframes` (v0.9.0) is currently an experimental proof-of-concept. The core static analysis and mypy/Rust
11
+ > `typedframes` (v0.11.1) 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.9.0"]
473
+ additional_dependencies: ["typedframes==0.11.1"]
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.9.0"` |
522
+ | `version` | `latest` | PyPI version to install, e.g. `"0.11.1"` |
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-30 · Darwin 27.0.0 · arm · CPython 3.14.4 · 64GiB RAM · Great Expectations pinned @ 1.20.0*
636
+ *2026-10-10 · Darwin 27.0.0 · arm · CPython 3.15.0 · 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.9.0 | DataFrame column checker | 51ms ±613µs (IQR 997µs) | 268ms ±4ms (IQR 5ms) |
641
- | ruff | 0.16.3 | Linter (no type checking) | 31ms ±735µs (IQR 882µs) | 237ms ±4ms (IQR 6ms) |
642
- | ty | 0.0.72 | Type checker | 74ms ±2ms (IQR 3ms) | 782ms ±12ms (IQR 17ms) |
643
- | pyrefly | 1.2.0 | Type checker | 98ms ±1ms (IQR 2ms) | 276ms ±8ms (IQR 14ms) |
644
- | mypy | 2.3.1 | Type checker (no plugin) | 2.73s ±37ms (IQR 43ms) | 4.30s ±72ms (IQR 106ms) |
645
- | mypy + typedframes | 2.3.1 | Type checker + column checker | 2.72s ±19ms (IQR 23ms) | 4.61s ±22ms (IQR 23ms) |
646
- | pyright | 1.1.411 | Type checker | 826ms ±11ms (IQR 11ms) | 3.34s ±26ms (IQR 40ms) |
640
+ | typedframes | 0.11.1 | DataFrame column checker | 45ms ±407µs (IQR 721µs) | 127ms ±1ms (IQR 1ms) |
641
+ | ruff | 0.16.10 | Linter (no type checking) | 21ms ±670µs (IQR 1ms) | 196ms ±2ms (IQR 3ms) |
642
+ | ty | 0.0.84 | Type checker | 70ms ±2ms (IQR 1ms) | 765ms ±9ms (IQR 12ms) |
643
+ | pyrefly | 1.3.2 | Type checker | 91ms ±2ms (IQR 3ms) | 269ms ±8ms (IQR 13ms) |
644
+ | mypy | 2.4.0 | Type checker (no plugin) | 1.96s ±43ms (IQR 72ms) | 3.12s ±37ms (IQR 59ms) |
645
+ | mypy + typedframes | 2.4.0 | Type checker + column checker | 1.89s ±10ms (IQR 15ms) | 3.53s ±39ms (IQR 67ms) |
646
+ | pyright | 1.1.414 | Type checker | 917ms ±10ms (IQR 13ms) | 4.03s ±36ms (IQR 61ms) |
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.11.1 | 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,27 +908,28 @@ 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
901
931
  validation via decorator. Appears inactive/abandoned. Broken on every currently-supported Python version
902
- (3.11 through 3.14) due to removal of internal typing APIs (`typing._TypingEmpty`) it depends on — confirmed
932
+ (3.11 through 3.15) due to removal of internal typing APIs (`typing._TypingEmpty`) it depends on — confirmed
903
933
  working only as far back as Python 3.9.
904
934
 
905
935
  - **[pandas-type-checks](https://pypi.org/project/pandas-type-checks/)** (v1.1.3): Runtime validation decorator. No
@@ -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.9.0"
7
+ version = "0.11.1"
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"
@@ -23,6 +23,7 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.12",
24
24
  "Programming Language :: Python :: 3.13",
25
25
  "Programming Language :: Python :: 3.14",
26
+ "Programming Language :: Python :: 3.15",
26
27
  "Topic :: Software Development :: Quality Assurance",
27
28
  "Topic :: Software Development :: Libraries :: Python Modules",
28
29
  ]
@@ -48,6 +49,7 @@ manifest-path = "rust/Cargo.toml"
48
49
  python-source = "src"
49
50
  module-name = "typedframes._rust_checker"
50
51
  features = ["pyo3/extension-module"]
52
+ include = [{ path = "LICENSE", format = "sdist" }]
51
53
 
52
54
  [tool.pytest.ini_options]
53
55
  addopts = "--cov=. --cov-report=term-missing --cov-report=json --cov-report=lcov --cov-branch -n auto"
@@ -128,7 +130,7 @@ docs = [
128
130
  ]
129
131
  dev = [
130
132
  "pandas>=3.0.5",
131
- "pandera>=0.32.1",
133
+ "pandera>=0.34.1",
132
134
  "pip-audit>=2.10.1",
133
135
  "polars>=1.43.2",
134
136
  "bandit>=1.9.4",
@@ -137,19 +139,19 @@ dev = [
137
139
  "invoke>=3.0.3",
138
140
  "maturin>=1.14.1",
139
141
  "pre-commit>=4.6.1",
140
- "pyrefly>=1.2.0",
141
- "pyright>=1.1.411",
142
+ "pyrefly>=1.3.2",
143
+ "pyright>=1.1.414",
142
144
  "pytest>=9.1.1",
143
145
  "pytest-cov>=7.1.0",
144
146
  "pytest-xdist>=3.8.0",
145
- "ruff>=0.16.3",
146
- "ty>=0.0.72",
147
+ "ruff>=0.16.10",
148
+ "ty>=0.0.84",
147
149
  "openpyxl>=3.1.5",
148
150
  "pyarrow>=25.0.1",
149
151
  "xlsxwriter>=3.2.9",
150
152
  "fastexcel>=0.20.2",
151
- "pandas-stubs>=3.0.5.260730",
152
- "mypy>=2.3.1",
153
+ "pandas-stubs>=3.0.5.260914",
154
+ "mypy>=2.4.0",
153
155
  "trustedlicenses>=0.1.1",
154
156
  ]
155
157
 
@@ -1347,7 +1347,7 @@ checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
1347
1347
 
1348
1348
  [[package]]
1349
1349
  name = "typedframes_checker"
1350
- version = "0.9.0"
1350
+ version = "0.11.1"
1351
1351
  dependencies = [
1352
1352
  "anyhow",
1353
1353
  "criterion",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "typedframes_checker"
3
- version = "0.9.0"
3
+ version = "0.11.1"
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
+ }