88# v. 2.0. If a copy of the MPL was not distributed with this file, You can
99# obtain one at https://mozilla.org/MPL/2.0/.
1010
11+ from collections .abc import Callable
12+ from typing import get_args , get_origin
13+
1114import pytest
1215
1316from hypothesis import strategies as st
17+ from hypothesis .errors import HypothesisException
18+ from hypothesis .strategies ._internal .types import evaluate_type_alias_type
1419
1520from tests .common .debug import assert_simple_property , find_any
1621from tests .common .utils import temp_registered
@@ -39,6 +44,13 @@ def test_resolves_nested():
3944 assert_simple_property (st .from_type (Point3 ), lambda x : isinstance (x , int ))
4045
4146
47+ def test_resolves_parametrized ():
48+ type MyList = list [int ]
49+ assert_simple_property (
50+ st .from_type (MyList ), lambda l : all (isinstance (x , int ) for x in l )
51+ )
52+
53+
4254def test_mutually_recursive_fails ():
4355 # example from
4456 # https://docs.python.org/3/library/typing.html#typing.TypeAliasType.__value__
@@ -51,6 +63,15 @@ def test_mutually_recursive_fails():
5163 find_any (st .from_type (A ))
5264
5365
66+ def test_mutually_recursive_fails_parametrized ():
67+ # same with parametrized types
68+ type A [T ] = B [T ]
69+ type B [T ] = A [T ]
70+
71+ with pytest .raises (RecursionError ):
72+ find_any (st .from_type (A [int ]))
73+
74+
5475def test_can_register_typealias ():
5576 type A = int
5677 st .register_type_strategy (A , st .just ("a" ))
@@ -65,3 +86,94 @@ def test_prefers_manually_registered_typealias():
6586
6687 with temp_registered (A , st .booleans ()):
6788 assert_simple_property (st .from_type (A ), lambda x : isinstance (x , bool ))
89+
90+
91+ def test_resolves_parameterized_typealias ():
92+ type A [T ] = list [T ]
93+
94+ assert_simple_property (st .from_type (A [int ]), lambda x : isinstance (x , list ))
95+ find_any (st .from_type (A [int ]), lambda x : len (x ) > 0 )
96+ assert_simple_property (
97+ st .from_type (A [int ]), lambda x : all (isinstance (i , int ) for i in x )
98+ )
99+
100+
101+ def test_resolves_nested_parameterized_typealias ():
102+ type Inner [T ] = list [T ]
103+ type Outer [T ] = Inner [T ]
104+
105+ assert_simple_property (st .from_type (Outer [str ]), lambda x : isinstance (x , list ))
106+ assert_simple_property (
107+ st .from_type (Outer [str ]), lambda x : all (isinstance (i , str ) for i in x )
108+ )
109+
110+
111+ def test_resolves_parameterized_typealias_with_literal_types ():
112+ # Type param used in non-first position with literal types
113+ type MyDict [T ] = dict [str , T ]
114+
115+ assert_simple_property (st .from_type (MyDict [int ]), lambda x : isinstance (x , dict ))
116+ assert_simple_property (
117+ st .from_type (MyDict [int ]),
118+ lambda x : all (isinstance (k , str ) and isinstance (v , int ) for k , v in x .items ()),
119+ )
120+
121+
122+ def test_can_register_parameterized_typealias_with_unused_params ():
123+ # Users can explicitly register strategies for such types using a resolver function
124+ type MyList [T1 , T2 ] = list [T1 ]
125+
126+ # Register a function that resolves the type alias
127+ def resolve_mylist (thing ):
128+ if get_origin (thing ) is MyList :
129+ args = get_args (thing )
130+ # Use the first type argument, ignore the second
131+ return st .lists (st .from_type (args [0 ]))
132+ return NotImplemented
133+
134+ st .register_type_strategy (MyList , resolve_mylist )
135+
136+ assert_simple_property (
137+ st .from_type (MyList [int , float ]), lambda x : isinstance (x , list )
138+ )
139+ assert_simple_property (
140+ st .from_type (MyList [int , float ]), lambda x : all (isinstance (i , int ) for i in x )
141+ )
142+
143+
144+ def test_typealias_evaluation ():
145+ type A [T1 , T2 ] = list [T1 ]
146+ assert evaluate_type_alias_type (A [int , float ]) == list [int ]
147+
148+ type A [T1 , T2 ] = list [T2 ]
149+ assert evaluate_type_alias_type (A [float , int ]) == list [int ]
150+
151+ type A [K , V ] = dict [V , K ]
152+ assert evaluate_type_alias_type (A [str , int ]) == dict [int , str ]
153+
154+ type A [T ] = list [list [T ]]
155+ assert evaluate_type_alias_type (A [int ]) == list [list [int ]]
156+
157+ type Inner [T ] = list [T ]
158+ type Outer [T ] = Inner [T ]
159+ assert evaluate_type_alias_type (Outer [int ]) == list [int ]
160+
161+ type Bare [T ] = list
162+ assert evaluate_type_alias_type (Bare [int ]) == list
163+
164+ type A [T1 , T2 ] = list [T1 ]
165+ assert evaluate_type_alias_type (A [int ]) == list [int ]
166+
167+ # tries to reference free variable
168+ type A [T1 , T2 ] = list [T2 ]
169+ with pytest .raises (ValueError ):
170+ evaluate_type_alias_type (A [int ])
171+
172+ # (currently) unsupported type forms
173+ type A [* Ts ] = tuple [* Ts ]
174+ with pytest .raises (HypothesisException , match = "Hypothesis does not yet support" ):
175+ assert evaluate_type_alias_type (A [int , str , float ]) == tuple [int , str , float ]
176+
177+ type A [** P ] = Callable [P , int ]
178+ with pytest .raises (HypothesisException , match = "Hypothesis does not yet support" ):
179+ assert evaluate_type_alias_type (A [[str , float ]]) == Callable [[str , float ], int ]
0 commit comments