Skip to content

Generate StrEnum and IntEnum for typed enumerations - #1242

Merged
tefra merged 1 commit into
mainfrom
typed-enums
Oct 17, 2026
Merged

tefra merged 1 commit into
mainfrom
typed-enums

Conversation

@tefra

@tefra tefra commented Oct 17, 2026

Copy link
Copy Markdown
Owner

Closes #1223

Generated enums now pick their base class from their members:

  • every member is a str → StrEnum
  • every member is an int → IntEnum
  • anything else (bool, Decimal, QName, tokens, mixed or union types) → Enum

There is no config option. Flag/IntFlag are left out: their bitwise semantics don't fit XSD enumerations.

Runtime fixes

Mixin enums list str/int before Enum in their mro, so the runtime treated their members as plain values:

  • ConverterFactory.type_converter resolves enum types before walking the mro. Before, "blue" parsed into a StrEnum field came back as a plain str with no validation.
  • Compound fields match enum values by exact type only. Before, a StrEnum value matched an earlier str choice, so netex wrote <ComparisonOperator> as <AttributeName>.
  • DictEncoder and EventGenerator.encode_primitive encode enums before primitives, so they output plain values instead of members.
  • PycodeSerializer renders Class.MEMBER from the qualname instead of str(member). That also fixes nested enums, which printed Inner.X while only Outer was imported.

Existing plain Enum models parse and serialize exactly as before.

Behaviour change for generated code

  • StrEnum members compare equal to their string value, str(member) and f-strings return the value, and a member and its raw string share a dict/set key.
  • IntEnum members compare, sort and do arithmetic with plain ints.

Regression suites

  • xsdata-samples: all 20 suites pass, no .xml output changes. The diff is 2589 StrEnum and 9 IntEnum base classes plus enum imports.
  • xsdata-w3c-tests: 14600 passed, 87 skipped (same skip list as main), no .xml output changes. The diff is 198 StrEnum and 98 IntEnum base classes plus enum imports.

The suite output branches (typed-enums) are ready locally.

Generated enums extend StrEnum when every member is a str, IntEnum
when every member is an int, and Enum otherwise.

Mixin enums list str/int before Enum in their mro, so the runtime
treated them as plain values:
- The converter resolves enum types before walking the mro
- Compound fields match enum values by their exact type only
- The dict and xml serializers encode enums before primitives
- The pycode serializer renders Class.MEMBER instead of str(member)

Closes #1223
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Oct 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.97%. Comparing base (0f7484c) to head (274d0d6).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1242   +/-   ##
=======================================
  Coverage   99.97%   99.97%           
=======================================
  Files         117      117           
  Lines        9412     9428   +16     
  Branches     1446     1451    +5     
=======================================
+ Hits         9410     9426   +16     
  Misses          1        1           
  Partials        1        1           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tefra
tefra merged commit 33b9558 into main Oct 17, 2026
14 checks passed
@tefra
tefra deleted the typed-enums branch October 17, 2026 09:26
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.

Giving the option to use StrEnum instead of Enum

1 participant