sqlrules 0.4.0__tar.gz → 1.0.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.
- {sqlrules-0.4.0 → sqlrules-1.0.0}/.gitignore +1 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/CHANGELOG.md +48 -0
- sqlrules-1.0.0/CODE_OF_CONDUCT.md +70 -0
- sqlrules-1.0.0/CONTRIBUTING.md +74 -0
- sqlrules-1.0.0/PKG-INFO +358 -0
- sqlrules-1.0.0/README.md +298 -0
- sqlrules-1.0.0/RELEASING.md +72 -0
- {sqlrules-0.4.0/docs → sqlrules-1.0.0}/ROADMAP.md +15 -4
- sqlrules-1.0.0/SECURITY.md +34 -0
- sqlrules-1.0.0/docs/API.md +209 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/ARCHITECTURE.md +8 -6
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/CONSTRAINTS.md +7 -2
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/DESIGN_DECISIONS.md +17 -17
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/DIALECT_SUPPORT.md +16 -16
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/ERRORS.md +22 -20
- sqlrules-1.0.0/docs/INTERNAL_API.md +91 -0
- sqlrules-1.0.0/docs/IR_CONTRACT.md +61 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/MILESTONES.md +34 -45
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/PERFORMANCE.md +28 -24
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/PLUGIN_SYSTEM.md +37 -18
- sqlrules-1.0.0/docs/README.md +19 -0
- sqlrules-1.0.0/docs/SECURITY.md +43 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/SPEC.md +18 -4
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/TESTING.md +24 -4
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/TRANSLATORS.md +15 -15
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/TYPE_SUPPORT.md +59 -28
- sqlrules-1.0.0/docs/_static/custom.css +225 -0
- sqlrules-1.0.0/docs/_static/favicon.svg +4 -0
- sqlrules-1.0.0/docs/_static/logo-dark.svg +5 -0
- sqlrules-1.0.0/docs/_static/logo.svg +5 -0
- sqlrules-1.0.0/docs/_templates/.gitkeep +0 -0
- sqlrules-1.0.0/docs/conf.py +190 -0
- sqlrules-1.0.0/docs/guides/design-philosophy.md +53 -0
- sqlrules-1.0.0/docs/guides/faq.md +75 -0
- sqlrules-1.0.0/docs/guides/getting-started.md +149 -0
- sqlrules-1.0.0/docs/guides/markers.md +66 -0
- sqlrules-1.0.0/docs/guides/orm-column-map.md +78 -0
- sqlrules-1.0.0/docs/guides/start-here.md +50 -0
- sqlrules-1.0.0/docs/guides/troubleshooting.md +74 -0
- sqlrules-1.0.0/docs/index.md +163 -0
- sqlrules-1.0.0/docs/internals/index.md +20 -0
- sqlrules-1.0.0/docs/project/changelog.md +2 -0
- sqlrules-1.0.0/docs/project/contributing.md +2 -0
- sqlrules-1.0.0/docs/project/roadmap.md +2 -0
- sqlrules-1.0.0/docs/project/support.md +55 -0
- sqlrules-1.0.0/docs/reference/application.md +16 -0
- sqlrules-1.0.0/docs/reference/glossary.md +22 -0
- sqlrules-1.0.0/docs/reference/index.md +46 -0
- sqlrules-1.0.0/docs/reference/plugin-api.md +17 -0
- sqlrules-1.0.0/docs/requirements.txt +7 -0
- sqlrules-1.0.0/examples/README.md +37 -0
- sqlrules-1.0.0/examples/basic_compile.py +38 -0
- sqlrules-1.0.0/examples/postgresql_pattern.py +53 -0
- sqlrules-1.0.0/examples/select_usage.py +38 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/pyproject.toml +30 -7
- sqlrules-1.0.0/scripts/check_versions.py +74 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/__init__.py +11 -3
- sqlrules-1.0.0/src/sqlrules/cache.py +51 -0
- sqlrules-1.0.0/src/sqlrules/compiler.py +361 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/constraints.py +153 -6
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/ir.py +9 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/plugins.py +25 -0
- sqlrules-1.0.0/src/sqlrules/py.typed +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/translators.py +90 -42
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_correctness.py +82 -11
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_coverage_edges.py +43 -10
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_errors.py +47 -1
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_literal_enum.py +18 -0
- sqlrules-1.0.0/tests/test_numeric_constraints.py +37 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_plugins.py +24 -0
- sqlrules-1.0.0/tests/test_string_constraints.py +31 -0
- sqlrules-1.0.0/tests/test_type_checks.py +297 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_v01_features.py +8 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_v02_features.py +2 -8
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_v04_markers.py +20 -9
- sqlrules-0.4.0/CONTRIBUTING.md +0 -86
- sqlrules-0.4.0/PKG-INFO +0 -171
- sqlrules-0.4.0/README.md +0 -131
- sqlrules-0.4.0/docs/API.md +0 -142
- sqlrules-0.4.0/docs/CONTRIBUTING.md +0 -10
- sqlrules-0.4.0/docs/INTERNAL_API.md +0 -251
- sqlrules-0.4.0/docs/index.md +0 -43
- sqlrules-0.4.0/packages/sqlrules-mssql/README.md +0 -37
- sqlrules-0.4.0/packages/sqlrules-mssql/pyproject.toml +0 -28
- sqlrules-0.4.0/packages/sqlrules-mssql/src/sqlrules_mssql/__init__.py +0 -52
- sqlrules-0.4.0/packages/sqlrules-mssql/src/sqlrules_mssql/json.py +0 -70
- sqlrules-0.4.0/packages/sqlrules-mssql/src/sqlrules_mssql/length.py +0 -26
- sqlrules-0.4.0/packages/sqlrules-mssql/tests/test_mssql_plugin.py +0 -52
- sqlrules-0.4.0/packages/sqlrules-mysql/README.md +0 -36
- sqlrules-0.4.0/packages/sqlrules-mysql/pyproject.toml +0 -28
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/__init__.py +0 -39
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/fulltext.py +0 -20
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/json.py +0 -35
- sqlrules-0.4.0/packages/sqlrules-mysql/src/sqlrules_mysql/pattern.py +0 -22
- sqlrules-0.4.0/packages/sqlrules-mysql/tests/test_mysql_plugin.py +0 -45
- sqlrules-0.4.0/packages/sqlrules-postgresql/README.md +0 -53
- sqlrules-0.4.0/packages/sqlrules-postgresql/pyproject.toml +0 -28
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/__init__.py +0 -46
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/array.py +0 -25
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/jsonb.py +0 -25
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/pattern.py +0 -19
- sqlrules-0.4.0/packages/sqlrules-postgresql/src/sqlrules_postgresql/range.py +0 -25
- sqlrules-0.4.0/packages/sqlrules-postgresql/tests/test_postgres_plugin.py +0 -88
- sqlrules-0.4.0/packages/sqlrules-sqlite/README.md +0 -53
- sqlrules-0.4.0/packages/sqlrules-sqlite/pyproject.toml +0 -28
- sqlrules-0.4.0/packages/sqlrules-sqlite/src/sqlrules_sqlite/__init__.py +0 -48
- sqlrules-0.4.0/packages/sqlrules-sqlite/src/sqlrules_sqlite/json.py +0 -58
- sqlrules-0.4.0/packages/sqlrules-sqlite/src/sqlrules_sqlite/pattern.py +0 -25
- sqlrules-0.4.0/packages/sqlrules-sqlite/src/sqlrules_sqlite/regexp.py +0 -30
- sqlrules-0.4.0/packages/sqlrules-sqlite/tests/test_sqlite_plugin.py +0 -72
- sqlrules-0.4.0/src/sqlrules/cache.py +0 -40
- sqlrules-0.4.0/src/sqlrules/compiler.py +0 -206
- sqlrules-0.4.0/tests/test_numeric_constraints.py +0 -23
- sqlrules-0.4.0/tests/test_string_constraints.py +0 -17
- {sqlrules-0.4.0 → sqlrules-1.0.0}/LICENSE +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/benchmarks/__init__.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/benchmarks/bench_compile.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/COMPILER.md +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/NON_GOALS.md +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/PHILOSOPHY.md +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/docs/VISION.md +0 -0
- /sqlrules-0.4.0/src/sqlrules/py.typed → /sqlrules-1.0.0/docs/_static/.gitkeep +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/columns.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/conformance.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/errors.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/inspectors.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/src/sqlrules/markers.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/conftest.py +0 -0
- {sqlrules-0.4.0 → sqlrules-1.0.0}/tests/test_helpers.py +0 -0
|
@@ -5,6 +5,53 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0/).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.0.0] - 2026-07-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Stable 1.0** Application + Plugin APIs frozen; classifiers
|
|
15
|
+
`Production/Stable`
|
|
16
|
+
- Opt-in `emit_type_checks` on `Compiler` / `compile()`: extracts `type_check`
|
|
17
|
+
IR (`TypeSpec`) from supported scalar annotations, honoring Field /
|
|
18
|
+
`Strict()` / `model_config` strictness and `Optional` → `allow_none`
|
|
19
|
+
- `TypeSpec` / `type_spec()` helper; dialect plugins translate `type_check`
|
|
20
|
+
(PostgreSQL reference matrix; SQLite / MySQL / MSSQL partial support)
|
|
21
|
+
- Application API `clear_model_cache()` for the process-wide Phase-1 IR cache
|
|
22
|
+
- `docs/IR_CONTRACT.md` — frozen Plugin API v1 IR schema
|
|
23
|
+
- `scripts/check_versions.py` — core/plugin lockstep + extras pin checks
|
|
24
|
+
- All-or-nothing PyPI publish via Trusted Publishing (OIDC)
|
|
25
|
+
- Plugin wheel install + conformance smoke in CI; mypy on dialect packages
|
|
26
|
+
- `examples/` runnable scripts; Makefile (`install` / `test` / `docs` / `dist`)
|
|
27
|
+
- Root `SECURITY.md`, `CODE_OF_CONDUCT.md`, `RELEASING.md`; GitHub issue templates
|
|
28
|
+
- Docs: Application vs Plugin autodoc pages; pattern footgun in getting started;
|
|
29
|
+
how-to guides (ORM/`column_map`, markers); Support & compatibility page
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- Core and dialect packages versioned `1.0.0` in lockstep; pins
|
|
34
|
+
`sqlrules>=1,<2` / extras `sqlrules-*>=1,<2`
|
|
35
|
+
- Phase-1 IR cache keyed by `(model, emit_type_checks)` so type-check IR
|
|
36
|
+
does not collide with default compiles
|
|
37
|
+
- `Strict()` metadata is no longer turned into a fake IR operator
|
|
38
|
+
- Builtin translator registry built once per process; module `compile()`
|
|
39
|
+
reuses shared default `Compiler` instances
|
|
40
|
+
- `max_digits` / `decimal_places` and unknown Field metadata keys rejected at
|
|
41
|
+
extract (no invented operators)
|
|
42
|
+
- Elevated pattern/ReDoS guidance in SECURITY and dialect READMEs
|
|
43
|
+
- Core sdist no longer bundles `/packages`
|
|
44
|
+
- `annotated-types` upper-bounded (`>=0.6,<1`)
|
|
45
|
+
- README mental-model anti-example, install honesty (PyPI vs source), prefer
|
|
46
|
+
`where`; CONTRIBUTING focused on contributors; release steps in RELEASING.md
|
|
47
|
+
|
|
48
|
+
### Docs
|
|
49
|
+
|
|
50
|
+
- Whole-model type matrix documented as intentional 1.0 contract
|
|
51
|
+
- Registry mutation after `Compiler` init documented as unsupported
|
|
52
|
+
- Prefer `register_constraint` in Plugin docs
|
|
53
|
+
- Diátaxis-oriented navigation; one H1 per page for sidebar hygiene
|
|
54
|
+
|
|
8
55
|
## [0.4.0] - 2026-07-10
|
|
9
56
|
|
|
10
57
|
### Added
|
|
@@ -132,6 +179,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
132
179
|
- Docs clarify that `on_unsupported` does not soften unsupported types;
|
|
133
180
|
plugin / two-phase compile designs are marked as future
|
|
134
181
|
|
|
182
|
+
[1.0.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v1.0.0
|
|
135
183
|
[0.4.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v0.4.0
|
|
136
184
|
[0.3.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v0.3.0
|
|
137
185
|
[0.2.0]: https://github.com/eddiethedean/sqlrules/releases/tag/v0.2.0
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
+
identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment:
|
|
18
|
+
|
|
19
|
+
- Demonstrating empathy and kindness toward other people
|
|
20
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
21
|
+
- Giving and gracefully accepting constructive feedback
|
|
22
|
+
- Accepting responsibility and apologizing to those affected by our mistakes
|
|
23
|
+
- Focusing on what is best for the community
|
|
24
|
+
|
|
25
|
+
Examples of unacceptable behavior:
|
|
26
|
+
|
|
27
|
+
- The use of sexualized language or imagery, and sexual attention or advances
|
|
28
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
29
|
+
- Public or private harassment
|
|
30
|
+
- Publishing others' private information without explicit permission
|
|
31
|
+
- Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
+
professional setting
|
|
33
|
+
|
|
34
|
+
## Enforcement Responsibilities
|
|
35
|
+
|
|
36
|
+
Project maintainers are responsible for clarifying and enforcing our standards
|
|
37
|
+
of acceptable behavior and will take appropriate and fair corrective action in
|
|
38
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
39
|
+
or harmful.
|
|
40
|
+
|
|
41
|
+
## Scope
|
|
42
|
+
|
|
43
|
+
This Code of Conduct applies within all community spaces and also applies when
|
|
44
|
+
an individual is officially representing the community in public spaces.
|
|
45
|
+
|
|
46
|
+
## Enforcement
|
|
47
|
+
|
|
48
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
49
|
+
reported by contacting the repository owner privately (do **not** use public
|
|
50
|
+
GitHub Issues for sensitive reports). Maintainers may also be reached through
|
|
51
|
+
a private GitHub security advisory when appropriate.
|
|
52
|
+
|
|
53
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
54
|
+
|
|
55
|
+
### Enforcement guidelines
|
|
56
|
+
|
|
57
|
+
| Impact | Consequence |
|
|
58
|
+
|---|---|
|
|
59
|
+
| Minor / first offense | Private warning; clarification of standards |
|
|
60
|
+
| Repeated or serious | Temporary restriction from community spaces |
|
|
61
|
+
| Severe or patterned abuse | Permanent ban from community spaces |
|
|
62
|
+
|
|
63
|
+
Project maintainers who do not follow or enforce this Code of Conduct in good
|
|
64
|
+
faith may face temporary or permanent repercussions as determined by other
|
|
65
|
+
maintainers.
|
|
66
|
+
|
|
67
|
+
## Attribution
|
|
68
|
+
|
|
69
|
+
This Code of Conduct is adapted from the
|
|
70
|
+
[Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve SQLRules.
|
|
4
|
+
|
|
5
|
+
**Requires** Python 3.10+.
|
|
6
|
+
|
|
7
|
+
## Principles
|
|
8
|
+
|
|
9
|
+
- Keep the Application API minimal.
|
|
10
|
+
- Every feature must map directly to SQLAlchemy expressions.
|
|
11
|
+
- Unsupported Pydantic features should not be approximated.
|
|
12
|
+
- Prefer fail-fast behavior over silent semantic changes.
|
|
13
|
+
- Update relevant docs before or alongside implementation.
|
|
14
|
+
|
|
15
|
+
## Development setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python -m venv .venv
|
|
19
|
+
source .venv/bin/activate
|
|
20
|
+
make install
|
|
21
|
+
pre-commit install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`make install` editable-installs core (`dev` + `docs` extras) and the four
|
|
25
|
+
dialect packages under `packages/`.
|
|
26
|
+
|
|
27
|
+
`pre-commit` runs **Ruff** only (format/lint). It does **not** replace CI —
|
|
28
|
+
still run the checks below before opening a PR.
|
|
29
|
+
|
|
30
|
+
## Day-to-day checks (match CI)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
make check # ruff, mypy, version sync, pytest, sphinx -W
|
|
34
|
+
make dist # build + twine check core and dialect plugins (CI package job)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Equivalents:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
make test # lint + types + version sync + pytest (no docs)
|
|
41
|
+
make docs # sphinx-build -W
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Coverage:** core `sqlrules` is gated at ≥95% (`--cov-fail-under=95`).
|
|
45
|
+
Dialect packages are tested but not included in that fail-under.
|
|
46
|
+
|
|
47
|
+
## Pull requests
|
|
48
|
+
|
|
49
|
+
- Keep changes focused.
|
|
50
|
+
- Add or update tests for every constraint or error path you touch.
|
|
51
|
+
- Update docs (and CHANGELOG under `[Unreleased]` for user-visible changes).
|
|
52
|
+
- Run `make check` and `make dist` so local gates match CI
|
|
53
|
+
(lint / test / docs / package).
|
|
54
|
+
- Issue templates label `bug` / `enhancement` / `question`; starter tasks may
|
|
55
|
+
also use `good first issue` when maintainers apply that label.
|
|
56
|
+
|
|
57
|
+
## Plugins
|
|
58
|
+
|
|
59
|
+
- Declare `name`, `api_version` (`PLUGIN_API_VERSION`), and `register(registry)`.
|
|
60
|
+
- Prefer `register_constraint(..., on_conflict=...)`.
|
|
61
|
+
- Use `pattern_text()` for `pattern` values (`PatternSpec` is part of API v1).
|
|
62
|
+
- Use `type_spec()` / `TypeSpec` when implementing `type_check` translators.
|
|
63
|
+
- Run `sqlrules.conformance.run_basic_conformance(plugin)` for API shape;
|
|
64
|
+
add golden SQL asserts for dialect correctness.
|
|
65
|
+
- Official dialect packages live under `packages/`. They pin
|
|
66
|
+
`sqlrules>=1,<2` and must share the same release tag version as core.
|
|
67
|
+
- Develop from the monorepo with `make install`; see each package README for
|
|
68
|
+
operators, and [PLUGIN_SYSTEM](https://sqlrules.readthedocs.io/en/latest/PLUGIN_SYSTEM.html)
|
|
69
|
+
for the full contract.
|
|
70
|
+
|
|
71
|
+
## Maintainers
|
|
72
|
+
|
|
73
|
+
Release process (CHANGELOG cut, Trusted Publishing, tags, lockstep bumps):
|
|
74
|
+
see [RELEASING.md](https://github.com/eddiethedean/sqlrules/blob/main/RELEASING.md).
|
sqlrules-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sqlrules
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Compile constrained Pydantic models into SQLAlchemy WHERE-rule dictionaries.
|
|
5
|
+
Project-URL: Homepage, https://github.com/eddiethedean/sqlrules
|
|
6
|
+
Project-URL: Documentation, https://sqlrules.readthedocs.io
|
|
7
|
+
Project-URL: Repository, https://github.com/eddiethedean/sqlrules
|
|
8
|
+
Project-URL: Changelog, https://github.com/eddiethedean/sqlrules/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/eddiethedean/sqlrules/issues
|
|
10
|
+
Author: SQLRules Contributors
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: compiler,constraints,filters,pydantic,sqlalchemy,where
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Database
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Requires-Dist: annotated-types<1,>=0.6
|
|
29
|
+
Requires-Dist: pydantic<3,>=2.0
|
|
30
|
+
Requires-Dist: sqlalchemy<3,>=2.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
34
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
38
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
39
|
+
Provides-Extra: dialects
|
|
40
|
+
Requires-Dist: sqlrules-mssql<2,>=1; extra == 'dialects'
|
|
41
|
+
Requires-Dist: sqlrules-mysql<2,>=1; extra == 'dialects'
|
|
42
|
+
Requires-Dist: sqlrules-postgresql<2,>=1; extra == 'dialects'
|
|
43
|
+
Requires-Dist: sqlrules-sqlite<2,>=1; extra == 'dialects'
|
|
44
|
+
Provides-Extra: docs
|
|
45
|
+
Requires-Dist: furo>=2024.8.6; extra == 'docs'
|
|
46
|
+
Requires-Dist: linkify-it-py>=2.0; extra == 'docs'
|
|
47
|
+
Requires-Dist: myst-parser<5,>=4.0; extra == 'docs'
|
|
48
|
+
Requires-Dist: sphinx-copybutton>=0.5.2; extra == 'docs'
|
|
49
|
+
Requires-Dist: sphinx-design<1,>=0.5; extra == 'docs'
|
|
50
|
+
Requires-Dist: sphinx<9,>=8.0; extra == 'docs'
|
|
51
|
+
Provides-Extra: mssql
|
|
52
|
+
Requires-Dist: sqlrules-mssql<2,>=1; extra == 'mssql'
|
|
53
|
+
Provides-Extra: mysql
|
|
54
|
+
Requires-Dist: sqlrules-mysql<2,>=1; extra == 'mysql'
|
|
55
|
+
Provides-Extra: postgresql
|
|
56
|
+
Requires-Dist: sqlrules-postgresql<2,>=1; extra == 'postgresql'
|
|
57
|
+
Provides-Extra: sqlite
|
|
58
|
+
Requires-Dist: sqlrules-sqlite<2,>=1; extra == 'sqlite'
|
|
59
|
+
Description-Content-Type: text/markdown
|
|
60
|
+
|
|
61
|
+
# SQLRules
|
|
62
|
+
|
|
63
|
+
[](https://github.com/eddiethedean/sqlrules/actions/workflows/ci.yml)
|
|
64
|
+
[](https://pypi.org/project/sqlrules/)
|
|
65
|
+
[](https://sqlrules.readthedocs.io/en/latest/)
|
|
66
|
+
[](https://github.com/eddiethedean/sqlrules/blob/main/LICENSE)
|
|
67
|
+
|
|
68
|
+
**Compile Pydantic Field constraint metadata into SQLAlchemy WHERE expressions.**
|
|
69
|
+
|
|
70
|
+
SQLRules reads constraints declared on a model (`Field(ge=18)`, `min_length`,
|
|
71
|
+
`Literal`, …) and turns them into SQLAlchemy boolean expressions. It depends on
|
|
72
|
+
Pydantic v2 and SQLAlchemy 2.x, performs **no database I/O**, and needs a
|
|
73
|
+
dialect plugin only for non-portable operators (regex, JSON, arrays, …).
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
SQLRules does THIS (constraint metadata → expressions):
|
|
77
|
+
Field(ge=18) → column >= 18
|
|
78
|
+
|
|
79
|
+
It does NOT do this (instance values → predicates):
|
|
80
|
+
UserFilter(age=25) → column == 25 # not what sqlrules.compile does
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Not an ORM, validator, or query builder: **constraint metadata → expressions**,
|
|
84
|
+
nothing else.
|
|
85
|
+
|
|
86
|
+
**In 30 seconds:**
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install "sqlrules>=1,<2"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from typing import Annotated
|
|
94
|
+
|
|
95
|
+
from pydantic import BaseModel, Field
|
|
96
|
+
from sqlalchemy import Column, Integer, MetaData, String, Table
|
|
97
|
+
|
|
98
|
+
import sqlrules
|
|
99
|
+
|
|
100
|
+
users = Table(
|
|
101
|
+
"users",
|
|
102
|
+
MetaData(),
|
|
103
|
+
Column("age", Integer),
|
|
104
|
+
Column("name", String),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
class UserFilter(BaseModel):
|
|
108
|
+
age: Annotated[int, Field(ge=18, le=65)]
|
|
109
|
+
name: Annotated[str, Field(min_length=2)]
|
|
110
|
+
|
|
111
|
+
rules = sqlrules.compile(UserFilter, users)
|
|
112
|
+
# {
|
|
113
|
+
# "age": [users.c.age >= 18, users.c.age <= 65],
|
|
114
|
+
# "name": [length(users.c.name) >= 2],
|
|
115
|
+
# }
|
|
116
|
+
|
|
117
|
+
stmt = users.select().where(*sqlrules.where(rules))
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
> **`pattern` needs a plugin.** Core compiles comparisons, lengths, `Literal`,
|
|
121
|
+
> and `Enum`. Regex / JSON / arrays require a dialect package (see below).
|
|
122
|
+
> Prefer static patterns; untrusted regex can be expensive (see
|
|
123
|
+
> [SECURITY](https://sqlrules.readthedocs.io/en/latest/SECURITY.html)).
|
|
124
|
+
|
|
125
|
+
Full walkthrough: [Getting started](https://sqlrules.readthedocs.io/en/latest/guides/getting-started.html) · Runnable scripts: [`examples/`](examples/).
|
|
126
|
+
|
|
127
|
+
| | |
|
|
128
|
+
|---|---|
|
|
129
|
+
| **Latest** | [sqlrules on PyPI](https://pypi.org/project/sqlrules/) · [Changelog](https://sqlrules.readthedocs.io/en/latest/project/changelog.html) |
|
|
130
|
+
| **Docs** | [sqlrules.readthedocs.io](https://sqlrules.readthedocs.io/en/latest/) |
|
|
131
|
+
| **Python** | 3.10+ (Pydantic v2, SQLAlchemy 2.x) |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Choose your path
|
|
136
|
+
|
|
137
|
+
| Path | Start here |
|
|
138
|
+
|---|---|
|
|
139
|
+
| **Not sure?** | [Start here](https://sqlrules.readthedocs.io/en/latest/guides/start-here.html) |
|
|
140
|
+
| **Compile constraints** | [Getting started](https://sqlrules.readthedocs.io/en/latest/guides/getting-started.html) |
|
|
141
|
+
| **Dialect plugins** | [Plugin system](https://sqlrules.readthedocs.io/en/latest/PLUGIN_SYSTEM.html) |
|
|
142
|
+
| **Evaluate fit** | [Design philosophy](https://sqlrules.readthedocs.io/en/latest/guides/design-philosophy.html) |
|
|
143
|
+
| **Contribute** | [Contributing](https://sqlrules.readthedocs.io/en/latest/project/contributing.html) |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Install
|
|
148
|
+
|
|
149
|
+
**Requires** Python 3.10+, Pydantic v2, and SQLAlchemy 2.x (pulled in
|
|
150
|
+
transitively).
|
|
151
|
+
|
|
152
|
+
### From PyPI (1.0+)
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pip install "sqlrules>=1,<2"
|
|
156
|
+
# or: uv add "sqlrules>=1,<2"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Confirm the installed version is **1.0.0 or newer** (`pip show sqlrules`).
|
|
160
|
+
If PyPI still serves an older release, or dialect packages are missing, use
|
|
161
|
+
**from source** below.
|
|
162
|
+
|
|
163
|
+
Optional dialect plugins (same major line as core):
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
pip install "sqlrules-postgresql>=1,<2" # ~ / ~*, JSONB, ARRAY, range
|
|
167
|
+
pip install "sqlrules-sqlite>=1,<2" # REGEXP helper + JSON
|
|
168
|
+
pip install "sqlrules-mysql>=1,<2" # REGEXP, JSON, full-text
|
|
169
|
+
pip install "sqlrules-mssql>=1,<2" # JSON + LEN string ops
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Extras are equivalent shortcuts (pull the matching plugin package):
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
pip install "sqlrules[postgresql]"
|
|
176
|
+
pip install "sqlrules[dialects]" # all four
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
SQLite `pattern` emits `REGEXP` — call `sqlrules_sqlite.register_regexp(connection)`
|
|
180
|
+
on each connection before executing queries.
|
|
181
|
+
|
|
182
|
+
### From source (repo / pre-PyPI)
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
git clone https://github.com/eddiethedean/sqlrules.git
|
|
186
|
+
cd sqlrules
|
|
187
|
+
python -m venv .venv && source .venv/bin/activate
|
|
188
|
+
make install # editable core + all four dialect plugins
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Or install built wheels from a checkout that already ran `make dist`:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
pip install dist/sqlrules-*.whl dist-plugins/sqlrules_*.whl
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**End users** should prefer PyPI once 1.0+ is published. You do not need to
|
|
198
|
+
clone this repo or install from `packages/` for normal application use.
|
|
199
|
+
|
|
200
|
+
### Plugin example
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from typing import Annotated, Any
|
|
204
|
+
|
|
205
|
+
from pydantic import BaseModel, Field
|
|
206
|
+
from sqlalchemy import Column, MetaData, String, Table
|
|
207
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
208
|
+
|
|
209
|
+
import sqlrules
|
|
210
|
+
from sqlrules import Compiler, JsonContains
|
|
211
|
+
from sqlrules_postgresql import PostgresPlugin
|
|
212
|
+
|
|
213
|
+
table = Table(
|
|
214
|
+
"rows",
|
|
215
|
+
MetaData(),
|
|
216
|
+
Column("name", String),
|
|
217
|
+
Column("meta", JSONB),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
class RowFilter(BaseModel):
|
|
221
|
+
name: Annotated[str, Field(pattern=r"^A")]
|
|
222
|
+
meta: Annotated[dict[str, Any], JsonContains({"active": True})]
|
|
223
|
+
|
|
224
|
+
# dialect= is a hint for custom translators only — it does not load plugins.
|
|
225
|
+
compiler = Compiler(plugins=[PostgresPlugin()], dialect="postgresql")
|
|
226
|
+
rules = compiler.compile(RowFilter, table)
|
|
227
|
+
stmt = table.select().where(*sqlrules.where(rules))
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
See [`examples/postgresql_pattern.py`](examples/postgresql_pattern.py).
|
|
231
|
+
|
|
232
|
+
### Common failures
|
|
233
|
+
|
|
234
|
+
| Symptom | Fix |
|
|
235
|
+
|---|---|
|
|
236
|
+
| `UnsupportedConstraintError` on `pattern` | Install a dialect plugin and pass it to `Compiler(plugins=[...])` |
|
|
237
|
+
| `NameError: sqlrules` after `from sqlrules import Compiler` | Also `import sqlrules` (or import `where`) |
|
|
238
|
+
| `pip` installs < 1.0 / dialect package 404 | Install [from source](#from-source-repo--pre-pypi) until PyPI has 1.0+ |
|
|
239
|
+
| SQLite `REGEXP` errors at execute time | Call `register_regexp(connection)` |
|
|
240
|
+
|
|
241
|
+
More: [Troubleshooting](https://sqlrules.readthedocs.io/en/latest/guides/troubleshooting.html).
|
|
242
|
+
|
|
243
|
+
## Supported constraints (1.0)
|
|
244
|
+
|
|
245
|
+
| Constraint | SQLAlchemy expression |
|
|
246
|
+
|---|---|
|
|
247
|
+
| `gt` / `ge` / `lt` / `le` | `column > / >= / < / <= value` |
|
|
248
|
+
| `multiple_of` | `column % value == 0` |
|
|
249
|
+
| `min_length` / `max_length` | `func.length(column) >= / <= value` |
|
|
250
|
+
| `Literal[...]` | `column.in_(...)` |
|
|
251
|
+
| `Enum` | `column.in_(...)` |
|
|
252
|
+
|
|
253
|
+
`pattern` is extracted into IR (`PatternSpec`) but has **no portable core
|
|
254
|
+
translator**. Install a dialect plugin or register a custom translator.
|
|
255
|
+
|
|
256
|
+
With `emit_type_checks=True`, supported scalar annotations also emit
|
|
257
|
+
`type_check` (`TypeSpec`) IR — likewise plugin-translated only.
|
|
258
|
+
|
|
259
|
+
Dialect markers (`JsonContains`, `ArrayContains`, `RangeContains`,
|
|
260
|
+
`FullTextMatch`, …) are re-exported from `sqlrules` (and live in
|
|
261
|
+
`sqlrules.markers`); they require a dialect plugin.
|
|
262
|
+
|
|
263
|
+
Unsupported constraints raise `UnsupportedConstraintError` by default.
|
|
264
|
+
Use `on_unsupported="warn"` or `"ignore"` for unknown **operators**.
|
|
265
|
+
Unconstrained fields with **supported** types are omitted from the rules
|
|
266
|
+
dict. Fields with **unsupported** type annotations always raise — even if
|
|
267
|
+
unconstrained.
|
|
268
|
+
|
|
269
|
+
## When *not* to use SQLRules
|
|
270
|
+
|
|
271
|
+
Skip SQLRules when:
|
|
272
|
+
|
|
273
|
+
- You need **request/instance values** as WHERE predicates
|
|
274
|
+
(`UserFilter(age=25)` → `age = 25`). That is a query/filter library, not
|
|
275
|
+
this compiler.
|
|
276
|
+
- You have a few static expressions and will never share constraint metadata
|
|
277
|
+
with a Pydantic model — write SQLAlchemy clauses directly.
|
|
278
|
+
- You need **portable regex** with core alone — `pattern` requires a dialect
|
|
279
|
+
plugin or custom translator.
|
|
280
|
+
- You expect `dialect="postgresql"` to **load plugins** — it is a hint only;
|
|
281
|
+
pass `plugins=[...]` explicitly.
|
|
282
|
+
- You want a general-purpose **query builder**, ORM, or SQL string generator.
|
|
283
|
+
|
|
284
|
+
SQLRules pays off when the Pydantic model *is* the shared source of truth for
|
|
285
|
+
constraint metadata across many fields or dialects.
|
|
286
|
+
|
|
287
|
+
## Public API
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
sqlrules.compile(model, table, *, column_map=None, on_unsupported="raise", cache=True, emit_type_checks=False)
|
|
291
|
+
sqlrules.where(rules) # prefer this to flatten expressions
|
|
292
|
+
sqlrules.flatten(rules) # alias of where()
|
|
293
|
+
sqlrules.clear_model_cache() # drop process-wide Phase-1 IR cache
|
|
294
|
+
|
|
295
|
+
compiler = sqlrules.Compiler(
|
|
296
|
+
plugins=[...], # optional SQLRulesPlugin instances
|
|
297
|
+
on_conflict="raise", # raise | replace | ignore
|
|
298
|
+
dialect=None, # hint only — does not load plugins
|
|
299
|
+
on_unsupported="raise",
|
|
300
|
+
cache=True,
|
|
301
|
+
emit_type_checks=False, # opt-in type_check IR (needs plugin translator)
|
|
302
|
+
)
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Non-goals
|
|
306
|
+
|
|
307
|
+
SQLRules is not an ORM, validator, query builder, SQL string generator,
|
|
308
|
+
migration tool, or database client. It only compiles supported Pydantic
|
|
309
|
+
constraints into SQLAlchemy expressions. See
|
|
310
|
+
[design philosophy](https://sqlrules.readthedocs.io/en/latest/guides/design-philosophy.html)
|
|
311
|
+
and [non-goals](https://sqlrules.readthedocs.io/en/latest/NON_GOALS.html).
|
|
312
|
+
|
|
313
|
+
## Documentation
|
|
314
|
+
|
|
315
|
+
Full site: **[sqlrules.readthedocs.io](https://sqlrules.readthedocs.io/en/latest/)**
|
|
316
|
+
|
|
317
|
+
| Topic | Link |
|
|
318
|
+
|---|---|
|
|
319
|
+
| Getting started | [Guide](https://sqlrules.readthedocs.io/en/latest/guides/getting-started.html) |
|
|
320
|
+
| How-to guides | [ORM binding](https://sqlrules.readthedocs.io/en/latest/guides/orm-column-map.html) · [Markers](https://sqlrules.readthedocs.io/en/latest/guides/markers.html) |
|
|
321
|
+
| Examples | [`examples/`](examples/) |
|
|
322
|
+
| Spec & constraints | [SPEC](https://sqlrules.readthedocs.io/en/latest/SPEC.html) · [CONSTRAINTS](https://sqlrules.readthedocs.io/en/latest/CONSTRAINTS.html) |
|
|
323
|
+
| Plugins | [Plugin system](https://sqlrules.readthedocs.io/en/latest/PLUGIN_SYSTEM.html) |
|
|
324
|
+
| API reference | [Reference hub](https://sqlrules.readthedocs.io/en/latest/reference/index.html) |
|
|
325
|
+
| FAQ | [FAQ](https://sqlrules.readthedocs.io/en/latest/guides/faq.html) |
|
|
326
|
+
| Support | [Support & compatibility](https://sqlrules.readthedocs.io/en/latest/project/support.html) |
|
|
327
|
+
| Roadmap | [Roadmap](https://sqlrules.readthedocs.io/en/latest/project/roadmap.html) |
|
|
328
|
+
| Changelog | [Changelog](https://sqlrules.readthedocs.io/en/latest/project/changelog.html) |
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
pip install -e ".[docs]"
|
|
332
|
+
make docs
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Repo map
|
|
336
|
+
|
|
337
|
+
| Path | Who cares |
|
|
338
|
+
|---|---|
|
|
339
|
+
| PyPI `sqlrules` / `sqlrules-*` | **Application users** — install these |
|
|
340
|
+
| [`examples/`](examples/) | Copy-paste runnable scripts |
|
|
341
|
+
| [`docs/`](docs/) | Spec, guides, architecture (also on Read the Docs) |
|
|
342
|
+
| [`packages/`](packages/) | **Contributors** — official dialect plugin sources |
|
|
343
|
+
| [`src/sqlrules/`](src/sqlrules/) | Core library source |
|
|
344
|
+
| [`benchmarks/`](benchmarks/) | Optional local compile benchmarks |
|
|
345
|
+
|
|
346
|
+
## Development (contributors)
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
make install
|
|
350
|
+
make check # lint, types, tests, docs (matches most of CI)
|
|
351
|
+
make dist # packaging smoke — required before release / to match full CI
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Maintainers: [RELEASING.md](RELEASING.md).
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
MIT
|