Skip to content

fix(stub): Stop pretending IntElement and FloatElement subclass int and float - #133

Open
andersk wants to merge 1 commit into
abelcheung:mainfrom
andersk:objectify-number-elements
Open

andersk wants to merge 1 commit into
abelcheung:mainfrom
andersk:objectify-number-elements

Conversation

@andersk

@andersk andersk commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

At runtime, IntElement and FloatElement are not subclasses of int and float: they implement numeric operators by applying them to the .pyval of both operands, and lack every non-dunder method and attribute of int and float. Inheriting from int and float in the stubs meant that isinstance(elem, int) narrowed incorrectly, that elements were accepted where int or float is required (json.dumps(), format(), round() all fail at runtime), and that names like .real or .is_integer were typed as int/float attributes instead of child element lookups. It also needed ignore comments for every type checker, since _Element and int/float are incompatible disjoint bases.

Declare the dunders explicitly instead, following typeshed's int and float, with number elements accepted as the other operand:

  • NumberElement: conversions, hashing, equality and truthiness, which all its subclasses share.
  • IntElement: __index__, arithmetic, bitwise and comparison operators, with int, float or complex results depending on the other operand.
  • FloatElement: the same minus the bitwise operators, which fail at runtime.
  • BoolElement: widen the &, | and ^ overloads to accept IntElement, and add their reflected versions (part of Reverse operator on BoolElement return incorrect result #13).

The 3-argument __pow__ overload comes first and
FloatElement.__rdivmod__ accepts IntElement, so that pow(IntElement, int, int) and divmod(IntElement, FloatElement) match typeshed's protocols.

…nd float

At runtime, IntElement and FloatElement are not subclasses of int and
float: they implement numeric operators by applying them to the .pyval
of both operands, and lack every non-dunder method and attribute of
int and float.  Inheriting from int and float in the stubs meant that
isinstance(elem, int) narrowed incorrectly, that elements were
accepted where int or float is required (json.dumps(), format(),
round() all fail at runtime), and that names like .real or .is_integer
were typed as int/float attributes instead of child element lookups.
It also needed ignore comments for every type checker, since _Element
and int/float are incompatible disjoint bases.

Declare the dunders explicitly instead, following typeshed's int and
float, with number elements accepted as the other operand:

- NumberElement: conversions, hashing, equality and truthiness, which
  all its subclasses share.
- IntElement: __index__, arithmetic, bitwise and comparison operators,
  with int, float or complex results depending on the other operand.
- FloatElement: the same minus the bitwise operators, which fail at
  runtime.
- BoolElement: widen the &, | and ^ overloads to accept IntElement, and
  add their reflected versions (part of abelcheung#13).

The 3-argument __pow__ overload comes first and
FloatElement.__rdivmod__ accepts IntElement, so that pow(IntElement,
int, int) and divmod(IntElement, FloatElement) match typeshed's
protocols.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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