feat: Union and tuple support for is_instance_of, and Union for is_instance_of_any - #33
Conversation
Merging this PR will improve performance by 14.63%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | test_clustering_a_whole_run |
618.7 µs | 539.7 µs | +14.63% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing dpinol:is_instance_union (d3c56c2) with main (22bfecb)
09b266d to
b68b277
Compare
|
Thanks for this one. It is a real bug, and a bigger one than the title claims. Here is assert_that(Person()).is_instance_of(Person | Car) # passes
assert_that(Person()).is_instance_of((Person, Car)) # passes
assert_that(Car()).is_instance_of(Person | str) # AssertionFailure: ... instance of class <Union>
assert_that(Car()).is_instance_of((Person, str)) # AttributeError: 'tuple' object has no attribute '__name__'The tuple form does not fail, it crashes, and an What makes it slightly embarrassing is that I fixed this once already and only on one half. Go through a matcher and the same case behaves: assert_that({"who": Car()}).matches_structure({"who": match.is_instance_of((Person, str))})
# AssertionFailure: ... <Person, str>, but was <Car object> of type <Car>That path uses The rest of your change I want: The Pyright does take it under About the wall of red CI: that is mine, not yours, and there was no way to find it from the repository. Three files under uv run python scripts/generate_poll_protocols.py
uv run python scripts/generate_check_protocols.py
ASSERTPY2_UPDATE_API=1 uv run pytest tests/test_api_compatibility.pyEither way, none of that being discoverable is a problem on my side, and writing it down has gone on my list. Two small things on the tests. Catch |
|
Pushed to
Your scope is unchanged: |
How do you run pyright? With 1.1.413 (only on npm) I get no errors
And on 1.1.411 it's even more "clever"
|
Do you really want |
|
You were right, I measured 1.1.411. Both my objections fall: Only union call sites fail on 1.1.411, and On isinstance(1.5, (float, (int, str))) # True
assert_that(x).is_instance_of_any(float, (int, str)) # AttributeError: 'tuple' object has no attribute '__name__'Same bug you reported, one level in.
It is recursive on Still yours:
|
b68b277 to
fd1855f
Compare
|
Amazing, I amended my commit. Please let me know if I missed something. |
|
Keep
Removing Known limitation, ours, not typeable: isinstance(1, int | list[int]) # True, `int` matched first
isinstance("x", int | list[int]) # TypeError
isinstance(1, list[int] | int) # TypeErrorYour side, dead since you moved to -def _class_name(some_class: type | UnionType) -> str:
- if isinstance(some_class, UnionType):
- return " | ".join(arg.__name__ for arg in some_class.__args__)
- if isinstance(some_class, tuple):
- return ", ".join(_class_name(arg) for arg in some_class)
- return some_class.__name__plus its Mine, pushed to your branch once that is gone: the facade ladder in |
ClassInfo used in is_instance_of to support whatever python's isintance supports TypeForm allows converting type[U]->Assertion[U] is_instance_of signature at _typing.py uses tuple[type | UnionType, ...] instead of ClassInfo to avoid ty error: Variable of type `tuple[<class 'bool'>, <class 'int'>]` is not allowed in a type expression _type_expression_name to also strip namespace from Union'ed types for consistency Added class tests for is_instance_of_any
fd1855f to
5d2277b
Compare
|
Cool, I removed the stale |
|
Pushed to your branch. Nested tuples.
Rung gone,
Three answers, measured on
No declaration reconciles those. Binding the union ahead of the concrete rungs costs Rest is bookkeeping. |
|
Hi, the last 2 commits are fine for me. |
Found it after all. assert_that(x).is_instance_of((PaidOrder, RefundedOrder)).value # PaidOrder | RefundedOrder
assert_that(x).is_instance_of_any(PaidOrder, RefundedOrder).value # sameOverloads per arity, up to three: a tuple of classes is classes, and
The cost is the domain: that rung also accepts |
|
Looks great, thanks |
ClassInfoused inis_instance_ofto support whatever python'sisintancesupportsTypeFormallows converting type hinttype[U]toAssertion[U]is_instance_ofsignature at _typing.py usestuple[type | UnionType, ...]instead ofClassInfoto avoid ty error: