typedframes 0.4.1__tar.gz → 0.5.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.4.1 → typedframes-0.5.0}/PKG-INFO +44 -241
- {typedframes-0.4.1 → typedframes-0.5.0}/README.md +43 -240
- {typedframes-0.4.1 → typedframes-0.5.0}/pyproject.toml +20 -1
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/Cargo.lock +1 -1
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/Cargo.toml +1 -1
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/ast_extract.rs +70 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/constants.rs +11 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/errors.rs +1 -1
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/index.rs +60 -10
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/linter.rs +720 -5
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/__init__.py +1 -1
- typedframes-0.5.0/src/typedframes/_rust_checker.pyi +12 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/cli.py +49 -44
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/mypy.py +2 -2
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/pandas.py +7 -7
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/polars.py +4 -4
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/.cargo/config.toml +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/README.md +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/benches/parser_bench.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/config.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/contract.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/lib.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/main.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/notebook.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/pyapi.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/sql.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/src/typo.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/rust/tests/integration_test.rs +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/base_schema.py +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/column.py +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/column_group.py +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/column_group_error.py +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/column_set.py +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/missing_dependency_error.py +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/pandera.py +0 -0
- {typedframes-0.4.1 → typedframes-0.5.0}/src/typedframes/py.typed +0 -0
- {typedframes-0.4.1 → typedframes-0.5.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.5.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.5.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
|
>
|
|
@@ -59,13 +59,13 @@ print(orders["revenue"]) # ✗ unknown-column — 'revenue' not in inferred col
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
```shell
|
|
62
|
-
typedframes check src/ --fail-under=90
|
|
62
|
+
typedframes check src/ --coverage-fail-under=90
|
|
63
63
|
# src/pipeline.py:7:8: error[unknown-column] Column 'revenue' does not exist in inferred column set (defined at line 6)
|
|
64
64
|
# ✗ Found 1 error in 12 files (0.0s)
|
|
65
65
|
# ✗ DataFrame schema coverage 82.0% is below the required 90.0% (9/11 DataFrames had column info)
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
-
`--fail-under=N` is the same idea as a test-coverage or mypy type-coverage gate, applied to how many DataFrames
|
|
68
|
+
`--coverage-fail-under=N` is the same idea as a test-coverage or mypy type-coverage gate, applied to how many DataFrames
|
|
69
69
|
the checker can resolve columns for — see [DataFrame Schema Coverage Thresholds](#dataframe-schema-coverage-thresholds-opt-in).
|
|
70
70
|
Add `BaseSchema` classes later for cross-file awareness and IDE autocomplete — see [Quick Start](#quick-start).
|
|
71
71
|
|
|
@@ -104,7 +104,7 @@ tracking and IDE autocomplete; they're a progressive enhancement, not a prerequi
|
|
|
104
104
|
**What you get:**
|
|
105
105
|
|
|
106
106
|
- ✅ **Works without schema annotations** - Column inference from `usecols=`, `dtype=`, and method chains catches errors on unannotated code
|
|
107
|
-
- ✅ **CI gate via coverage threshold** - `--fail-under=N` fails the build when too much of your codebase is invisible to the checker, the same way you'd gate on test coverage or a type checker's type-coverage number
|
|
107
|
+
- ✅ **CI gate via coverage threshold** - `--coverage-fail-under=N` fails the build when too much of your codebase is invisible to the checker, the same way you'd gate on test coverage or a type checker's type-coverage number
|
|
108
108
|
- ✅ **Rust-fast** - Milliseconds, not seconds, even on hundreds of files; fast enough for pre-commit hooks and CI (see [benchmarks](#static-analysis-performance))
|
|
109
109
|
- ✅ **Cross-file awareness** - Add `BaseSchema` and typed return annotations to follow schemas across module boundaries
|
|
110
110
|
- ✅ **Refactor-safe access** - `df[Schema.column_group.s].mean()` (pandas) or `df.select(Schema.col.col)` (polars) instead of scattered string literals
|
|
@@ -125,7 +125,13 @@ or
|
|
|
125
125
|
uv add typedframes
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
The Rust-based checker is included — no separate install needed.
|
|
128
|
+
The Rust-based checker is included — no separate install needed. `typedframes` itself has
|
|
129
|
+
no required dependencies; add the extra for whichever backend(s) your code uses:
|
|
130
|
+
|
|
131
|
+
```shell
|
|
132
|
+
pip install typedframes[pandas] # includes pandas
|
|
133
|
+
pip install typedframes[polars] # includes polars
|
|
134
|
+
```
|
|
129
135
|
|
|
130
136
|
---
|
|
131
137
|
|
|
@@ -429,10 +435,10 @@ typedframes check src/ --no-index
|
|
|
429
435
|
typedframes check src/ --no-warnings
|
|
430
436
|
|
|
431
437
|
# Enforce minimum DataFrame schema coverage (see below)
|
|
432
|
-
typedframes check src/ --fail-under=90
|
|
438
|
+
typedframes check src/ --coverage-fail-under=90
|
|
433
439
|
|
|
434
440
|
# Show which DataFrames lack column info, per file
|
|
435
|
-
typedframes check src/ --coverage-
|
|
441
|
+
typedframes check src/ --coverage-detail=term-missing
|
|
436
442
|
```
|
|
437
443
|
|
|
438
444
|
To suppress warnings project-wide, add to `pyproject.toml`:
|
|
@@ -488,103 +494,27 @@ code reference.
|
|
|
488
494
|
resolve column information for — the analogue of the "type coverage" reported by mypy,
|
|
489
495
|
pyright, and pyre, and unrelated to test coverage. That number is informational by
|
|
490
496
|
default. If you want it enforced — failing the run when too much of your code is
|
|
491
|
-
invisible to the checker — enable a threshold
|
|
492
|
-
|
|
493
|
-
This is **entirely opt-in**. With no `[tool.typedframes.coverage]` table and no
|
|
494
|
-
`--fail-under`, nothing changes: no threshold, no exit-code difference.
|
|
495
|
-
|
|
496
|
-
Every supported key, at its default value:
|
|
497
|
+
invisible to the checker — enable a threshold:
|
|
497
498
|
|
|
498
499
|
```toml
|
|
499
500
|
[tool.typedframes.coverage]
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
enabled = false
|
|
503
|
-
|
|
504
|
-
# Minimum percentage of DataFrames that must have recognized column/schema info
|
|
505
|
-
# before `typedframes check` exits 1. Only consulted when `enabled = true`.
|
|
506
|
-
# Applies to every file not captured by a glob in [overrides] below.
|
|
507
|
-
fail_under = 100.0
|
|
508
|
-
|
|
509
|
-
# How much coverage detail to print after each check. One of:
|
|
510
|
-
# "summary" one line (the default, unchanged from before this feature)
|
|
511
|
-
# "term-missing" per-file table plus the DataFrame sites lacking column info
|
|
512
|
-
# "json" machine-readable document, for CI tooling
|
|
513
|
-
# Independent of `enabled` — a detailed report is useful without a gate, and
|
|
514
|
-
# vice versa. Overridden by `--coverage-report`.
|
|
515
|
-
report = "summary"
|
|
516
|
-
|
|
517
|
-
[tool.typedframes.coverage.overrides]
|
|
518
|
-
# Per-path glob overrides of `fail_under`, for holding legacy code to a lower bar
|
|
519
|
-
# than new code. Each glob is graded on its own files as a separate group, so a
|
|
520
|
-
# lenient legacy bucket can't drag down (or rescue) the rest of the project.
|
|
521
|
-
# Paths are matched project-relative: `**` spans any number of directories,
|
|
522
|
-
# `*` and `?` stay within one path segment.
|
|
523
|
-
# When several globs match one file the most specific wins — longest literal
|
|
524
|
-
# prefix before the first `*` or `?`. Files matching no glob use `fail_under`.
|
|
525
|
-
# "legacy/**" = 50.0
|
|
526
|
-
# "src/new_module/**" = 100.0
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
Prefer to keep `pyproject.toml` clean? The same settings work in a standalone
|
|
530
|
-
`typedframes.toml` at the project root, with the `[tool.typedframes]` prefix dropped
|
|
531
|
-
(the way `ruff.toml` drops `[tool.ruff]`):
|
|
532
|
-
|
|
533
|
-
```toml
|
|
534
|
-
# typedframes.toml
|
|
535
|
-
[coverage]
|
|
536
|
-
enabled = false
|
|
537
|
-
fail_under = 100.0
|
|
538
|
-
report = "summary"
|
|
539
|
-
|
|
540
|
-
[coverage.overrides]
|
|
541
|
-
# "legacy/**" = 50.0
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
If both files exist, `typedframes.toml` wins **entirely** — the two are never merged,
|
|
545
|
-
so exactly one file explains the whole configuration.
|
|
546
|
-
|
|
547
|
-
### Seeing What's Missing
|
|
548
|
-
|
|
549
|
-
The default one-line DataFrame schema coverage summary tells you the ratio but not what
|
|
550
|
-
to fix. `--coverage-report=term-missing` names the DataFrames that cost you coverage:
|
|
551
|
-
|
|
552
|
-
```shell
|
|
553
|
-
typedframes check src/ --coverage-report=term-missing
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
```
|
|
557
|
-
Name Typed Total Cover Missing
|
|
558
|
-
---------------------------------------------
|
|
559
|
-
legacy/old.py 0 2 0% old_one:2, old_two:3
|
|
560
|
-
src/new.py 1 2 50% bad:3
|
|
561
|
-
---------------------------------------------
|
|
562
|
-
TOTAL 1 4 25%
|
|
501
|
+
enabled = true
|
|
502
|
+
fail_under = 90.0
|
|
563
503
|
```
|
|
564
504
|
|
|
565
|
-
|
|
566
|
-
DataFrame but couldn't resolve its columns. Fix those (add `usecols=`, name the columns
|
|
567
|
-
in the `SELECT`, or annotate the variable) and coverage rises.
|
|
568
|
-
|
|
569
|
-
For CI tooling, `--coverage-report=json` emits the same data as a document. Combine it
|
|
570
|
-
with `--output-format=json` and the coverage report is nested under a `coverage` key so
|
|
571
|
-
stdout stays a single valid JSON document:
|
|
505
|
+
or as a one-off, without touching config:
|
|
572
506
|
|
|
573
507
|
```shell
|
|
574
|
-
typedframes check src/ --
|
|
508
|
+
typedframes check src/ --coverage-fail-under=90
|
|
575
509
|
```
|
|
576
510
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
- A group with no recognized DataFrames passes: 0/0 means there was nothing to
|
|
585
|
-
measure, not that something failed.
|
|
586
|
-
- A failed threshold exits **1** and is reported even under `--no-info` — that flag
|
|
587
|
-
silences the informational summary line, not a gate result.
|
|
511
|
+
This is **entirely opt-in** — with no `[tool.typedframes.coverage]` table and no
|
|
512
|
+
`--coverage-fail-under`, nothing changes: no threshold, no exit-code difference. Per-path
|
|
513
|
+
overrides (e.g. a lower bar for `legacy/**`), a `--coverage-detail=term-missing`
|
|
514
|
+
breakdown of exactly which DataFrames cost you coverage (as text or, combined with
|
|
515
|
+
`--output-format=json`, as structured JSON), and the full config reference all live in
|
|
516
|
+
the
|
|
517
|
+
[DataFrame schema coverage thresholds guide](docs/usage.md#dataframe-schema-coverage-thresholds).
|
|
588
518
|
|
|
589
519
|
## Static Analysis Performance
|
|
590
520
|
|
|
@@ -824,6 +754,7 @@ Comprehensive comparison of pandas/DataFrame typing and validation tools. **type
|
|
|
824
754
|
| Column name checking | ✅ Yes | ⚠️ Limited | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No |
|
|
825
755
|
| Column type checking | ✅ Yes | ⚠️ Limited | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No |
|
|
826
756
|
| Typo suggestions | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No |
|
|
757
|
+
| Coverage gate (`--coverage-fail-under`) | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No |
|
|
827
758
|
| **Runtime Validation** |
|
|
828
759
|
| Data validation | ❌ No | ✅ Excellent | ✅ Excellent | ✅ typeguard | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes |
|
|
829
760
|
| Value constraints | ❌ No | ✅ Yes | ✅ Excellent | ❌ No | ❌ No | ❌ No | ❌ No | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes |
|
|
@@ -967,122 +898,23 @@ The conversion maps:
|
|
|
967
898
|
|
|
968
899
|
## Examples
|
|
969
900
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
df: Annotated[pd.DataFrame, Orders] = pd.read_csv("orders.csv")
|
|
990
|
-
revenue = calculate_revenue(df)
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
### Time Series Analysis
|
|
994
|
-
|
|
995
|
-
```python
|
|
996
|
-
from typing import Annotated
|
|
997
|
-
import pandas as pd
|
|
998
|
-
from typedframes import BaseSchema, Column, ColumnSet, ColumnGroup
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
class SensorData(BaseSchema):
|
|
1002
|
-
timestamp = Column(type=str)
|
|
1003
|
-
temperature = ColumnSet(type=float, members=["temp_1", "temp_2", "temp_3"])
|
|
1004
|
-
humidity = ColumnSet(type=float, members=["humidity_1", "humidity_2"])
|
|
1005
|
-
|
|
1006
|
-
all_sensors = ColumnGroup(members=[temperature, humidity])
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
df: Annotated[pd.DataFrame, SensorData] = pd.read_csv("sensors.csv")
|
|
1010
|
-
|
|
1011
|
-
# Clean, type-safe operations using .s for column name lists
|
|
1012
|
-
avg_temp_per_row = df[SensorData.temperature.s].mean(axis=1)
|
|
1013
|
-
all_readings_stats = df[SensorData.all_sensors.s].describe()
|
|
1014
|
-
```
|
|
1015
|
-
|
|
1016
|
-
### Multi-Step Pipeline
|
|
1017
|
-
|
|
1018
|
-
```python
|
|
1019
|
-
from typing import Annotated
|
|
1020
|
-
import pandas as pd
|
|
1021
|
-
from typedframes import BaseSchema, Column
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
class RawSales(BaseSchema):
|
|
1025
|
-
date = Column(type=str)
|
|
1026
|
-
product_id = Column(type=int)
|
|
1027
|
-
quantity = Column(type=int)
|
|
1028
|
-
price = Column(type=float)
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
class AggregatedSales(BaseSchema):
|
|
1032
|
-
date = Column(type=str)
|
|
1033
|
-
total_revenue = Column(type=float)
|
|
1034
|
-
total_quantity = Column(type=int)
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
def aggregate_daily(df: Annotated[pd.DataFrame, RawSales]) -> Annotated[pd.DataFrame, AggregatedSales]:
|
|
1038
|
-
result = (
|
|
1039
|
-
df.groupby(RawSales.date.s)
|
|
1040
|
-
.agg(
|
|
1041
|
-
{
|
|
1042
|
-
RawSales.price.s: "sum",
|
|
1043
|
-
RawSales.quantity.s: "sum",
|
|
1044
|
-
}
|
|
1045
|
-
)
|
|
1046
|
-
.reset_index()
|
|
1047
|
-
)
|
|
1048
|
-
result.columns = pd.Index(["date", "total_revenue", "total_quantity"])
|
|
1049
|
-
return result # type: ignore[return-value]
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
# Type-safe pipeline
|
|
1053
|
-
raw: Annotated[pd.DataFrame, RawSales] = pd.read_csv("sales.csv")
|
|
1054
|
-
aggregated = aggregate_daily(raw)
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
# Type checker validates schema transformations
|
|
1058
|
-
def analyze(df: Annotated[pd.DataFrame, AggregatedSales]) -> float:
|
|
1059
|
-
df["total_revenue"] # ✓ OK
|
|
1060
|
-
df["price"] # ✗ Error: 'price' not in AggregatedSales
|
|
1061
|
-
return df[AggregatedSales.total_revenue.s].mean()
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
### Polars Performance Pipeline
|
|
1065
|
-
|
|
1066
|
-
```python
|
|
1067
|
-
from typing import Annotated
|
|
1068
|
-
import polars as pl
|
|
1069
|
-
from typedframes import BaseSchema, Column
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
class LargeDataset(BaseSchema):
|
|
1073
|
-
id = Column(type=int)
|
|
1074
|
-
value = Column(type=float)
|
|
1075
|
-
category = Column(type=str)
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
def efficient_aggregation(df: Annotated[pl.DataFrame, LargeDataset]) -> pl.DataFrame:
|
|
1079
|
-
return df.filter(pl.col("value") > 100).group_by("category").agg(pl.col("value").mean())
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
# Polars handles large files efficiently
|
|
1083
|
-
df: Annotated[pl.DataFrame, LargeDataset] = pl.read_csv("huge_file.csv")
|
|
1084
|
-
result = efficient_aggregation(df)
|
|
1085
|
-
```
|
|
901
|
+
Runnable versions of everything shown in [Quick Start](#quick-start) and
|
|
902
|
+
[Advanced Usage](#advanced-usage) live under [`examples/features/`](examples/features/):
|
|
903
|
+
|
|
904
|
+
- [`annotated_pandas_example.py`](examples/features/annotated_pandas_example.py) —
|
|
905
|
+
`Annotated[pd.DataFrame, Schema]` with string and `.s` descriptor column access
|
|
906
|
+
- [`annotated_polars_example.py`](examples/features/annotated_polars_example.py) —
|
|
907
|
+
the same, with `pl.col()` and `.col` descriptor expressions
|
|
908
|
+
- [`typedframes_example.py`](examples/features/typedframes_example.py) — pandas and
|
|
909
|
+
polars side by side against one shared schema
|
|
910
|
+
- [`schema_algebra_example.py`](examples/features/schema_algebra_example.py) —
|
|
911
|
+
composing schemas via inheritance and the `+` operator
|
|
912
|
+
- [`inference_example.py`](examples/features/inference_example.py) — all four
|
|
913
|
+
column-inference modes with annotated ✓/✗ comments, no `BaseSchema` at all
|
|
914
|
+
- [`multi_file_inference/`](examples/features/multi_file_inference/) and
|
|
915
|
+
[`multi_file_with_schema/`](examples/features/multi_file_with_schema/) — the same
|
|
916
|
+
cross-file pipeline checked with and without schemas, see
|
|
917
|
+
[`examples/features/README.md`](examples/features/README.md) for what each one catches
|
|
1086
918
|
|
|
1087
919
|
---
|
|
1088
920
|
|
|
@@ -1170,35 +1002,6 @@ MIT License - see [LICENSE](LICENSE)
|
|
|
1170
1002
|
|
|
1171
1003
|
---
|
|
1172
1004
|
|
|
1173
|
-
## Roadmap
|
|
1174
|
-
|
|
1175
|
-
**Shipped:**
|
|
1176
|
-
- [x] Schema definition API
|
|
1177
|
-
- [x] Pandas support
|
|
1178
|
-
- [x] Polars support
|
|
1179
|
-
- [x] Mypy plugin
|
|
1180
|
-
- [x] Standalone checker (Rust)
|
|
1181
|
-
- [x] Explicit backend types
|
|
1182
|
-
- [x] Merge/join schema preservation
|
|
1183
|
-
- [x] Schema Composition (multiple inheritance, `SchemaA + SchemaB`)
|
|
1184
|
-
- [x] Column name collision warnings
|
|
1185
|
-
- [x] Pandera integration (`to_pandera_schema()`)
|
|
1186
|
-
- [x] Cross-file schema inference (project-level index, `--no-index` flag)
|
|
1187
|
-
- [x] Aggressive column inference (untracked-dataframe/dropped-unknown-column warnings, method chain propagation)
|
|
1188
|
-
- [x] Function parameter contracts (`missing-column`), resolved transitively across chains of helper
|
|
1189
|
-
functions and cross-file calls; schema-annotated parameters take priority over body-scanning
|
|
1190
|
-
- [x] SQL / data-warehouse column inference (`SELECT` list parsing across Snowflake, BigQuery, Athena, Redshift,
|
|
1191
|
-
Databricks, PySpark, DuckDB, SQLAlchemy Core/ORM, Feast, and T-SQL/Synapse/Fabric dialects)
|
|
1192
|
-
- [x] Jupyter notebook (`.ipynb`) checking — code cells are checked directly, with errors reported as
|
|
1193
|
-
`notebook.ipynb:cell N:line:col`
|
|
1194
|
-
|
|
1195
|
-
**Planned:**
|
|
1196
|
-
|
|
1197
|
-
- [ ] **Opt-in data loading constraints** - `Field` class with constraints (`gt`, `ge`, `lt`, `le`), strictly isolated
|
|
1198
|
-
to `from_schema()` ingestion boundaries
|
|
1199
|
-
|
|
1200
|
-
---
|
|
1201
|
-
|
|
1202
1005
|
## FAQ
|
|
1203
1006
|
|
|
1204
1007
|
**Q: Do I need to choose between pandas and polars?**
|