1
1
from __future__ import annotations
2
2
3
3
from mypy .nodes import CONTRAVARIANT , COVARIANT , INVARIANT
4
- from mypy .subtypes import is_subtype
4
+ from mypy .subtypes import is_proper_subtype , is_subtype , restrict_subtype_away
5
5
from mypy .test .helpers import Suite
6
6
from mypy .test .typefixture import InterfaceTypeFixture , TypeFixture
7
7
from mypy .types import Instance , TupleType , Type , UninhabitedType , UnpackType
@@ -277,6 +277,74 @@ def test_type_var_tuple_unpacked_variable_length_tuple(self) -> None:
277
277
def test_fallback_not_subtype_of_tuple (self ) -> None :
278
278
self .assert_not_subtype (self .fx .a , TupleType ([self .fx .b ], fallback = self .fx .a ))
279
279
280
+ def test_literal (self ) -> None :
281
+ str1 = self .fx .lit_str1
282
+ str2 = self .fx .lit_str2
283
+ str1_inst = self .fx .lit_str1_inst
284
+ str2_inst = self .fx .lit_str2_inst
285
+ str_type = self .fx .str_type
286
+
287
+ # other operand is the fallback type
288
+ # "x" ≲ str -> YES
289
+ # str ≲ "x" -> NO
290
+ # "x"? ≲ str -> YES
291
+ # str ≲ "x"? -> YES
292
+ self .assert_subtype (str1 , str_type )
293
+ self .assert_not_subtype (str_type , str1 )
294
+ self .assert_subtype (str1_inst , str_type )
295
+ self .assert_subtype (str_type , str1_inst )
296
+
297
+ # other operand is the same literal
298
+ # "x" ≲ "x" -> YES
299
+ # "x" ≲ "x"? -> YES
300
+ # "x"? ≲ "x" -> YES
301
+ # "x"? ≲ "x"? -> YES
302
+ self .assert_subtype (str1 , str1 )
303
+ self .assert_subtype (str1 , str1_inst )
304
+ self .assert_subtype (str1_inst , str1 )
305
+ self .assert_subtype (str1_inst , str1_inst )
306
+
307
+ # second operand is a different literal
308
+ # "x" ≲ "y" -> NO
309
+ # "x" ≲ "y"? -> YES
310
+ # "x"? ≲ "y" -> NO
311
+ # "x"? ≲ "y"? -> YES
312
+ self .assert_not_subtype (str1 , str2 )
313
+ self .assert_subtype (str1 , str2_inst )
314
+ self .assert_not_subtype (str1_inst , str2 )
315
+ self .assert_subtype (str1_inst , str2_inst )
316
+
317
+ # check proper subtyping
318
+ # second operand is the fallback type
319
+ # "x" <: str -> YES
320
+ # str <: "x" -> NO
321
+ # "x"? <: str -> YES
322
+ # str <: "x"? -> YES
323
+ self .assert_proper_subtype (str1 , str_type )
324
+ self .assert_not_proper_subtype (str_type , str1 )
325
+ self .assert_proper_subtype (str1_inst , str_type )
326
+ self .assert_proper_subtype (str_type , str1_inst )
327
+
328
+ # second operand is the same literal
329
+ # "x" <: "x" -> YES
330
+ # "x" <: "x"? -> YES
331
+ # "x"? <: "x" -> NO
332
+ # "x"? <: "x"? -> YES
333
+ self .assert_proper_subtype (str1 , str1 )
334
+ self .assert_proper_subtype (str1 , str1_inst )
335
+ self .assert_not_proper_subtype (str1_inst , str1 )
336
+ self .assert_proper_subtype (str1_inst , str1_inst )
337
+
338
+ # second operand is a different literal
339
+ # "x" ≲ "y" -> NO
340
+ # "x" ≲ "y"? -> NO
341
+ # "x"? ≲ "y" -> NO
342
+ # "x"? ≲ "y"? -> YES
343
+ self .assert_not_proper_subtype (str1 , str2 )
344
+ self .assert_not_proper_subtype (str1 , str2_inst )
345
+ self .assert_not_proper_subtype (str1_inst , str2 )
346
+ self .assert_proper_subtype (str1_inst , str2_inst )
347
+
280
348
# IDEA: Maybe add these test cases (they are tested pretty well in type
281
349
# checker tests already):
282
350
# * more interface subtyping test cases
@@ -287,6 +355,12 @@ def test_fallback_not_subtype_of_tuple(self) -> None:
287
355
# * any type
288
356
# * generic function types
289
357
358
+ def assert_proper_subtype (self , s : Type , t : Type ) -> None :
359
+ assert is_proper_subtype (s , t ), f"{ s } not proper subtype of { t } "
360
+
361
+ def assert_not_proper_subtype (self , s : Type , t : Type ) -> None :
362
+ assert not is_proper_subtype (s , t ), f"{ s } not proper subtype of { t } "
363
+
290
364
def assert_subtype (self , s : Type , t : Type ) -> None :
291
365
assert is_subtype (s , t ), f"{ s } not subtype of { t } "
292
366
@@ -304,3 +378,53 @@ def assert_equivalent(self, s: Type, t: Type) -> None:
304
378
def assert_unrelated (self , s : Type , t : Type ) -> None :
305
379
self .assert_not_subtype (s , t )
306
380
self .assert_not_subtype (t , s )
381
+
382
+
383
+ class RestrictionSuite (Suite ):
384
+ # Tests for type restrictions "A - B", i.e. ``T <: A and not T <: B``.
385
+
386
+ def setUp (self ) -> None :
387
+ self .fx = TypeFixture ()
388
+
389
+ def assert_restriction (self , s : Type , t : Type , expected : Type ) -> None :
390
+ actual = restrict_subtype_away (s , t )
391
+ msg = f"restrict_subtype_away({ s } , { t } ) == {{}} ({{}} expected)"
392
+ self .assertEqual (actual , expected , msg = msg .format (actual , expected ))
393
+
394
+ def test_literal (self ) -> None :
395
+ str1 = self .fx .lit_str1
396
+ str2 = self .fx .lit_str2
397
+ str1_inst = self .fx .lit_str1_inst
398
+ str2_inst = self .fx .lit_str2_inst
399
+ str_type = self .fx .str_type
400
+ uninhabited = self .fx .uninhabited
401
+
402
+ # other operand is the fallback type
403
+ # "x" - str -> Never
404
+ # str - "x" -> str
405
+ # "x"? - str -> Never
406
+ # str - "x"? -> Never
407
+ self .assert_restriction (str1 , str_type , uninhabited )
408
+ self .assert_restriction (str_type , str1 , str_type )
409
+ self .assert_restriction (str1_inst , str_type , uninhabited )
410
+ self .assert_restriction (str_type , str1_inst , uninhabited )
411
+
412
+ # other operand is the same literal
413
+ # "x" - "x" -> Never
414
+ # "x" - "x"? -> Never
415
+ # "x"? - "x" -> Never
416
+ # "x"? - "x"? -> Never
417
+ self .assert_restriction (str1 , str1 , uninhabited )
418
+ self .assert_restriction (str1 , str1_inst , uninhabited )
419
+ self .assert_restriction (str1_inst , str1 , uninhabited )
420
+ self .assert_restriction (str1_inst , str1_inst , uninhabited )
421
+
422
+ # other operand is a different literal
423
+ # "x" - "y" -> "x"
424
+ # "x" - "y"? -> Never
425
+ # "x"? - "y" -> "x"?
426
+ # "x"? - "y"? -> Never
427
+ self .assert_restriction (str1 , str2 , str1 )
428
+ self .assert_restriction (str1 , str2_inst , uninhabited )
429
+ self .assert_restriction (str1_inst , str2 , str1_inst )
430
+ self .assert_restriction (str1_inst , str2_inst , uninhabited )
0 commit comments