diff options
| author | Bobby <[email protected]> | 2026-07-16 18:03:04 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-07-16 18:03:04 +0530 |
| commit | 99b159c11ccc3f2dacea64416047b1778e346b81 (patch) | |
| tree | 6b5f87a9949660f9042a506b592b64aa634cfeb4 /docs | |
| parent | b9914de793844d3ad6e2d7503209ac8fd339d6de (diff) | |
| parent | 188c17c19a0c32e944d9a4234d33702d3adfe143 (diff) | |
| download | edify-99b159c11ccc3f2dacea64416047b1778e346b81.tar.xz edify-99b159c11ccc3f2dacea64416047b1778e346b81.zip | |
Deprecation policy, 0.3 → 1.0 upgrade guide, MIT relicense, and codebase-wide strict typing (#286)
Ships the deprecation-and-license milestone — a written deprecation
policy, the rewritten 0.3 → 1.0 upgrade guide, the relicense to MIT, and
the machinery that keeps the public surface and upgrade notes honest —
alongside a codebase-wide pass to fully strict static typing.
## Deprecation policy & upgrade guide
- A written deprecation policy covering the stub pattern, removal
cadence, message format, and the behavior-change rule.
- The 0.3 → 1.0 upgrade guide rewritten against the shipped breaking
surface, with frozen section anchors so a warning can link straight to
its migration note.
- `docs/upgrading/` scaffolding wired into the documentation table of
contents.
- Every deprecated stub carries a warning-assertion harness, and a gate
parses each warning's URL to guarantee the anchor it points at exists in
the guide.
## Changes fragments & public-surface snapshot
- A one-file-per-change `changes/` fragment format plus a small
standard-library generator that assembles the changelog and
upgrade-guide sections from them.
- A committed `.public-surface` snapshot of the exported API,
regenerated whenever the surface moves.
- A gate that fails a pull request whose public surface shifts without
an accompanying `changes/` fragment or an explicit no-migration marker.
## Relicense to MIT
- The changelog records the relicense in a single cross-linked entry,
and the upgrade guide carries the matching license anchor.
## Trusted Publishing
- Releases publish through Trusted Publishing, retiring the long-lived
upload token.
## Fully strict typing
- The library, tests, and tooling now pass fully strict static
type-checking end to end, with zero inline suppressions and zero blanket
rule overrides.
- Optional third-party integrations that ship no type information are
reached through typed boundaries and local stubs, keeping the strict
guarantee intact without importing them at module load.
- Helper-level tests now exercise their behavior through the public API
rather than reaching into private functions, and unreachable defensive
branches were retired so every line stays covered.
Closes #199, Closes #200, Closes #201, Closes #202, Closes #203, Closes
#204, Closes #205, Closes #206, Closes #207, Closes #228, Closes #235
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/deprecation-policy.rst | 73 | ||||
| -rw-r--r-- | docs/index.rst | 2 | ||||
| -rw-r--r-- | docs/upgrading/0.3-to-1.0.rst | 202 | ||||
| -rw-r--r-- | docs/upgrading/index.rst | 14 |
4 files changed, 250 insertions, 41 deletions
diff --git a/docs/deprecation-policy.rst b/docs/deprecation-policy.rst new file mode 100644 index 0000000..0879406 --- /dev/null +++ b/docs/deprecation-policy.rst @@ -0,0 +1,73 @@ +.. _deprecation-policy: + +Deprecation Policy +================== + +This policy governs how every removed, renamed, or behavior-changed public API +in Edify **1.0 and later** communicates the change to existing users. It does +not apply to the 0.3 → 1.0 transition, which is a clean redesign with no +deprecation stubs — see :ref:`upgrading-0-3-to-1-0` for that move. + +The stub pattern +---------------- + +When a public symbol is renamed or removed, the old name stays importable for one +minor-release cycle as a **deprecation stub**. The stub forwards to the new +implementation (for a rename) or raises on use (for a removal), and always emits +a :class:`DeprecationWarning` on first use. + +.. code-block:: python + + import warnings + + def old_name(*args, **kwargs): + warnings.warn( + "old_name is deprecated since 1.1; use new_name. " + "See https://edify.readthedocs.io/en/latest/upgrading/1.0-to-1.1.html#new-name", + DeprecationWarning, + stacklevel=2, + ) + return new_name(*args, **kwargs) + +Message format +-------------- + +Every deprecation warning message follows this exact shape:: + + <name> is deprecated since <version>; use <replacement>. See <docs-url> + +* ``<name>`` — the deprecated symbol, spelled exactly as the user typed it. +* ``<version>`` — the release that introduced the deprecation. +* ``<replacement>`` — the symbol to use instead, or a one-clause instruction + when there is no drop-in replacement. +* ``<docs-url>`` — an absolute URL into the upgrade guide, ending in the frozen + ``#anchor`` for the relevant section. The anchor is a + ``.. _label:`` target under ``docs/upgrading/``, decoupled from the heading + text so rewording a heading never breaks the link. + +Warning category and stacklevel +------------------------------- + +* The category is always :class:`DeprecationWarning`. +* ``stacklevel=2`` so the warning points at the caller's line, not at the stub. +* Each stub fires its warning **exactly once per call site** — it never suppresses + or batches, and it never fires at import time. + +Removal cadence +--------------- + +* A symbol deprecated in release ``X.Y`` remains importable through the end of the + ``X.(Y+1)`` line and is removed no earlier than ``X.(Y+2)``. +* Removals only land in a minor or major release, never a patch release. +* The removal is recorded as a breaking change in the CHANGELOG and gets its own + section (with a frozen anchor) in the matching upgrade guide. + +Behavior-change rule +-------------------- + +A change to what an existing, unchanged call *returns* or *raises* — with no +change to its name or signature — is a breaking change even though nothing looks +different at the call site. It is documented in the upgrade guide with a +before/after, cross-linked from the CHANGELOG, and (where technically possible) +announced with a :class:`DeprecationWarning` for one cycle before the new +behavior becomes the default. diff --git a/docs/index.rst b/docs/index.rst index 8799638..94ce6cc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,6 +9,8 @@ Contents built-in/index regex-builder/index integrations/index + upgrading/index + deprecation-policy contributing authors changelog diff --git a/docs/upgrading/0.3-to-1.0.rst b/docs/upgrading/0.3-to-1.0.rst index 900fc25..2c7c1b3 100644 --- a/docs/upgrading/0.3-to-1.0.rst +++ b/docs/upgrading/0.3-to-1.0.rst @@ -1,18 +1,46 @@ .. _upgrading-0-3-to-1-0: Upgrading from 0.3 to 1.0 -========================== +========================= + +The 1.0 release is a ground-up redesign. This guide covers only the breaking +surface — the changes that require a code edit or a behavior review when moving +a 0.3 codebase to 1.0. Every section below carries an explicit ``.. _label:`` +anchor; deprecation-warning URLs point at these anchors, so they stay valid even +if a heading is later reworded. + +.. _license-mit: + +License changed to MIT +---------------------- + +Edify 1.0 is released under the MIT License. Releases ``0.3.0`` and earlier were +Apache-2.0. + +Two user-facing facts: + +* MIT drops Apache-2.0's **explicit patent grant**. If your legal review + specifically relied on that grant, pin ``edify<1.0``. +* Every git tag ``v0.3.0`` and earlier **remains Apache-2.0 permanently** — the + relicense applies going forward only. Installs pinned to a ``0.3.x`` tag are + unaffected. + +.. _python-floor: + +Python floor raised to 3.11 +--------------------------- + +Edify 1.0 requires Python 3.11 or newer. The 0.3 line supported 3.8+. Upgrade +your interpreter before installing 1.0; ``pip install edify`` on 3.10 or older +resolves to the last 0.3 release. .. _validators-callable: Validators are callable ``Pattern`` instances --------------------------------------------- -In 0.3 the built-in validators under ``edify.library`` were plain functions. -In 1.0 every validator exposes itself as a :class:`edify.Pattern` instance -that is *callable* — ``email(value)`` still returns a ``bool``, and the -underlying pattern is available via ``email.match(value)``, -``email.to_regex_string()``, and every other :class:`Pattern` method: +In 0.3 the built-in validators under ``edify.library`` were plain functions. In +1.0 every validator is a callable :class:`edify.Pattern` instance: .. code-block:: python @@ -25,41 +53,133 @@ underlying pattern is available via ``email.match(value)``, email("[email protected]") # True (call form unchanged) email.match("[email protected]") # <re.Match object> -The library is also reorganized into 22 category folders (``identifier/``, -``address/``, ``contact/``, ``auth/``, ``financial/``, ``temporal/``, -``geo/``, ``numeric/``, ``text/``, ``color/``, ``media/``, ``software/``, -``transport/``, ``publishing/``, ``product/``, ``medical/``, ``api/``, -``data/``, ``document/``, ``security/``, ``grammar/``, ``web/``). Every -validator is importable both from its category submodule and from the -flat top-level ``edify.library`` namespace: +The ``validator(value) -> bool`` call form is unchanged, so most call sites need +no edit. The new surface adds ``.match``, ``.search``, ``.to_regex_string``, and +every other :class:`Pattern` method on top. + +.. _library-reorg: + +Library reorganized into category submodules +-------------------------------------------- + +The library moved from a flat module into category folders (``identifier/``, +``address/``, ``contact/``, ``auth/``, ``financial/``, ``temporal/``, and more). +Every validator remains importable from the flat top-level namespace, and is now +*also* importable from its category submodule: .. code-block:: python - from edify.library import uuid # flat top-level - from edify.library.identifier import uuid # category submodule - -The library grew from 14 validators to **200 exported validators** across -these categories. - -Renamed and merged validators ------------------------------ - -Several 0.3 names have been merged into more general patterns in 1.0: - -+---------------------------------+-------------------------------+ -| 0.3 name | 1.0 replacement | -+=================================+===============================+ -| ``iso_date`` | ``date`` | -+---------------------------------+-------------------------------+ -| ``email_rfc_5322`` | ``email`` | -+---------------------------------+-------------------------------+ -| ``ipv4``, ``ipv6`` | ``ip`` | -+---------------------------------+-------------------------------+ -| ``phone_number`` | ``phone`` | -+---------------------------------+-------------------------------+ -| ``zip`` | ``postal`` | -+---------------------------------+-------------------------------+ - -The new names accept any recognised form of their concept, and the old -name-plus-``form`` variants continue to work via each validator's -``form`` / ``strict`` / ``version`` keyword arguments where applicable. + from edify.library import uuid # flat top-level (unchanged) + from edify.library.identifier import uuid # category submodule (new) + +If you imported validators from ``edify.library`` in 0.3, those imports keep +working. + +.. _named-backref-return: + +Named back-references no longer raise +------------------------------------- + +In 0.3, ``.named_back_reference(name)`` raised when the named group had not been +declared earlier in the chain. In 1.0 it emits the back-reference and defers +validation to compile time, matching the behavior of the numeric +``.back_reference(index)``. + +.. _to-regex-string-output: + +``to_regex_string()`` output normalized +--------------------------------------- + +The emitted pattern string is now the minimal correct form. The most visible +change is character-class escaping: inside ``[...]`` only ``\``, ``]``, +first-position ``^``, and interior ``-`` are escaped; every other metacharacter +is a literal. + +.. code-block:: text + + 0.3: any_of_chars('#?!@$%^&*-') -> [\#\?!@$%\^\&\*\-] + 1.0: any_of_chars('#?!@$%^&*-') -> [#?!@$%^&*-] + +The two forms match identically — only the string representation changed. Code +that compared ``to_regex_string()`` against a hard-coded expected string with the +old redundant backslashes must drop them. + +.. _char-class-escape: + +Character-class escaping is the minimal correct form +---------------------------------------------------- + +This is the rule behind the ``to_regex_string()`` change above, stated directly +for callers who construct char classes: ``any_of_chars`` and +``anything_but_chars`` escape only the characters that carry syntactic meaning +inside ``[...]``. Match behavior is identical to 0.3's over-escaped output. + +.. _regex-wrapper-return: + +Match methods return an edify ``Match`` wrapper +----------------------------------------------- + +``.match``, ``.search``, and ``.fullmatch`` on :class:`Regex` (and ``.finditer``) +now return an edify :class:`edify.result.Match` rather than a bare +:class:`re.Match`. Named captures are reachable as attributes +(``m.username`` / ``m.captures.username``), and every :class:`re.Match` method +(``.group()``, ``.groupdict()``, ``.span()``, …) is forwarded, so existing call +sites keep working. + +.. _closed-match-verbs: + +Builder match-verb surface closed at five +----------------------------------------- + +The builder exposes exactly ``test`` / ``match`` / ``search`` / ``findall`` / +``sub``. The less-common ``fullmatch`` / ``finditer`` / ``subn`` / ``split`` +verbs moved off the builder — reach them through ``.to_regex()`` and call them on +the returned :class:`Regex`: + +.. code-block:: python + + # 0.3 + builder.finditer(text) + + # 1.0 + builder.to_regex().finditer(text) + +.. _silent-failure-raises: + +Silent failures now raise typed errors +--------------------------------------- + +Constructs that failed silently or produced a cryptic stdlib error in 0.3 now +raise a specific :class:`edify.errors.EdifySyntaxError` subclass with an +annotated message. The most common case: a variable-width ``assert_behind`` under +the default ``re`` engine raises +:class:`edify.errors.backend.VariableWidthLookbehindNotSupportedError` instead of +surfacing ``re``'s "look-behind requires fixed-width pattern". + +.. _engine-kwarg: + +Optional ``regex`` engine via ``engine=`` kwarg +----------------------------------------------- + +``to_regex(engine="regex")`` selects the third-party ``regex`` backend, which +unlocks variable-width lookbehind and per-call timeouts. It requires the extra: +``pip install edify[regex]``. The default ``engine="re"`` is unchanged. + +.. _builder-equality: + +Builder equality compares emitted patterns +------------------------------------------ + +Two builders compare equal when they emit the same pattern string and carry the +same flags, rather than by object identity. This makes builders usable as +dict keys and in set membership. + +.. _moved-import-paths: + +Moved internal import paths +--------------------------- + +Internal machinery that leaked into 0.3's import surface has been privatized or +relocated. If you imported from an undocumented internal path in 0.3, import the +symbol from its documented location instead — every public symbol is reachable +from ``edify`` or a documented submodule. diff --git a/docs/upgrading/index.rst b/docs/upgrading/index.rst new file mode 100644 index 0000000..ba2e83a --- /dev/null +++ b/docs/upgrading/index.rst @@ -0,0 +1,14 @@ +.. _upgrading: + +Upgrading +========= + +Every breaking transition between released versions of Edify has a dedicated +guide below. Each guide freezes an explicit ``.. _label:`` anchor on every +section so the deprecation-warning URLs stay valid even when a heading is +reworded. + +.. toctree:: + :maxdepth: 2 + + 0.3-to-1.0 |
