Skip to content

upstream(tinydb): matches() is documented as whole-value but anchors only at the start #144

Open

Description

@cdwilson

Tracks upstream friction in TinyDB. The explanation lives in the docs; this issue is the unit of work.

Explanation: Query.matches() is documented as whole-value and anchors only at the start — the limitation, why it matters to an ODM, the workaround tinydantic carries, and the change that would remove it. That section is written to be read standalone by an upstream maintainer, so it doubles as the ready-to-send proposal if we ever file this.

Reproduction

Query.matches() documents itself as "Run a regex test against a dict value (whole string has to match)" and implements the test with re.match, which anchors at the start only.

from tinydb import TinyDB
from tinydb.storages import MemoryStorage

from tinydantic import TinydanticModel

db = TinyDB(storage=MemoryStorage)


class User(TinydanticModel, database=db):
    email: str


User(email="[email protected]").insert()
print(User.search(User.email.matches(r".*@example\.com")))

Run with python repro.py.

Actual output:

[User(id=1, email='[email protected]')]

Expected from the documented "whole string has to match" contract: []. re.match succeeds on any prefix, so the trailing .evil is simply not looked at. re.fullmatch is the function the docstring describes.

Environment: TinyDB 4.9.0, tinydantic 0.4.0 at 719ca78, Python 3.10.20, pydantic 2.13.4, macOS. Run 2026-08-13.

What tinydantic carries today

No code workaround — the behavior is fine, the documentation is not, and changing the behavior upstream would break every existing prefix-matching caller. tinydantic's own GuardedQuery.matches() now documents the anchoring accurately and points at $, and docs/usage/queries.md does the same. Before this, tinydantic had copied the upstream wording verbatim and told users matches() tested the whole value.

The security shape is worth stating plainly: a reader who trusts the docstring writes email.matches(r".*@example\.com") as an allow-list and gets a filter that accepts [email protected].

Upstream status

Not filed. No draft prepared. Do not file without maintainer approval.

What would close this

A docstring correction upstream — "the pattern is matched against the start of the value (re.match); end it with $ to require a whole-value match" — plus, optionally, a fullmatch=/Query.fullmatch() spelling for the behavior the current wording promises. A behavior change to re.fullmatch is not being proposed: it would silently narrow every existing caller's results.

What was not done

The reproduction was run and its output pasted above. No fix was built or tested, nothing was filed upstream, and no upstream maintainer has been contacted.

Originally finding Q2 / D-QRY-3 of the 2026-08-13 docs and docstring audit.

Assisted-by: Claude:claude-opus-5

Activity

  1. added
    documentationImprovements or additions to documentation
    upstreamTracks a defect or request in an upstream dependency
    ai-assistedCreated or investigated with help from an AI agent
    on Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-assistedCreated or investigated with help from an AI agentdocumentationImprovements or additions to documentationupstreamTracks a defect or request in an upstream dependency

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions