Skip to content

fix: Specify Python str semantics for character classification functions - #177

Open
mwiebe wants to merge 2 commits into
OpenJobDescription:mainlinefrom
mwiebe:feature/string-classification-unicode
Open

fix: Specify Python str semantics for character classification functions#177
mwiebe wants to merge 2 commits into
OpenJobDescription:mainlinefrom
mwiebe:feature/string-classification-unicode

Conversation

@mwiebe

@mwiebe mwiebe commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

fix: Specify Python str semantics for character classification functions

Description of the change. What is being added or fixed?

RFC 0006 and the 2026-02 Expression Language spec describe isdigit,
isalpha, isalnum, isspace, isupper, and islower with one-line
summaries ("True if all characters are digits...") that don't say which
character-class convention applies. Every language draws different lines
through Unicode — Rust's Alphabetic property is a superset of Python's
L* categories, Rust has no notion of Numeric_Type, and so on — so
implementations diverged: openjd-rs used Rust's char predicates, which
made isdigit ASCII-only while isalnum was Unicode-wide, and the two
contradicted each other for characters like '٣' (U+0663 ARABIC-INDIC
DIGIT THREE). Reported as
openjd-rs#309.

This PR:

  • Clarifies in both RFC 0006 and the wiki spec that these six functions have
    exactly the semantics of the Python str methods of the same name over
    the Unicode Character Database, with the specific Unicode properties named
    per function.
  • Documents two subtleties: isalnum is strictly broader than
    isalpha OR isdigit (Numeric_Type=Numeric characters like ½), and the
    cased-character rule for isupper/islower (uncased characters are
    ignored; titlecase characters are cased but neither upper nor lower).
  • Permits Unicode-version differences for newly assigned code points, so
    implementations aren't broken by UCD updates.
  • Adds a conformance test (expr2.2.4--string-classification-unicode) with
    21 assertions on characters where the conventions differ, using \uXXXX
    escapes so the file stays ASCII. The test fails against openjd-rs main
    and passes against the fix in the companion PR.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

The isdigit/isalpha/isalnum/isspace/isupper/islower descriptions in RFC
0006 and the 2026-02 Expression Language spec did not say which
character-class convention applies, and implementations diverged:
openjd-rs used Rust char predicates, making isdigit ASCII-only while
isalnum was Unicode-wide, so isdigit('\u0663') and isalnum('\u0663')
disagreed (openjd-rs issue #309).

Clarify that these functions have exactly the semantics of the Python
str methods of the same name over the Unicode Character Database,
document the cased-character rule for isupper/islower and the
isalnum-vs-isalpha/isdigit relationship, and add a conformance test
covering Unicode inputs where the conventions differ.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
@mwiebe
mwiebe requested a review from a team as a code owner August 21, 2026 23:37
Follow-up to the character classification clarification: the title and
capitalize descriptions had the same ambiguity, and implementations
diverged the same way (word boundaries by alphanumeric-ness instead of
cased-ness, uppercase instead of titlecase mappings, no Final_Sigma
context rule).

Clarify both function rows and the section note in RFC 0006 and the
2026-02 Expression Language spec, and extend the Unicode conformance
test with title/capitalize cases covering digit word boundaries,
titlecase digraph mappings, sharp-s expansion, and final sigma.

Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant