Skip to content

Commit c401a6d

Browse files
authored
[ty] Add failing tests for tuple subclasses (#19803)
1 parent 7b6abfb commit c401a6d

7 files changed

Lines changed: 636 additions & 1 deletion

File tree

crates/ty_python_semantic/resources/mdtest/call/function.md

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,101 @@ def _(args: tuple[int, str]) -> None:
164164
takes_at_least_two_positional_only(*args) # error: [invalid-argument-type]
165165
```
166166

167+
### Subclass of fixed-length tuple argument
168+
169+
```py
170+
def takes_zero() -> None: ...
171+
def takes_one(x: int) -> None: ...
172+
def takes_two(x: int, y: int) -> None: ...
173+
def takes_two_positional_only(x: int, y: int, /) -> None: ...
174+
def takes_two_different(x: int, y: str) -> None: ...
175+
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
176+
def takes_at_least_zero(*args) -> None: ...
177+
def takes_at_least_one(x: int, *args) -> None: ...
178+
def takes_at_least_two(x: int, y: int, *args) -> None: ...
179+
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...
180+
181+
# Test all of the above with a number of different splatted argument types
182+
183+
class SingleElementTuple(tuple[int]): ...
184+
185+
def _(args: SingleElementTuple) -> None:
186+
# TODO: we should emit `[too-many-positional-arguments]` here
187+
takes_zero(*args)
188+
189+
takes_one(*args)
190+
191+
# TODO: we should emit `[missing-argument]` on both of these
192+
takes_two(*args)
193+
takes_two_positional_only(*args)
194+
195+
# TODO: these should both be `[missing-argument]`, not `[invalid-argument-type]`
196+
takes_two_different(*args) # error: [invalid-argument-type]
197+
takes_two_different_positional_only(*args) # error: [invalid-argument-type]
198+
199+
takes_at_least_zero(*args)
200+
takes_at_least_one(*args)
201+
202+
# TODO: we should emit `[missing-argument]` on both of these
203+
takes_at_least_two(*args)
204+
takes_at_least_two_positional_only(*args)
205+
206+
class TwoElementIntTuple(tuple[int, int]): ...
207+
208+
def _(args: TwoElementIntTuple) -> None:
209+
# TODO: we should emit `[too-many-positional-arguments]` on both of these
210+
takes_zero(*args)
211+
takes_one(*args)
212+
213+
takes_two(*args)
214+
takes_two_positional_only(*args)
215+
takes_two_different(*args) # error: [invalid-argument-type]
216+
takes_two_different_positional_only(*args) # error: [invalid-argument-type]
217+
takes_at_least_zero(*args)
218+
takes_at_least_one(*args)
219+
takes_at_least_two(*args)
220+
takes_at_least_two_positional_only(*args)
221+
222+
class IntStrTuple(tuple[int, str]): ...
223+
224+
def _(args: IntStrTuple) -> None:
225+
# TODO: we should emit `[too-many-positional-arguments]` here
226+
takes_zero(*args)
227+
228+
# TODO: this should be `[too-many-positional-arguments]`, not `[invalid-argument-type]`
229+
takes_one(*args) # error: [invalid-argument-type]
230+
231+
# TODO: we should have one diagnostic for each of these, not two
232+
# error: [invalid-argument-type]
233+
# error: [invalid-argument-type]
234+
takes_two(*args)
235+
# error: [invalid-argument-type]
236+
# error: [invalid-argument-type]
237+
takes_two_positional_only(*args)
238+
239+
# TODO: these are all false positives
240+
# error: [invalid-argument-type]
241+
# error: [invalid-argument-type]
242+
takes_two_different(*args)
243+
# error: [invalid-argument-type]
244+
# error: [invalid-argument-type]
245+
takes_two_different_positional_only(*args)
246+
247+
takes_at_least_zero(*args)
248+
249+
# TODO: false positive
250+
# error: [invalid-argument-type]
251+
takes_at_least_one(*args)
252+
253+
# TODO: we should only emit one diagnostic for each of these, not two
254+
# error: [invalid-argument-type]
255+
# error: [invalid-argument-type]
256+
takes_at_least_two(*args)
257+
# error: [invalid-argument-type]
258+
# error: [invalid-argument-type]
259+
takes_at_least_two_positional_only(*args)
260+
```
261+
167262
### Mixed tuple argument
168263

169264
```toml
@@ -258,6 +353,197 @@ def _(args: tuple[int, *tuple[str, ...], int]) -> None:
258353
takes_at_least_two_positional_only(*args) # error: [invalid-argument-type]
259354
```
260355

356+
### Subclass of mixed tuple argument
357+
358+
```toml
359+
[environment]
360+
python-version = "3.11"
361+
```
362+
363+
```py
364+
def takes_zero() -> None: ...
365+
def takes_one(x: int) -> None: ...
366+
def takes_two(x: int, y: int) -> None: ...
367+
def takes_two_positional_only(x: int, y: int, /) -> None: ...
368+
def takes_two_different(x: int, y: str) -> None: ...
369+
def takes_two_different_positional_only(x: int, y: str, /) -> None: ...
370+
def takes_at_least_zero(*args) -> None: ...
371+
def takes_at_least_one(x: int, *args) -> None: ...
372+
def takes_at_least_two(x: int, y: int, *args) -> None: ...
373+
def takes_at_least_two_positional_only(x: int, y: int, /, *args) -> None: ...
374+
375+
# Test all of the above with a number of different splatted argument types
376+
377+
class IntStarInt(tuple[int, *tuple[int, ...]]): ...
378+
379+
def _(args: IntStarInt) -> None:
380+
# TODO: we should emit `[too-many-positional-arguments]` here
381+
takes_zero(*args)
382+
383+
takes_one(*args)
384+
takes_two(*args)
385+
takes_two_positional_only(*args)
386+
takes_two_different(*args) # error: [invalid-argument-type]
387+
takes_two_different_positional_only(*args) # error: [invalid-argument-type]
388+
takes_at_least_zero(*args)
389+
takes_at_least_one(*args)
390+
takes_at_least_two(*args)
391+
takes_at_least_two_positional_only(*args)
392+
393+
class IntStarStr(tuple[int, *tuple[str, ...]]): ...
394+
395+
def _(args: IntStarStr) -> None:
396+
# TODO: we should emit `[too-many-positional-arguments]` here
397+
takes_zero(*args)
398+
399+
# TODO: false positive
400+
# error: [invalid-argument-type]
401+
takes_one(*args)
402+
403+
# TODO: we should only emit one diagnostic for each of these, not two
404+
# error: [invalid-argument-type]
405+
# error: [invalid-argument-type]
406+
takes_two(*args)
407+
# error: [invalid-argument-type]
408+
# error: [invalid-argument-type]
409+
takes_two_positional_only(*args)
410+
411+
# TODO: false positives
412+
# error: [invalid-argument-type]
413+
# error: [invalid-argument-type]
414+
takes_two_different(*args)
415+
# error: [invalid-argument-type]
416+
# error: [invalid-argument-type]
417+
takes_two_different_positional_only(*args)
418+
419+
takes_at_least_zero(*args)
420+
421+
# TODO: false positive
422+
# error: [invalid-argument-type]
423+
takes_at_least_one(*args)
424+
425+
# TODO: we should only have one diagnostic for each of these, not two
426+
# error: [invalid-argument-type]
427+
# error: [invalid-argument-type]
428+
takes_at_least_two(*args)
429+
# error: [invalid-argument-type]
430+
# error: [invalid-argument-type]
431+
takes_at_least_two_positional_only(*args)
432+
433+
class IntIntStarInt(tuple[int, int, *tuple[int, ...]]): ...
434+
435+
def _(args: IntIntStarInt) -> None:
436+
# TODO: we should emit `[too-many-positional-arguments]` on both of these
437+
takes_zero(*args)
438+
takes_one(*args)
439+
440+
takes_two(*args)
441+
takes_two_positional_only(*args)
442+
takes_two_different(*args) # error: [invalid-argument-type]
443+
takes_two_different_positional_only(*args) # error: [invalid-argument-type]
444+
takes_at_least_zero(*args)
445+
takes_at_least_one(*args)
446+
takes_at_least_two(*args)
447+
takes_at_least_two_positional_only(*args)
448+
449+
class IntIntStarStr(tuple[int, int, *tuple[str, ...]]): ...
450+
451+
def _(args: IntIntStarStr) -> None:
452+
# TODO: we should emit `[too-many-positional-arguments]` here
453+
takes_zero(*args)
454+
455+
# TODO: this should be `[too-many-positional-arguments]`, not `invalid-argument-type`
456+
takes_one(*args) # error: [invalid-argument-type]
457+
458+
# TODO: these are all false positives
459+
# error: [invalid-argument-type]
460+
# error: [invalid-argument-type]
461+
takes_two(*args)
462+
# error: [invalid-argument-type]
463+
# error: [invalid-argument-type]
464+
takes_two_positional_only(*args)
465+
466+
# TODO: each of these should only have one diagnostic, not two
467+
# error: [invalid-argument-type]
468+
# error: [invalid-argument-type]
469+
takes_two_different(*args)
470+
# error: [invalid-argument-type]
471+
# error: [invalid-argument-type]
472+
takes_two_different_positional_only(*args)
473+
474+
takes_at_least_zero(*args)
475+
476+
# TODO: false positive
477+
# error: [invalid-argument-type]
478+
takes_at_least_one(*args)
479+
480+
# TODO: these are both false positives
481+
# error: [invalid-argument-type]
482+
# error: [invalid-argument-type]
483+
takes_at_least_two(*args)
484+
485+
# TODO: these are both false positives
486+
# error: [invalid-argument-type]
487+
# error: [invalid-argument-type]
488+
takes_at_least_two_positional_only(*args)
489+
490+
class IntStarIntInt(tuple[int, *tuple[int, ...], int]): ...
491+
492+
def _(args: IntStarIntInt) -> None:
493+
# TODO: we should emit `[too-many-positional-arguments]` on both of these
494+
takes_zero(*args)
495+
takes_one(*args)
496+
497+
takes_two(*args)
498+
takes_two_positional_only(*args)
499+
takes_two_different(*args) # error: [invalid-argument-type]
500+
takes_two_different_positional_only(*args) # error: [invalid-argument-type]
501+
takes_at_least_zero(*args)
502+
takes_at_least_one(*args)
503+
takes_at_least_two(*args)
504+
takes_at_least_two_positional_only(*args)
505+
506+
class IntStarStrInt(tuple[int, *tuple[str, ...], int]): ...
507+
508+
def _(args: IntStarStrInt) -> None:
509+
# TODO: we should emit `too-many-positional-arguments` here
510+
takes_zero(*args)
511+
512+
# TODO: this should be `too-many-positional-arguments`, not `invalid-argument-type`
513+
takes_one(*args) # error: [invalid-argument-type]
514+
515+
# TODO: we should only emit one diagnostic for each of these
516+
# error: [invalid-argument-type]
517+
# error: [invalid-argument-type]
518+
takes_two(*args)
519+
# error: [invalid-argument-type]
520+
# error: [invalid-argument-type]
521+
takes_two_positional_only(*args)
522+
523+
# TODO: we should not emit diagnostics for these
524+
# error: [invalid-argument-type]
525+
# error: [invalid-argument-type]
526+
takes_two_different(*args)
527+
# error: [invalid-argument-type]
528+
# error: [invalid-argument-type]
529+
takes_two_different_positional_only(*args)
530+
531+
takes_at_least_zero(*args)
532+
533+
# TODO: false positive
534+
takes_at_least_one(*args) # error: [invalid-argument-type]
535+
536+
# TODO: should only have one diagnostic here
537+
# error: [invalid-argument-type]
538+
# error: [invalid-argument-type]
539+
takes_at_least_two(*args)
540+
541+
# TODO: should only have one diagnostic here
542+
# error: [invalid-argument-type]
543+
# error: [invalid-argument-type]
544+
takes_at_least_two_positional_only(*args)
545+
```
546+
261547
### String argument
262548

263549
```py

crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,33 @@ class C(tuple[T, U]): ...
392392
reveal_type(C((1, 2))) # revealed: C[int, int]
393393
```
394394

395+
### Upcasting a `tuple` to its `Sequence` supertype
396+
397+
This test is taken from the
398+
[typing spec conformance suite](https://github.com/python/typing/blob/c141cdfb9d7085c1aafa76726c8ce08362837e8b/conformance/tests/tuples_type_compat.py#L133-L153)
399+
400+
```toml
401+
[environment]
402+
python-version = "3.11"
403+
```
404+
405+
```py
406+
from typing import TypeVar, Sequence, Never
407+
408+
T = TypeVar("T")
409+
410+
def test_seq(x: Sequence[T]) -> Sequence[T]:
411+
return x
412+
413+
def func8(t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: tuple[()]):
414+
# TODO: should be `Sequence[int | float | complex | list[int]]`
415+
reveal_type(test_seq(t1)) # revealed: Sequence[Unknown]
416+
# TODO: should be `Sequence[int | str]`
417+
reveal_type(test_seq(t2)) # revealed: Sequence[Unknown]
418+
# TODO: this should be `Sequence[Never]`
419+
reveal_type(test_seq(t3)) # revealed: Sequence[Unknown]
420+
```
421+
395422
### `__init__` is itself generic
396423

397424
```py

crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,26 @@ class C[T, U](tuple[T, U]): ...
355355
reveal_type(C((1, 2))) # revealed: C[int, int]
356356
```
357357

358+
### Upcasting a `tuple` to its `Sequence` supertype
359+
360+
This test is taken from the
361+
[typing spec conformance suite](https://github.com/python/typing/blob/c141cdfb9d7085c1aafa76726c8ce08362837e8b/conformance/tests/tuples_type_compat.py#L133-L153)
362+
363+
```py
364+
from typing import Sequence, Never
365+
366+
def test_seq[T](x: Sequence[T]) -> Sequence[T]:
367+
return x
368+
369+
def func8(t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: tuple[()]):
370+
# TODO: should be `Sequence[int | float | complex | list[int]]`
371+
reveal_type(test_seq(t1)) # revealed: Sequence[Unknown]
372+
# TODO: should be `Sequence[int | str]`
373+
reveal_type(test_seq(t2)) # revealed: Sequence[Unknown]
374+
# TODO: this should be `Sequence[Never]`
375+
reveal_type(test_seq(t3)) # revealed: Sequence[Unknown]
376+
```
377+
358378
### `__init__` is itself generic
359379

360380
```py

0 commit comments

Comments
 (0)