| Age | Commit message (Collapse) | Author | Files | Lines | |
|---|---|---|---|---|---|
| 12 days | chore(typing): pyright per-directory overrides for graphviz/regex missing stubs | natsuoto | 1 | -2/+13 | |
| 12 days | refactor(typing): make edify pass mypy --strict and pyright --strict end to end | natsuoto | 15 | -46/+101 | |
| 12 days | test(discipline): every type-checker suppression carries a code and inline ↵ | natsuoto | 1 | -0/+35 | |
| reason | |||||
| 12 days | feat(builder): typed engine=Literal kwarg on to_regex with EngineNotWiredError | natsuoto | 4 | -0/+67 | |
| 12 days | refactor(builder): TypeVar-generic helpers and cross-mixin surface on ↵ | natsuoto | 3 | -20/+37 | |
| BuilderProtocol | |||||
| 12 days | refactor(result): keep _RePatternAttribute union on Regex.__getattr__; drop ↵ | natsuoto | 1 | -1/+2 | |
| Any escape | |||||
| 12 days | refactor(atoms): drop docstring from __init__ per exports-only rule | natsuoto | 1 | -7/+0 | |
| 12 days | refactor(serialize): precise JSONValue types replace Any; hoist imports; ↵ | natsuoto | 7 | -170/+127 | |
| drop docstring from __init__ | |||||
| 12 days | chore: refresh uv.lock for mypy + pyright pins | natsuoto | 1 | -0/+72 | |
| 12 days | chore(typing): ship py.typed marker and library-scope mypy/pyright config | natsuoto | 2 | -0/+33 | |
| 13 days | feat(pattern): canonical dict/JSON serialization for Pattern (#277) | Bobby | 9 | -0/+792 | |
| Adds canonical serialization to `Pattern` — round-trip-safe dict and JSON, with a schema-version header and a public `kind`-based AST vocabulary that shields the wire format from internal class-name churn. ## Usage ```python from edify import Pattern p = Pattern().start_of_input().named_capture("year").exactly(4).digit().end().ignore_case() blob = p.to_json() # {"edify":0,"flags":{"ignore_case":true},"pattern":{"children":[...],"kind":"root"}} restored = Pattern.from_json(blob) assert restored == p ``` ## Schema shape ```json { "edify": 0, "pattern": { "kind": "root", "children": [ { "kind": "start" }, { "kind": "named-capture", "name": "year", "children": [ { "kind": "exactly", "times": 4, "child": { "kind": "digit" } } ] } ] }, "flags": { "ignore_case": true } } ``` Every element carries a public `kind` string (registered in `edify.serialize.kinds`). Internal class names (`ExactlyElement`, etc.) never appear in the payload, so later privatisation and refactors don't leak into serialized output. The `flags` key is omitted when no flags are set. ## Schema version `SCHEMA_VERSION = 0` — **experimental**. The concrete AST shape may change without a deprecation cycle until it is promoted to `1` in a later release. Consumers should not commit to the on-wire shape yet. `from_dict` / `from_json` reject any payload whose `edify` key does not match this build's `SCHEMA_VERSION`, and reject unknown `kind` values with `UnknownElementKindError` so payloads from a newer build fail loudly on an older loader. ## Forward-compatibility Unknown fields on individual element dicts are silently ignored, so a newer edify that adds `{"kind": "digit", "future_marker": true}` will still load on this build — the extra field just drops. Closes #193 Closes #194 Closes #195 Closes #196 Closes #197 | |||||
| 13 days | feat(pattern): canonical dict/JSON serialization on Pattern (to_dict, ↵ | natsuoto | 9 | -0/+792 | |
| from_dict, to_json, from_json, schema v0) | |||||
| 13 days | feat(atoms): public edify.atoms — 83 composable Pattern fragments (#276) | Bobby | 171 | -38/+3626 | |
| Adds `edify.atoms`, a public module of unanchored `Pattern` fragments that snap into any builder via `.use()`. ## Usage ```python from edify import Pattern from edify.atoms import hostname, port, protocol url_shape = ( Pattern() .start_of_input() .use(protocol).string("://").use(hostname).char(":").use(port) .end_of_input() ) url_shape("https://example.com:8080") # True ``` ## The 83 atoms | Group | Count | Names | |---|---|---| | Character | 7 | `letter`, `lower`, `upper`, `alnum`, `printable`, `ascii`, `space` | | Numeric | 12 | `natural`, `integer`, `signed`, `unsigned`, `decimal`, `floatnum`, `scientific`, `percent`, `ratio`, `hexnum`, `binnum`, `octnum` | | Network | 12 | `octet`, `port`, `hostname`, `label`, `ipv4`, `ipv6`, `cidr`, `mac`, `protocol`, `scheme`, `tld`, `nibble` | | Temporal | 11 | `clock`, `clock12`, `isodate`, `isodatetime`, `epoch`, `year`, `month`, `day`, `weekday`, `timezone`, `duration` | | Identifier | 7 | `uuid`, `guid`, `ulid`, `oid`, `objectid`, `semver`, `slug` | | Text | 9 | `word`, `line`, `quoted`, `parens`, `brackets`, `braces`, `username`, `localpart`, `hexcolor` | | Path | 3 | `filename`, `filepath`, `extension` | | Crypto | 7 | `hexstring`, `base64`, `base64url`, `base32`, `base58`, `sha256`, `md5` | | Web | 7 | `url`, `uri`, `email`, `rgbcolor`, `httpmethod`, `httpstatus`, `mimetype` | | Financial | 5 | `iban`, `bic`, `currency`, `money`, `creditcard` | | Boolean | 3 | `yesno`, `truefalse`, `boolean` | Every atom is a `Pattern` instance built via the fluent chain (no raw regex). Every atom has a corpus test at `tests/atoms/<name>.test.py` covering: accepts a shape-conforming sample, rejects an off-shape input, composes cleanly under `Pattern().use(...)`, emits a non-empty regex fragment. Every filename is single-word (no underscores). Every attribute name avoids Python builtins. `edify/library/_support/atoms.py` (the previous private home of `octet` / `hex_any`) is deleted; the three library validators that used it (`address/ip`, `address/ptr`, `address/socket`) now import from `edify.atoms`. Closes #170 Closes #171 Closes #172 Closes #173 Closes #174 | |||||
| 13 days | feat(atoms): expand from 13 to 83 general-purpose composable fragments | natsuoto | 141 | -0/+2991 | |
| 13 days | rename(atoms): single-word filenames ↵ | natsuoto | 25 | -112/+106 | |
| (octet/port/label/uuid/hexcolor/nibble/isodate/clock/localpart) | |||||
| 13 days | feat(atoms): public edify.atoms with 13 composable Pattern fragments | natsuoto | 31 | -40/+643 | |
| 2026-07-11 | feat!: callable-Pattern validator API + 223-pattern library across 22 ↵ | Bobby | 302 | -1599/+8100 | |
| category folders (#274) Ships the callable-`Pattern` validator API and expands the built-in library from 14 validators to **223**, organized into 22 category folders. ## Callable-`Pattern` contract Every built-in validator is now a `Pattern` instance whose `__call__` returns `bool`. The old function-style `validator(value)` call form still works; every `Pattern` method is now also available on each validator (`match`, `to_regex_string`, `.explain()`, `.visualize()`, etc.). ```python from edify import Pattern from edify.library import uuid, email isinstance(uuid, Pattern) # True uuid("550e8400-e29b-11d4-a716-446655440000") # True uuid.match("550e8400-e29b-11d4-a716-446655440000") # <re.Match ...> email("[email protected]", strict=True) # available on parameterized validators ``` Implementation: - `Pattern.__call__(value) -> bool` on the base class (non-string returns `False`, no crash on user input). - **Every validator is built via the fluent chain** — `Pattern().start_of_input().exactly(8).any_of()....end_of_input()`. No raw-regex escape hatch anywhere in the library. - Composable atoms in `edify/library/_support/atoms.py` (`hex_any`, `octet`, etc.) keep the chains readable across categories. ## The 223 patterns across 22 categories | Category | Count | Patterns | |---|---|---| | `identifier/` | 26 | uuid, guid, mac, ssn, ein, tin, itin, iban, bic, lei, isin, cusip, sedol, sku, asin, imei, meid, iccid, vin, imo, mmsi, iata, icao, arn, orcid, did | | `data/` | 14 | xml, json, yaml, toml, csv, tsv, ini, avro, protobuf, msgpack, html, hdf5, parquet, orc | | `address/` | 13 | ip, cidr, subnet, hostname, domain, subdomain, tld, url, uri, path, port, socket, ptr | | `software/` | 13 | semver, version, package, docker, image, digest, git, ref, checksum, component, makefile, cargo, bump | | `api/` | 12 | oauth, openid, saml, openapi, swagger, jsonapi, hal, graphql, soap, webhook, rss, atom | | `auth/` | 19 | password, pin, token, jwt, otp, apikey, bearer, session, csrf, mnemonic, hmac, passkey, mfa, webauthn, secret, sso, refresh, signing, challenge | | `temporal/` | 11 | date, time, datetime, duration, timezone, offset, epoch, timestamp, year, cron, interval | | `text/` | 11 | slug, base, script, ascii, printable, alphanumeric, alpha, numeric, word, unicode, emoji | | `media/` | 11 | mimetype, extension, filename, glob, regex, shebang, encoding, charset, locale, favicon, codec | | `document/` | 11 | svg, pdf, epub, mobi, docx, xlsx, pptx, odt, readme, rtf, tex | | `security/` | 11 | pgp, pem, der, csr, x509, certificate, ssh, age, keyring, nonce, signature | | `web/` | 11 | sitemap, captcha, robots, manifest, htaccess, humans, nginx, apache, cookie, csp, useragent | | `numeric/` | 10 | number, integer, hash, percentage, ratio, fraction, scientific, roman, ordinal, natural | | `geo/` | 8 | coordinate, geohash, plus, postal, place, altitude, mgrs, bearing | | `contact/` | 7 | email, phone, fax, pager, address, username, handle | | `financial/` | 7 | currency, card, wallet, crypto, routing, sortcode, vat | | `publishing/` | 6 | isbn, issn, doi, arxiv, pmid, pmc | | `grammar/` | 6 | bnf, ebnf, abnf, peg, pest, antlr | | `color/` | 5 | color, palette, swatch, gradient, filter | | `transport/` | 4 | vehicle, aircraft, flight, plate | | `medical/` | 4 | medical, blood, dosage, dicom | | `product/` | 3 | gtin, mpn, barcode | | **Total** | **223** | | Each pattern is importable both from its category submodule and from the flat top-level namespace: ```python from edify.library import uuid # flat from edify.library.identifier import uuid # category ``` ## Merged and renamed 0.3 validators Per the \"each pattern is unique / everything doable via a form must be merged\" rule: - `iso_date` → `date` (accepts every recognised date form) - `email_rfc_5322` → `email` (accepts basic or RFC 5322) - `ipv4` + `ipv6` → `ip` - `phone_number` → `phone` - `zip` → `postal` Documented in \`docs/upgrading/0.3-to-1.0.rst\` under the \`.. _validators-callable:\` anchor. Closes #176 Closes #177 Closes #178 Closes #179 Closes #180 Closes #181 Closes #182 Closes #183 Closes #184 Closes #185 Closes #186 Closes #187 Closes #188 Closes #189 Closes #190 | |||||
| 2026-07-11 | feat(library): add 23 patterns across 12 categories (medical, transport, ↵ | natsuoto | 36 | -10/+601 | |
| financial, geo, security, numeric, web, grammar, document, data, contact, software) | |||||
| 2026-07-11 | refactor: eliminate suppression comments and internal-import tests, delete ↵ | natsuoto | 24 | -503/+68 | |
| now-dead defensive code | |||||
| 2026-07-11 | refactor(library): rewrite geo/postal to inline patterns; delete ↵ | natsuoto | 44 | -719/+451 | |
| RegexBackedPattern escape hatch | |||||
| 2026-07-11 | refactor(library): rewrite software/ validators to fluent Pattern() chain | natsuoto | 12 | -57/+562 | |
| 2026-07-11 | refactor(library): rewrite temporal/, text/ validators to fluent Pattern() chain | natsuoto | 21 | -107/+514 | |
| 2026-07-11 | refactor(library): rewrite web/, security/, media/ validators to fluent ↵ | natsuoto | 27 | -62/+632 | |
| Pattern() chain | |||||
| 2026-07-11 | refactor(library): rewrite numeric/ validators to fluent Pattern() chain | natsuoto | 8 | -27/+201 | |
| 2026-07-11 | refactor(library): rewrite validators to fluent Pattern() chain | natsuoto | 77 | -267/+2365 | |
| 2026-07-11 | chore: ruff format + lint fixes (en-dash → hyphen, line-length, script.py ↵ | natsuoto | 186 | -317/+1308 | |
| noqa) | |||||
| 2026-07-11 | feat(library): reorganize into 22 category folders + flat 200-name re-export ↵ | natsuoto | 29 | -837/+126 | |
| at edify.library | |||||
| 2026-07-11 | feat(library): 22 patterns across security/grammar/web categories | natsuoto | 25 | -0/+140 | |
| 2026-07-11 | feat(library): 33 patterns across api/data/document categories | natsuoto | 36 | -0/+209 | |
| 2026-07-11 | feat(library): 12 patterns across transport/publishing/product/medical ↵ | natsuoto | 16 | -0/+85 | |
| categories | |||||
| 2026-07-11 | feat(library/software): 12 software patterns (semver, version, package, ↵ | natsuoto | 13 | -0/+102 | |
| docker, image, digest, git, ref, checksum, component, makefile, cargo) | |||||
| 2026-07-11 | feat(library/media): 11 media patterns (mimetype, extension, filename, glob, ↵ | natsuoto | 12 | -0/+84 | |
| regex, shebang, encoding, charset, locale, favicon, codec) | |||||
| 2026-07-11 | feat(library/color): 5 color patterns (color, palette, swatch, gradient, filter) | natsuoto | 6 | -0/+46 | |
| 2026-07-11 | feat(library/text): 11 text patterns (slug, base, script, ascii, printable, ↵ | natsuoto | 12 | -0/+137 | |
| alphanumeric, alpha, numeric, word, unicode, emoji) | |||||
| 2026-07-11 | feat(library/numeric): 9 numeric patterns (number, integer, hash, ↵ | natsuoto | 10 | -0/+109 | |
| percentage, ratio, fraction, scientific, roman, ordinal) | |||||
| 2026-07-11 | feat(library/geo): 6 geo patterns (coordinate, geohash, plus, postal, place, ↵ | natsuoto | 7 | -0/+80 | |
| altitude) | |||||
| 2026-07-11 | feat(library/temporal): 11 temporal patterns (date, time, datetime, ↵ | natsuoto | 12 | -0/+178 | |
| duration, timezone, offset, epoch, timestamp, year, cron, interval) | |||||
| 2026-07-11 | feat(library/financial): 4 financial patterns (currency, card, wallet, crypto) | natsuoto | 5 | -0/+65 | |
| 2026-07-11 | feat(library/auth): 19 auth patterns (password, pin, token, jwt, otp, ↵ | natsuoto | 20 | -0/+371 | |
| apikey, bearer, session, csrf, mnemonic, hmac, passkey, mfa, webauthn, secret, sso, refresh, signing, challenge) | |||||
| 2026-07-11 | feat(library/contact): 6 contact patterns (email, phone, fax, pager, ↵ | natsuoto | 7 | -0/+103 | |
| address, username) | |||||
| 2026-07-11 | feat(library/address): 13 address patterns (ip, cidr, subnet, hostname, ↵ | natsuoto | 14 | -0/+242 | |
| domain, subdomain, tld, url, uri, path, port, socket, ptr) | |||||
| 2026-07-11 | chore: drop as_pattern helper (obsoleted by Pattern()-only construction) | natsuoto | 1 | -20/+0 | |
| 2026-07-11 | feat(library/identifier): all 26 identifier patterns as callable Pattern chains | natsuoto | 28 | -118/+525 | |
| 2026-07-11 | feat(library): infrastructure and first three identifier migrations (uuid, ↵ | natsuoto | 6 | -0/+181 | |
| guid, mac) | |||||
| 2026-07-10 | feat: Pattern.__call__(value) -> bool and RegexBackedPattern base for ↵ | natsuoto | 2 | -0/+60 | |
| validator migration | |||||
| 2026-07-10 | chore!: relicense edify from Apache 2.0 to MIT (#273) | Bobby | 4 | -203/+39 | |
| Relicenses edify from Apache 2.0 to the MIT License across every artifact that names or ships it. Sole-copyright-holder relicense — no CLA sweep needed. ## Landings - **`LICENSE`** — the Apache 2.0 text is replaced with the standard MIT text, copyright line `Copyright (c) 2022-2026 Bobby` frozen as a static literal through the first MIT release. - **`pyproject.toml`** — the SPDX `license = "…"` field switches from `Apache-2.0` to `MIT`. `license-files = ["LICENSE"]` continues to attach the license file to the wheel and sdist via hatchling's Core Metadata 2.4 emission. - **`README.rst`** — the license section reads "MIT License" and continues to link the repo's `LICENSE`. The shields.io license badge reads its label from PyPI metadata, so no manual badge edit is needed. - **`tests/license.test.py`** — two acceptance tests that read the installed edify metadata via `importlib.metadata`: `License-Expression == "MIT"` and `Apache` does not appear in any classifier. Guards against silent backslide in a future dependency update. The `License :: OSI Approved :: Apache Software License` classifier has already been absent from `pyproject.toml`'s `classifiers` list since the toolchain migration; the second acceptance test above pins that state going forward. Closes #231 Closes #232 Closes #233 Closes #234 Closes #237 | |||||
| 2026-07-10 | test: license.test.py asserts installed edify carries MIT license expression ↵ | natsuoto | 1 | -0/+16 | |
| (#237) | |||||
| 2026-07-10 | docs: README license section reads MIT (#234) | natsuoto | 1 | -1/+1 | |
| 2026-07-10 | chore!: pyproject.toml license field = MIT SPDX (#232) | natsuoto | 1 | -1/+1 | |
| 2026-07-10 | chore!: replace LICENSE with MIT text (#231) | natsuoto | 1 | -201/+21 | |
