Changelog
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Planned
Inline
{...:validator(...)}syntax and async validation pipelines (currently deferred in API documentation).composed_typeextensions: pattern+, inheritance, and flattening nested parse results into the parent (see#7 <https://github.com/eddiethedean/formatparse/issues/7>_).
0.8.2 - 2026-05-22
Added
tytype checking:_formatparse.pyistubs, stricter typing informatparse/, and[tool.ty]configuration (tests use targeted overrides).findallandfindall_iter(including :meth:FormatParser.findall_iter) accept optionalmax_matchesto cap how many non-overlapping matches are returned on untrusted input.
Changed
Dependencies: PyO3 0.28 (with API migration),
fancy-regex0.17,lru0.18,rayon1.12; Sphinx / myst-parser / RTD theme bumps for docs; GitHub Actions artifact actions v7/v8.MSRV: Rust 1.83 (required by PyO3 0.28).
Pattern LRU cache emits a :mod:
warningsmessage when a hash hit fails full verification (e.g. in-place mutation ofconverter.patternon a reusedextra_typesdict).
Fixed
:class:
BidirectionalPatternnow passesextra_typesto :func:compileso custom-type regexes are strict at compile time, not only on :meth:~BidirectionalPattern.parse.:meth:
FormatParser.parseand :meth:FormatParser.searchre-resolve the compiled parser through the pattern cache when call-timeextra_typesdiffer from compile-time fingerprints, so custom converters use their@with_patternregex instead of the\S+fallback (and no longer raiseValueErroron non-matching text that the loose regex would accept).:class:
BidirectionalPatternvalidation constraints are derived from compiled field metadata (field_constraints), fixing incorrect constraint extraction for nested brace specs and custom type names.
0.8.1 - 2026-05-15
Changed
Python package layout: split the monolithic
formatparsemodule into focused submodules (api,validation,bidirectional, etc.) with unchangedfrom formatparse import …surface.Rust internals: phased SOLID refactor—
CompiledFieldsonFormatParser, splittypes/regexandparser/matchingmodules, pattern compilation informatparse-core, unified builtin conversion viabuiltin_convert, andformat_parser/findall_itermodule splits. No intended public API or match-semantics changes.
Maintenance
Quality:
ruff,mypy(check target 3.9+),cargo clippy -D warnings, andrustfmtclean across the workspace.
0.8.0 - 2026-05-15
Added
Multiline fields (
:ml) for captures that may span newlines, with greedy/non-greedy boundaries like plain string fields (#8). Width, precision, alignment, and fill are supported for:mlthe same way as for string fields (#70, #75).Indent-block fields (
:blk): same boundary rules as:ml, then dedent by removing the largest common prefix of spaces and tabs on each line (blank lines do not set the margin; tabs count as single characters) (#69).Pattern line continuations: a backslash immediately before end-of-line continues the format pattern on the next line (
\r\nor\n); doubled backslashes keep a literal newline; leading spaces and tabs on the continued line are stripped (#68).findall_iterfor incremental iteration overfindall-style matches (#13).parse_batchto parse many strings with one compiled parser (#14).:bracefield type for capturing a literal{...}payload in the input (#15).Datetime strftime: merge adjacent strftime fragments that share a field name into a single datetime conversion (#4).
Composition: embed a compiled
FormatParseras a custom type so a field is parsed by a child parser and returns a nestedParseResult, viacomposed_type/extra_types(#7).Post-parse validators:
ValidationError,MultipleValidationErrors,apply_validators,validatordecorator,parse/compile/ValidatedParserwithvalidators=, and built-insin_range/non_empty_str(#10).Validation pipeline (
ValidationPipeline): ordered per-field validators,validation_modestrict/collect/lenient,parse(..., pipeline=...), whole-result hooks for cross-field checks,parse_with_validation, and built-insis_valid_email/is_valid_url(heuristic checks, not full RFC compliance or security audits) (#11, #74–#78).Regex lookaround assertions after the type token for integer (
:dand related) and float (:f/:F) fields: append(?=…),(?!…),(?<=…), or(?<!…)groups so the numeric capture stays zero-width outside the field span (issues#9 <https://github.com/eddiethedean/formatparse/issues/9>, upstreamparse#209 <https://github.com/r1chardj0n3s/parse/issues/209>). Compiled format patterns use thefancy-regexengine; simple literal positive lookarounds may be rewritten as non-capturing groups for correct anchoring. Lookarounds are rejected inside strftime%…type tails for this release.Nested format patterns in a field’s format specification (e.g.
{outer:{inner:d}}): the inner{…}is compiled as its own pattern, the outer capture is parsed again, and nested values appear asParseResultobjects undernamed(issue#12 <https://github.com/eddiethedean/formatparse/issues/12>; upstreamparse#206 <https://github.com/r1chardj0n3s/parse/issues/206>). Brace-balanced scanning applies in the spec after:; nesting depth is capped at 10;findallandFindallIteruse the Python match path when nested fields are present (same rule as nested dict field names).Input line continuations for
:mland:blkcaptures: a backslash before end-of-line in the matched text joins lines (same odd/even backslash rules as pattern continuations in #68); leading spaces and tabs on the continued line are stripped (#80).
Fixed
Empty string input can match patterns whose fields use the default string conversion (
#) (#16).Post-parse validation:
_validator_field_valueno longer returnedNonefor validfixedindices (incorrectreturnindentation under the bounds check).ValidationPipelinecollectmode: all hooks still run when field validators fail; field and hook failures are merged into a singleMultipleValidationErrors(field errors first in validator key order, then hook errors in registration order) (#11).Float
f/Fwith.0precision (e.g.{:02.0f}): accept integer-shaped captures such as20, matchingstr.formatoutput for those specs (#84; upstreamparse#159 <https://github.com/r1chardj0n3s/parse/issues/159>_).CI: Skip pympler-based memory tests on PyPy (
SummaryTracker/asizeofraisesKeyErroron PyPy 3.11).CI / ReDoS guard: Raise the post-regex-compile wall-clock cap from 200ms to 500ms so slow shared runners (e.g. macOS Intel) do not spuriously reject valid patterns; documentation updated accordingly.
Pattern LRU cache: after a hash hit, the entry is checked against the full normalized pattern and
extra_typesfingerprint so a colliding key cannot return the wrong compiled parser.Fixed-width string fields with literals beside the field (e.g.
"{s:<5.5} "vs"abc "): when width and precision are equal, the compiled fragment is exactlypreccharacters so trailing (or leading) pattern space is not pulled into the capture (#97). Right-aligned fields with that same equal width and precision also accept an opaque fixed-width capture (including trailing spaces) so post-capture validation matches parse for those cells (#97).String width/precision with literal newline after the field (e.g.
" {s:<4.4}\n"vs" \n"): bounded string fragments no longer use DOTALL.for content, so a trailing\nin the input is not consumed as part of the field (#95). The same bounded runs also exclude VT, FF, NEL (U+0085), U+2028 LINE SEPARATOR, and U+2029 PARAGRAPH SEPARATOR so those line boundaries cannot be absorbed under(?s)when they appear as literals after the field.Integer and radix fields with both width and precision (e.g.
"{:2.2d}{:2.2d}","#{:2.2x}{:2.2x}"): digit runs use inclusive min/max bounds (parse semantics: width = minimum digits, precision = maximum) so adjacent fields no longer steal digits with a greedy+(#82; upstreamparse#107 <https://github.com/r1chardj0n3s/parse/issues/107>_). Patterns that previously matched too loosely on short inputs may now return no match.String fields with alignment + precision before a fixed-width integer (e.g.
"{n:>10.10}{x:02d}"): leading fill in the regex is non-greedy so the slice for the width/precision content is left for the following digit field (#88; relatedparse#218 <https://github.com/r1chardj0n3s/parse/issues/218>_).Default string fields next to literals (e.g.
"/{name}"): an empty capture is allowed when it matchesstr.formatoutput such as"/"forname=""(#83; upstreamparse#136 <https://github.com/r1chardj0n3s/parse/issues/136>_). Applies to full-string parse / compile().parse; search / findall still use.+?for those segments so unanchored matching does not stop early.Integer
d: leading spaces and tabs before decimal digits are accepted (e.g.parse("{a:d}", " 0")) for parity with paddedstr.formatoutput (#81; upstreamparse#133 <https://github.com/r1chardj0n3s/parse/issues/133>_).
Changed
compile()uses the same pattern LRU cache asparse/search/findallwhen cache keys match (#29).PyO3:
extension-moduleis an explicit Cargo feature enabled by Maturin for wheels andmaturin develop; plaincargo test/cargo clippyonformatparse-pyo3link libPython without extra linker configuration.
Documentation
Expanded the custom types /
extra_typesuser guide (#17).
Maintenance
CI, Makefile, and publish workflow: pass
--features extension-modulewhen Maturin is run fromformatparse-pyo3/so builds match rootpip install/maturin develop --manifest-path(Maturin does not read the repo-rootpyprojectfrom that working directory).Pytest: skip
tests/test_indent_block.pywhen the installed_formatparselacks:blk/ multiline validation (skip message points tomaturin develop/ editable install).Rust: migrate off deprecated
ToPyObject::to_object(#45); remove several crate-level Clippy allows (manual_strip,dead_code,wrong_self_convention,if_same_then_else,too_many_arguments,type_complexity) (#44, #46–#50).CI: Codecov Action
filesinput (replaces deprecatedfile); Dependabot bumps for Rust crates and GitHub Actions.Formatting: Ruff-format touched Python tests and package sources.
CI: Standalone Rust · formatparse-core (Linux) job (separate row in Actions) plus matrix Summary table; matrix splits Rust compile / Rust test / Python build (
maturin build+pip installwheel, no pytest until aftercargo test) / Python test (pytest).CI: Ubuntu PyPy 3.11 job;
python-testsforformatparse-pyo3is a blocking step on Ubuntu CPython 3.11 (nocontinue-on-error).Rust: clearer
expectmessages for UTF-8 invariants in line-continuation helpers andResultsIterator.
0.7.0 - 2026-05-14
PyPI publish (post-tag workflow fix)
Publish workflow: build sdist from repo root with
--manifest-path(fixes “pyproject.toml not found” when run fromformatparse-pyo3/only).Remove
[tool.maturin] python-source = "."somaturin sdistdoes not require a repo-root_formatparsePython package (that check failed for this PyO3 cdylib layout on 1.12.x and 1.13.x). Publish and[build-system].requiresusematurin>=1.12.6,<2.0.Linux wheel for CPython 3.8: use
manylinux2014+--manylinux 2014whenmanylinux_2_28no longer ships that interpreter (avoids exit 127); pinmanylinux2014_x86_64:2026.05.01-2becauselatestremoved/opt/python/cp38-cp38after manylinux#1882.
Added
formatparse_core::count_capturing_groupsfor validating customwith_patternregexes, including correct handling of(?P<name>...)named capture groups.Richer
ParseResult.__repr__for REPL debugging.
Fixed
Pattern cache keys for
extra_typesnow incorporate converterpatternandregex_group_countso cached parsers are not incorrectly reused.Resultsnegative indexing aligned with Python list semantics.Zero-fill and right-align
width.precisionparsing parity with the referenceparselibrary (#40).Trailing fill preserved for left-aligned string fields with width (#39).
formatparse.__version__stable when installed from sdist/wheel (PEP 440) and fallback fromCargo.tomlin source checkouts (#38).FormatParserpickling behavior documented and clarified forextra_types.
Changed
findallreturn type documentation: fast path returnsResults; withextra_types,evaluate_result=False, or nested dict fields, a plainlistis returned (matches runtime).Security documentation: clarify post-compile timing check vs match-time behavior; document uncapped
findallmatch count within input limits.
Maintenance
CI: load
pytest-covwhenPYTEST_DISABLE_PLUGIN_AUTOLOAD=1; bumpcargo-auditin the main CI Ubuntu step for advisory DB compatibility.Dependency updates (e.g.
lrufor RustSec advisories), formatting, and Clippy cleanups.