diff options
| author | Bobby <[email protected]> | 2026-07-16 14:21:24 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-07-16 14:21:24 +0530 |
| commit | b9914de793844d3ad6e2d7503209ac8fd339d6de (patch) | |
| tree | ede1621e0daf6e15706595fb905c0fbc91f26dfd /.github/workflows | |
| parent | 433d41bf5887e61cc570a874e244e226c25b8a41 (diff) | |
| parent | 453aea2b3ec5ae941407ac781ede7e4efda43ae8 (diff) | |
| download | edify-b9914de793844d3ad6e2d7503209ac8fd339d6de.tar.xz edify-b9914de793844d3ad6e2d7503209ac8fd339d6de.zip | |
feat(integrations): pydantic, fastapi, django integration modules as opt-in extras (#284)
Ships the framework-integrations bundle end to end so an
:class:`edify.Pattern` can drop straight into the field-validation
layers of the three Python frameworks most teams actually use.
## Integration modules
- **`edify.integrations.pydantic`** — `pattern_validator(pattern)`
returns a Pydantic-compatible validator callable;
`pattern_field(pattern)` returns an `Annotated[str,
AfterValidator(...)]` type you drop into a `BaseModel` field.
- **`edify.integrations.fastapi`** — `pattern_query(pattern, ...)` and
`pattern_path(pattern, ...)` return `fastapi.Query` / `fastapi.Path`
values pinned to the pattern's emitted regex string, with `default=` and
every other FastAPI kwarg forwarded through.
- **`edify.integrations.django`** — `pattern_validator(pattern,
message=..., code=...)` returns a
`django.core.validators.RegexValidator` pinned to the pattern's regex
source, with the message default derived from the pattern.
Every integration follows the same deferred-import pattern that
`edify[regex]` uses: `import edify.integrations.pydantic` (etc.)
succeeds without the framework installed; the first helper call resolves
the framework lazily and raises `MissingIntegrationDependencyError`
(annotated summary + pointer block + `= note:` + `help:` line) telling
the caller to `pip install edify[<framework>]`.
## Extras + optional-dependencies
- `[project.optional-dependencies]` gains `pydantic =
["pydantic>=2.0"]`, `fastapi = ["fastapi>=0.100"]`, `django =
["django>=4.2"]`, and `all = [everything]`.
- The `dev` group installs all three frameworks so local + CI runs cover
the integrations without gating on the extras.
## CI install-per-extra verification
- New matrix job `install-with-integration-extra` runs one entry per
extra (`pydantic`, `fastapi`, `django`, `all`).
- Each entry installs into a fresh venv, imports the integration module,
and drives a small probe script that verifies the helper accepts a
matching value and rejects a non-matching value.
- Same shape as the existing `install: edify[regex]` verification — any
drift in the `[project.optional-dependencies]` declarations fails
loudly.
## Sphinx autodoc
- New `docs/integrations/index.rst` page with `.. automodule::` blocks
for each of the three integration modules.
- Wired into `docs/index.rst` toctree.
- `.readthedocs.yml` installs `.[all]` so autodoc always has every
framework annotation available to resolve.
Closes #220, closes #221, closes #222, closes #223, closes #224, closes
#225, closes #226, closes #227.
Diffstat (limited to '.github/workflows')
| -rw-r--r-- | .github/workflows/github-actions.yml | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index c0ed6e2..f9f49d6 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -131,6 +131,81 @@ jobs: assert via_regex.search("foofoobar") is not None PYCHECK + install-with-integration-extra: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + include: + - name: 'install: edify[pydantic]' + extra: 'pydantic' + probe: | + import edify + from edify.integrations.pydantic import pattern_validator + from edify.library import email + validate = pattern_validator(email) + assert validate("[email protected]") == "[email protected]" + try: + validate("bad") + except ValueError: + pass + else: + raise SystemExit("expected ValueError on non-matching input") + - name: 'install: edify[fastapi]' + extra: 'fastapi' + probe: | + import edify + from edify.integrations.fastapi import pattern_query, pattern_path + from edify.library import uuid + from fastapi.params import Query, Path + assert isinstance(pattern_query(uuid), Query) + assert isinstance(pattern_path(uuid), Path) + - name: 'install: edify[django]' + extra: 'django' + probe: | + import edify + from edify.integrations.django import pattern_validator + from edify.library import uuid + from django.core.exceptions import ValidationError + from django.core.validators import RegexValidator + validator = pattern_validator(uuid) + assert isinstance(validator, RegexValidator) + validator("01234567-89ab-1cde-8f01-23456789abcd") + try: + validator("not-a-uuid") + except ValidationError: + pass + else: + raise SystemExit("expected ValidationError on non-matching input") + - name: 'install: edify[all]' + extra: 'all' + probe: | + import edify + import pydantic + import fastapi + import django + from edify.integrations import pydantic as edify_pydantic + from edify.integrations import fastapi as edify_fastapi + from edify.integrations import django as edify_django + from edify.library import email + assert edify_pydantic.pattern_validator(email)("[email protected]") == "[email protected]" + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 + - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 + with: + python-version: '3.11' + - name: install edify[${{ matrix.extra }}] + run: | + uv venv .venv-${{ matrix.extra }} + uv pip install --python .venv-${{ matrix.extra }}/bin/python '.[${{ matrix.extra }}]' + - name: probe integration surface + env: + PROBE_SCRIPT: ${{ matrix.probe }} + run: | + .venv-${{ matrix.extra }}/bin/python -c "$PROBE_SCRIPT" + bench: name: 'bench (advisory)' runs-on: ubuntu-latest |
