typedframes 0.9.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.
- {typedframes-0.9.0 → typedframes-0.10.0}/PKG-INFO +55 -25
- {typedframes-0.9.0 → typedframes-0.10.0}/README.md +54 -24
- {typedframes-0.9.0 → typedframes-0.10.0}/pyproject.toml +8 -8
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/Cargo.lock +1 -1
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/Cargo.toml +1 -1
- typedframes-0.10.0/rust/build.rs +35 -0
- typedframes-0.10.0/rust/src/apply.rs +852 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/ast_extract.rs +184 -0
- typedframes-0.10.0/rust/src/column_consuming_methods.rs +85 -0
- typedframes-0.10.0/rust/src/column_usage.rs +1798 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/errors.rs +21 -0
- typedframes-0.10.0/rust/src/helpers.rs +309 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/index.rs +512 -20
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/lib.rs +11 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/linter.rs +3028 -695
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/notebook.rs +46 -0
- typedframes-0.10.0/rust/src/parallel.rs +107 -0
- typedframes-0.10.0/rust/src/pyapi.rs +539 -0
- typedframes-0.10.0/rust/src/static_eval.rs +4682 -0
- typedframes-0.10.0/rust/src/suggest.rs +1061 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/__init__.py +1 -1
- typedframes-0.10.0/src/typedframes/_cache.py +109 -0
- typedframes-0.10.0/src/typedframes/_rust_checker.pyi +27 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/cli.py +484 -105
- typedframes-0.9.0/rust/src/pyapi.rs +0 -281
- typedframes-0.9.0/src/typedframes/_rust_checker.pyi +0 -16
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/.cargo/config.toml +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/README.md +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/benches/parser_bench.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/config.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/constants.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/contract.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/frame_ops.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/main.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/sql.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/src/typo.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/rust/tests/integration_test.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/base_schema.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/column.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/column_group.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/column_group_error.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/column_set.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/missing_dependency_error.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/mypy.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/pandas.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/pandera.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/polars.py +0 -0
- {typedframes-0.9.0 → typedframes-0.10.0}/src/typedframes/py.typed +0 -0
- {typedframes-0.9.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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
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 (
|
|
672
|
+
| Tool | Version | What it does | typedframes (14 files) | great_expectations (494 files) |
|
|
673
673
|
|------|---------|--------------|------------------------|--------------------------------|
|
|
674
|
-
| typedframes | 0.
|
|
675
|
-
| ruff | 0.16.
|
|
676
|
-
| ty | 0.0.
|
|
677
|
-
| pyrefly | 1.2
|
|
678
|
-
| mypy | 2.
|
|
679
|
-
| mypy + typedframes | 2.
|
|
680
|
-
| pyright | 1.1.
|
|
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.
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
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 (
|
|
638
|
+
| Tool | Version | What it does | typedframes (14 files) | great_expectations (494 files) |
|
|
639
639
|
|------|---------|--------------|------------------------|--------------------------------|
|
|
640
|
-
| typedframes | 0.
|
|
641
|
-
| ruff | 0.16.
|
|
642
|
-
| ty | 0.0.
|
|
643
|
-
| pyrefly | 1.2
|
|
644
|
-
| mypy | 2.
|
|
645
|
-
| mypy + typedframes | 2.
|
|
646
|
-
| pyright | 1.1.
|
|
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.
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
141
|
-
"pyright>=1.1.
|
|
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.
|
|
146
|
-
"ty>=0.0.
|
|
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.
|
|
152
|
-
"mypy>=2.
|
|
151
|
+
"pandas-stubs>=3.0.5.260914",
|
|
152
|
+
"mypy>=2.4.0",
|
|
153
153
|
"trustedlicenses>=0.1.1",
|
|
154
154
|
]
|
|
155
155
|
|
|
@@ -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
|
+
}
|