polspec 0.1.5__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 (68) hide show
  1. polspec-0.1.5/.github/workflows/docs.yml +63 -0
  2. polspec-0.1.5/.github/workflows/release.yml +126 -0
  3. polspec-0.1.5/.github/workflows/test.yml +126 -0
  4. polspec-0.1.5/.gitignore +167 -0
  5. polspec-0.1.5/.pre-commit-config.yaml +27 -0
  6. polspec-0.1.5/.python-version +1 -0
  7. polspec-0.1.5/CHANGELOG.md +113 -0
  8. polspec-0.1.5/CONTRIBUTING.md +85 -0
  9. polspec-0.1.5/Cargo.lock +3853 -0
  10. polspec-0.1.5/Cargo.toml +43 -0
  11. polspec-0.1.5/LICENSE +21 -0
  12. polspec-0.1.5/PKG-INFO +195 -0
  13. polspec-0.1.5/README.md +165 -0
  14. polspec-0.1.5/benchmarks/bench_generate.py +192 -0
  15. polspec-0.1.5/docs/comparison.md +179 -0
  16. polspec-0.1.5/docs/getting-started.md +118 -0
  17. polspec-0.1.5/docs/guide/categories.md +169 -0
  18. polspec-0.1.5/docs/guide/cli.md +176 -0
  19. polspec-0.1.5/docs/guide/columns.md +256 -0
  20. polspec-0.1.5/docs/guide/constraints.md +185 -0
  21. polspec-0.1.5/docs/guide/documenting.md +93 -0
  22. polspec-0.1.5/docs/guide/generating.md +100 -0
  23. polspec-0.1.5/docs/guide/testing.md +203 -0
  24. polspec-0.1.5/docs/guide/validating.md +124 -0
  25. polspec-0.1.5/docs/guide/yaml.md +150 -0
  26. polspec-0.1.5/docs/index.md +102 -0
  27. polspec-0.1.5/docs/reference/architecture.md +108 -0
  28. polspec-0.1.5/docs/reference/limitations.md +128 -0
  29. polspec-0.1.5/docs/reference/roadmap.md +180 -0
  30. polspec-0.1.5/examples/categories.yaml +8 -0
  31. polspec-0.1.5/examples/products.yaml +43 -0
  32. polspec-0.1.5/examples/related_specs.py +212 -0
  33. polspec-0.1.5/pyproject.toml +105 -0
  34. polspec-0.1.5/python/polspec/__init__.py +24 -0
  35. polspec-0.1.5/python/polspec/__main__.py +6 -0
  36. polspec-0.1.5/python/polspec/bound.py +47 -0
  37. polspec-0.1.5/python/polspec/catspec.py +788 -0
  38. polspec-0.1.5/python/polspec/check.py +60 -0
  39. polspec-0.1.5/python/polspec/cli.py +567 -0
  40. polspec-0.1.5/python/polspec/constants.py +33 -0
  41. polspec-0.1.5/python/polspec/distributions.py +101 -0
  42. polspec-0.1.5/python/polspec/dtypes.py +134 -0
  43. polspec-0.1.5/python/polspec/engine.py +408 -0
  44. polspec-0.1.5/python/polspec/foreign_key.py +176 -0
  45. polspec-0.1.5/python/polspec/framespec.py +1507 -0
  46. polspec-0.1.5/python/polspec/profiler.py +196 -0
  47. polspec-0.1.5/python/polspec/py.typed +0 -0
  48. polspec-0.1.5/python/polspec/report.py +414 -0
  49. polspec-0.1.5/python/polspec/rules.py +241 -0
  50. polspec-0.1.5/python/polspec/serialization.py +388 -0
  51. polspec-0.1.5/python/polspec/spec.py +375 -0
  52. polspec-0.1.5/python/polspec/validation.py +707 -0
  53. polspec-0.1.5/src/lib.rs +717 -0
  54. polspec-0.1.5/tests/test_catspec.py +648 -0
  55. polspec-0.1.5/tests/test_cli.py +348 -0
  56. polspec-0.1.5/tests/test_declaration.py +377 -0
  57. polspec-0.1.5/tests/test_foreign_key.py +390 -0
  58. polspec-0.1.5/tests/test_framespec.py +314 -0
  59. polspec-0.1.5/tests/test_generation.py +479 -0
  60. polspec-0.1.5/tests/test_profiler.py +234 -0
  61. polspec-0.1.5/tests/test_report.py +131 -0
  62. polspec-0.1.5/tests/test_roundtrip.py +647 -0
  63. polspec-0.1.5/tests/test_rules.py +232 -0
  64. polspec-0.1.5/tests/test_serialization.py +399 -0
  65. polspec-0.1.5/tests/test_streaming.py +199 -0
  66. polspec-0.1.5/tests/test_validation.py +923 -0
  67. polspec-0.1.5/uv.lock +531 -0
  68. polspec-0.1.5/zensical.toml +102 -0
@@ -0,0 +1,63 @@
1
+ name: Documentation
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "docs/**"
9
+ - "zensical.toml"
10
+ - "README.md"
11
+ - ".github/workflows/docs.yml"
12
+ pull_request:
13
+ paths:
14
+ - "docs/**"
15
+ - "zensical.toml"
16
+ - "README.md"
17
+ - ".github/workflows/docs.yml"
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ jobs:
23
+ build:
24
+ # Runs on pull requests too, so a broken link fails before merge rather
25
+ # than after. Only the push to main deploys.
26
+ name: Build (strict)
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v7
30
+
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@v7
33
+ with:
34
+ python-version: "3.13"
35
+
36
+ # zensical is pinned by uv.lock through the docs group.
37
+ - name: Install docs tooling
38
+ run: uv sync --only-group docs --no-install-project
39
+
40
+ - name: Build site
41
+ run: uv run --no-sync zensical build --clean --strict
42
+
43
+ - name: Upload site
44
+ if: github.event_name == 'push'
45
+ uses: actions/upload-pages-artifact@v5
46
+ with:
47
+ path: site
48
+
49
+ deploy:
50
+ name: Deploy to GitHub Pages
51
+ if: github.event_name == 'push'
52
+ needs: build
53
+ runs-on: ubuntu-latest
54
+ permissions:
55
+ pages: write
56
+ id-token: write
57
+ environment:
58
+ name: github-pages
59
+ url: ${{ steps.deployment.outputs.page_url }}
60
+ steps:
61
+ - uses: actions/configure-pages@v6
62
+ - uses: actions/deploy-pages@v5
63
+ id: deployment
@@ -0,0 +1,126 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ check-version:
10
+ name: Tag matches pyproject.toml version
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v7
14
+ - name: Compare tag to pyproject.toml
15
+ run: |
16
+ tag="${GITHUB_REF_NAME#v}"
17
+ declared="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
18
+ if [ "$tag" != "$declared" ]; then
19
+ echo "Tag v$tag does not match pyproject.toml version $declared" >&2
20
+ exit 1
21
+ fi
22
+
23
+ # A tag publishes nothing unless the full test workflow is green.
24
+ test:
25
+ name: Test
26
+ needs: check-version
27
+ uses: ./.github/workflows/test.yml
28
+
29
+ build:
30
+ name: Build wheel (${{ matrix.os }}, ${{ matrix.target }})
31
+ needs: [check-version, test]
32
+ runs-on: ${{ matrix.os }}
33
+ strategy:
34
+ fail-fast: false
35
+ matrix:
36
+ include:
37
+ - os: ubuntu-latest
38
+ target: x86_64
39
+ - os: ubuntu-24.04-arm
40
+ target: aarch64
41
+ - os: macos-latest
42
+ target: aarch64
43
+ - os: macos-latest
44
+ target: x86_64
45
+ - os: windows-latest
46
+ target: x86_64
47
+
48
+ steps:
49
+ - uses: actions/checkout@v7
50
+
51
+ # One abi3 wheel per platform covers every supported CPython version.
52
+ - name: Build wheel
53
+ uses: PyO3/maturin-action@v1
54
+ with:
55
+ command: build
56
+ target: ${{ matrix.target }}
57
+ manylinux: auto
58
+ args: --release --out dist
59
+ sccache: "true"
60
+
61
+ - name: Upload wheel artifact
62
+ uses: actions/upload-artifact@v7
63
+ with:
64
+ name: wheels-${{ matrix.os }}-${{ matrix.target }}
65
+ path: dist/*.whl
66
+
67
+ sdist:
68
+ name: Build sdist
69
+ needs: [check-version, test]
70
+ runs-on: ubuntu-latest
71
+ steps:
72
+ - uses: actions/checkout@v7
73
+
74
+ - name: Build sdist
75
+ uses: PyO3/maturin-action@v1
76
+ with:
77
+ command: sdist
78
+ args: --out dist
79
+
80
+ - name: Upload sdist artifact
81
+ uses: actions/upload-artifact@v7
82
+ with:
83
+ name: wheels-sdist
84
+ path: dist/*.tar.gz
85
+
86
+ publish-pypi:
87
+ name: Publish to PyPI
88
+ needs: [build, sdist]
89
+ runs-on: ubuntu-latest
90
+ environment: pypi
91
+ permissions:
92
+ id-token: write
93
+ steps:
94
+ - name: Download all artifacts
95
+ uses: actions/download-artifact@v8
96
+ with:
97
+ pattern: wheels-*
98
+ path: dist
99
+ merge-multiple: true
100
+
101
+ - name: Publish to PyPI
102
+ uses: pypa/gh-action-pypi-publish@release/v1
103
+ with:
104
+ packages-dir: dist
105
+ skip-existing: true
106
+
107
+ release:
108
+ name: Attach artifacts to GitHub Release
109
+ needs: [build, sdist]
110
+ runs-on: ubuntu-latest
111
+ permissions:
112
+ contents: write
113
+ steps:
114
+ - name: Download all artifacts
115
+ uses: actions/download-artifact@v8
116
+ with:
117
+ pattern: wheels-*
118
+ path: dist
119
+ merge-multiple: true
120
+
121
+ - name: Upload to GitHub Release
122
+ uses: softprops/action-gh-release@v3
123
+ with:
124
+ files: |
125
+ dist/*.whl
126
+ dist/*.tar.gz
@@ -0,0 +1,126 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_call:
8
+
9
+ jobs:
10
+ lint:
11
+ name: Lint (ruff)
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v7
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v7
18
+ with:
19
+ python-version: "3.13"
20
+
21
+ # The project itself is not installed: linting needs no Rust build.
22
+ - name: Sync dev dependencies
23
+ run: uv sync --no-install-project
24
+
25
+ - name: ruff check
26
+ run: uv run --no-sync ruff check .
27
+
28
+ - name: ruff format
29
+ run: uv run --no-sync ruff format --check .
30
+
31
+ rust:
32
+ name: Rust (fmt, clippy, test)
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v7
36
+
37
+ - name: Install Rust
38
+ uses: dtolnay/rust-toolchain@stable
39
+ with:
40
+ components: rustfmt, clippy
41
+
42
+ - name: Cache Rust build
43
+ uses: Swatinem/rust-cache@v2
44
+
45
+ - name: Install Python (the pyo3 build script needs an interpreter)
46
+ uses: actions/setup-python@v7
47
+ with:
48
+ python-version: "3.13"
49
+
50
+ - name: cargo fmt
51
+ run: cargo fmt --check
52
+
53
+ - name: cargo clippy
54
+ run: cargo clippy --release -- -D warnings
55
+
56
+ - name: cargo test
57
+ run: cargo test --release
58
+
59
+ test:
60
+ name: Test (${{ matrix.os }}, py${{ matrix.python-version }})
61
+ runs-on: ${{ matrix.os }}
62
+ strategy:
63
+ fail-fast: false
64
+ matrix:
65
+ os: [ubuntu-latest, windows-latest, macos-latest]
66
+ python-version: ["3.12", "3.13", "3.14"]
67
+
68
+ steps:
69
+ - uses: actions/checkout@v7
70
+
71
+ - name: Install Rust
72
+ uses: dtolnay/rust-toolchain@stable
73
+
74
+ - name: Cache Rust build
75
+ uses: Swatinem/rust-cache@v2
76
+
77
+ - name: Install uv
78
+ uses: astral-sh/setup-uv@v7
79
+ with:
80
+ python-version: ${{ matrix.python-version }}
81
+
82
+ - name: Sync dependencies
83
+ run: uv sync --all-extras
84
+
85
+ - name: Build extension (maturin develop)
86
+ run: uv run maturin develop --release
87
+
88
+ - name: Run tests
89
+ run: uv run pytest
90
+
91
+ - name: Run the worked example as a smoke test
92
+ run: uv run python examples/related_specs.py
93
+
94
+ polars-latest:
95
+ # uv.lock pins one Polars release; this job catches a break in the newest
96
+ # release that still satisfies the bound declared in pyproject.toml.
97
+ name: Test (ubuntu, py3.13, newest polars)
98
+ runs-on: ubuntu-latest
99
+ steps:
100
+ - uses: actions/checkout@v7
101
+
102
+ - name: Install Rust
103
+ uses: dtolnay/rust-toolchain@stable
104
+
105
+ - name: Cache Rust build
106
+ uses: Swatinem/rust-cache@v2
107
+
108
+ - name: Install uv
109
+ uses: astral-sh/setup-uv@v7
110
+ with:
111
+ python-version: "3.13"
112
+
113
+ - name: Upgrade polars within the declared bound
114
+ run: uv lock --upgrade-package polars
115
+
116
+ - name: Sync dependencies
117
+ run: uv sync --all-extras
118
+
119
+ - name: Report polars version
120
+ run: uv run python -c "import polars; print(polars.__version__)"
121
+
122
+ - name: Build extension (maturin develop)
123
+ run: uv run maturin develop --release
124
+
125
+ - name: Run tests
126
+ run: uv run pytest
@@ -0,0 +1,167 @@
1
+ ### Python template
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # poetry
99
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103
+ #poetry.lock
104
+
105
+ # pdm
106
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
107
+ #pdm.lock
108
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
109
+ # in version control.
110
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
111
+ .pdm.toml
112
+ .pdm-python
113
+ .pdm-build/
114
+
115
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
116
+ __pypackages__/
117
+
118
+ # Celery stuff
119
+ celerybeat-schedule
120
+ celerybeat.pid
121
+
122
+ # SageMath parsed files
123
+ *.sage.py
124
+
125
+ # Environments
126
+ .env
127
+ .venv
128
+ env/
129
+ venv/
130
+ ENV/
131
+ env.bak/
132
+ venv.bak/
133
+
134
+ # Spyder project settings
135
+ .spyderproject
136
+ .spyproject
137
+
138
+ # Rope project settings
139
+ .ropeproject
140
+
141
+ # mkdocs documentation
142
+ /site
143
+
144
+ # mypy
145
+ .mypy_cache/
146
+ .dmypy.json
147
+ dmypy.json
148
+
149
+ # Pyre type checker
150
+ .pyre/
151
+
152
+ # pytype static type analyzer
153
+ .pytype/
154
+
155
+ # Cython debug symbols
156
+ cython_debug/
157
+
158
+ # PyCharm
159
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
160
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
161
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
162
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
163
+ .idea/
164
+ *.pdb
165
+ *.pyd
166
+
167
+ *sandbox*
@@ -0,0 +1,27 @@
1
+ # Optional local hooks. Enable with:
2
+ # uv run --with pre-commit pre-commit install
3
+ # CI runs the same ruff and cargo checks whether or not these are installed.
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: v5.0.0
7
+ hooks:
8
+ - id: end-of-file-fixer
9
+ - id: trailing-whitespace
10
+ - id: check-yaml
11
+ - id: check-toml
12
+
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.16.5
15
+ hooks:
16
+ - id: ruff-check
17
+ args: [--fix]
18
+ - id: ruff-format
19
+
20
+ - repo: local
21
+ hooks:
22
+ - id: cargo-fmt
23
+ name: cargo fmt
24
+ entry: cargo fmt --
25
+ language: system
26
+ types: [rust]
27
+ pass_filenames: false
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,113 @@
1
+ # Changelog
2
+
3
+ All notable changes to polspec are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Until 1.0, minor
5
+ versions may break the Python API, the YAML format, and the values a given
6
+ seed produces; see
7
+ [Roadmap and stability](https://maxwellb13.github.io/polspec/reference/roadmap/).
8
+
9
+ ## [Unreleased]
10
+
11
+ ## [0.1.5] - 2026-09-03
12
+
13
+ ### Added
14
+
15
+ - `python/polspec/py.typed`, so type checkers use the package's annotations.
16
+ - `CONTRIBUTING.md`, this changelog, and a `.python-version` file.
17
+ - `examples/related_specs.py`: a worked example of four related specs
18
+ (foreign keys, shared categories, rules, checks, a YAML-declared spec). It
19
+ runs in CI as a smoke test.
20
+ - A release-workflow job that refuses a `vX.Y.Z` tag whose version does not
21
+ match `pyproject.toml`.
22
+ - CI now runs `ruff check` with a wider rule set, `ruff format --check`, `cargo fmt --check`,
23
+ `cargo clippy -D warnings` and `cargo test`, tests on macOS as well as
24
+ Linux and Windows, and tests against the newest Polars release inside the
25
+ declared bound. The docs build runs strictly on pull requests.
26
+ - Release builds now produce wheels for Linux aarch64 and macOS (x86_64 and
27
+ arm64) alongside Linux and Windows x86_64, plus an sdist, and only publish
28
+ when the test workflow is green.
29
+ - An optional `.pre-commit-config.yaml` with ruff and cargo fmt hooks.
30
+ - `tests/test_colspec.py` (2,000 lines, unsectioned) is split into
31
+ `test_generation.py`, `test_rules.py`, `test_serialization.py`,
32
+ `test_profiler.py`, `test_framespec.py`, `test_report.py` and
33
+ `test_foreign_key.py`, each with a docstring saying what it covers.
34
+
35
+ ### Changed
36
+
37
+ - The crate version in `Cargo.toml` is a placeholder; `pyproject.toml` is the
38
+ only place the version is set, so `uv version --bump` works.
39
+ - The `parquet`, `ipc` and `all` extras (all identical) are replaced by a
40
+ single `arrow` extra. Install with `polspec[arrow]` for the Parquet and
41
+ Arrow IPC sinks.
42
+ - `polars` is bounded to `<2`; the Rust extension is coupled to a Polars
43
+ release line.
44
+ - The abi3 floor is now Python 3.12, matching `requires-python`.
45
+ - `cargo test` links again (`extension-module` is no longer an unconditional
46
+ crate feature; maturin enables it).
47
+
48
+ ### Fixed
49
+
50
+ - Repository URL in package metadata pointed at the repository's old name.
51
+ - README claimed the license was unspecified; it is MIT.
52
+ - Documentation: `ColSpec(col_name=...)` is now described in *Declaring
53
+ columns*, `FrameSpec.to_python()` in *YAML specs*, the getting-started
54
+ example imports `date`, and the architecture page lists the `cli` module
55
+ and its tests.
56
+
57
+ ## [0.1.4] - 2026-09-02
58
+
59
+ ### Added
60
+
61
+ - `polspec schema infer --output spec.py` and `FrameSpec.to_python()`, which
62
+ write a spec as an editable Python module rather than YAML.
63
+
64
+ ## [0.1.3] - 2026-09-01
65
+
66
+ ### Added
67
+
68
+ - `ColSpec(col_name=...)`, so a column's name in data may differ from the
69
+ attribute name used to declare it.
70
+
71
+ ### Changed
72
+
73
+ - Roadmap expanded with detailed plans for a spec registry, structured
74
+ validation results, and generation guardrails.
75
+
76
+ ## [0.1.2] - 2026-08-31
77
+
78
+ Version bump only; no user-facing change.
79
+
80
+ ## [0.1.1] - 2026-08-31
81
+
82
+ ### Added
83
+
84
+ - Test workflow on GitHub Actions (Linux and Windows, Python 3.12 to 3.14).
85
+
86
+ ### Fixed
87
+
88
+ - `ColSpec.dtype` accepts a dtype class as well as an instance.
89
+
90
+ ## [0.1.0] - 2026-08-31
91
+
92
+ First tagged release.
93
+
94
+ - `ColSpec` and `FrameSpec`: declare a Polars schema with nullability,
95
+ bounds, string lengths, choices and weights, distributions, tags, and
96
+ conditional `ColRule`s.
97
+ - `generate()` backed by a parallel Rust extension, `method="cartesian"` for
98
+ coverage sets, batched generation and Parquet/CSV/IPC/NDJSON sinks.
99
+ - `validate()` collecting every violation in one Polars aggregation, with
100
+ column validators, multi-column `Check`s, composite uniqueness and
101
+ `ForeignKey`s.
102
+ - `CatSpec` registries for shared `Enum`/`Categorical` domains.
103
+ - YAML round-trip, `from_dataframe()` profiling, Markdown and Mermaid output.
104
+ - CLI: `polspec schema infer`, `polspec schema new`, `polspec test`.
105
+ - Documentation site, comparison guide, and release automation.
106
+
107
+ [Unreleased]: https://github.com/MaxwellB13/polspec/compare/v0.1.5...HEAD
108
+ [0.1.5]: https://github.com/MaxwellB13/polspec/compare/v0.1.4...v0.1.5
109
+ [0.1.4]: https://github.com/MaxwellB13/polspec/compare/v0.1.3...v0.1.4
110
+ [0.1.3]: https://github.com/MaxwellB13/polspec/compare/v0.1.2...v0.1.3
111
+ [0.1.2]: https://github.com/MaxwellB13/polspec/compare/v0.1.1...v0.1.2
112
+ [0.1.1]: https://github.com/MaxwellB13/polspec/compare/v0.1.0...v0.1.1
113
+ [0.1.0]: https://github.com/MaxwellB13/polspec/releases/tag/v0.1.0
@@ -0,0 +1,85 @@
1
+ # Contributing
2
+
3
+ polspec is a Python package over a Rust extension. The Python side owns the
4
+ vocabulary (what a column can declare and what it means); the Rust side owns
5
+ the inner loop that fills arrays with values. See
6
+ [Architecture](https://maxwellb13.github.io/polspec/reference/architecture/)
7
+ for the module map.
8
+
9
+ ## Set up
10
+
11
+ You need Python 3.12+, [uv](https://docs.astral.sh/uv/), and a Rust toolchain
12
+ (`rustup`).
13
+
14
+ ```bash
15
+ git clone https://github.com/MaxwellB13/polspec.git
16
+ cd polspec
17
+ uv sync --group dev # Python deps, including maturin and pyarrow
18
+ uv run maturin develop --release
19
+ ```
20
+
21
+ `maturin develop` compiles the extension and installs the package into the
22
+ project's virtual environment as editable. Re-run it whenever `src/` changes;
23
+ Python-only edits are picked up immediately.
24
+
25
+ ## Check your change
26
+
27
+ Run everything CI runs before opening a pull request:
28
+
29
+ ```bash
30
+ uv run pytest # Python test suite
31
+ uv run ruff check . && uv run ruff format --check .
32
+ cargo test --release # Rust unit tests
33
+ cargo clippy --release
34
+ uv run python examples/related_specs.py # worked example, doubles as a smoke test
35
+ ```
36
+
37
+ Optionally, install the pre-commit hooks so ruff and `cargo fmt` run on
38
+ every commit:
39
+
40
+ ```bash
41
+ uv run --with pre-commit pre-commit install
42
+ ```
43
+
44
+ The docs build with [zensical](https://pypi.org/project/zensical/):
45
+
46
+ ```bash
47
+ uv run --group docs zensical serve # live preview
48
+ uv run --group docs zensical build --strict # what the docs workflow runs
49
+ ```
50
+
51
+ ## Conventions
52
+
53
+ - **Generate and validate must agree.** Anything `generate()` produces,
54
+ `validate()` must accept. `tests/test_roundtrip.py` pins that property. A
55
+ known gap is recorded once in
56
+ [`docs/reference/limitations.md`](docs/reference/limitations.md) and once as
57
+ an `xfail(strict=True)` test, so fixing it forces the docs to be updated.
58
+ - **Error messages name the fix.** Say what was declared, what was expected,
59
+ and what to change. Look at the existing `ValueError`s in
60
+ `python/polspec/spec.py` for the register.
61
+ - **Two tables must match.** The distribution parameter aliases live in both
62
+ `python/polspec/distributions.py` and `DistKind::from_spec` in
63
+ `src/lib.rs`. Change them together.
64
+ - **Docs are part of the change.** A new field, option or CLI flag lands with
65
+ its guide page and a `CHANGELOG.md` entry under *Unreleased*.
66
+ - Commit messages follow the existing `type: summary` style
67
+ (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `ci:`, `chore:`).
68
+
69
+ ## Releasing
70
+
71
+ The version lives in exactly one place: `project.version` in
72
+ `pyproject.toml`. maturin prefers it over the crate version in `Cargo.toml`,
73
+ which is a placeholder.
74
+
75
+ 1. Bump it and refresh the lock file:
76
+
77
+ ```bash
78
+ uv version --bump patch # or minor / major
79
+ uv lock
80
+ ```
81
+
82
+ Then move the *Unreleased* section of `CHANGELOG.md` under the new version.
83
+ 2. Commit, then tag `vX.Y.Z` and push the tag.
84
+ 3. The release workflow checks the tag matches `pyproject.toml`, builds wheels,
85
+ publishes to PyPI, and attaches the wheels to a GitHub release.