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.
- typedframes-0.11.1/LICENSE +21 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/PKG-INFO +58 -26
- {typedframes-0.9.0 → typedframes-0.11.1}/README.md +55 -25
- {typedframes-0.9.0 → typedframes-0.11.1}/pyproject.toml +10 -8
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/Cargo.lock +1 -1
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/Cargo.toml +1 -1
- typedframes-0.11.1/rust/build.rs +35 -0
- typedframes-0.11.1/rust/src/apply.rs +854 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/ast_extract.rs +184 -0
- typedframes-0.11.1/rust/src/column_consuming_methods.rs +90 -0
- typedframes-0.11.1/rust/src/column_usage.rs +1798 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/errors.rs +23 -0
- typedframes-0.11.1/rust/src/helpers.rs +309 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/index.rs +547 -20
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/lib.rs +15 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/linter.rs +3122 -689
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/notebook.rs +49 -0
- typedframes-0.11.1/rust/src/parallel.rs +107 -0
- typedframes-0.11.1/rust/src/phrase.rs +233 -0
- typedframes-0.11.1/rust/src/pyapi.rs +572 -0
- typedframes-0.11.1/rust/src/report.rs +1229 -0
- typedframes-0.11.1/rust/src/report_names.rs +631 -0
- typedframes-0.11.1/rust/src/static_eval.rs +4682 -0
- typedframes-0.11.1/rust/src/suggest.rs +1287 -0
- typedframes-0.11.1/rust/src/words.txt +2048 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/__init__.py +1 -1
- typedframes-0.11.1/src/typedframes/_cache.py +134 -0
- typedframes-0.11.1/src/typedframes/_rust_checker.pyi +31 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/cli.py +698 -110
- 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.11.1}/rust/.cargo/config.toml +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/README.md +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/benches/parser_bench.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/config.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/constants.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/contract.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/frame_ops.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/main.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/sql.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/src/typo.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/rust/tests/integration_test.rs +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/base_schema.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column_group.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column_group_error.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/column_set.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/missing_dependency_error.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/mypy.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/pandas.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/pandera.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/polars.py +0 -0
- {typedframes-0.9.0 → typedframes-0.11.1}/src/typedframes/py.typed +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
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 (
|
|
674
|
+
| Tool | Version | What it does | typedframes (14 files) | great_expectations (494 files) |
|
|
673
675
|
|------|---------|--------------|------------------------|--------------------------------|
|
|
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.
|
|
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.
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
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 (
|
|
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.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.
|
|
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 (
|
|
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
|
|
901
931
|
validation via decorator. Appears inactive/abandoned. Broken on every currently-supported Python version
|
|
902
|
-
(3.11 through 3.
|
|
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.
|
|
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.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.
|
|
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
|
|
141
|
-
"pyright>=1.1.
|
|
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.
|
|
146
|
-
"ty>=0.0.
|
|
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.
|
|
152
|
-
"mypy>=2.
|
|
153
|
+
"pandas-stubs>=3.0.5.260914",
|
|
154
|
+
"mypy>=2.4.0",
|
|
153
155
|
"trustedlicenses>=0.1.1",
|
|
154
156
|
]
|
|
155
157
|
|
|
@@ -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
|
+
}
|