Instantiate type variables during analyze_class_attribute_access, e.g T and Q in the following: class A(Generic[T]): @classmethod def foo(cls: Type[Q]) -> Tuple[T, Q]: ... class B(A[str]): pass B.foo() Args: t: Declared type of the method (or property)
(
t: ProperType,
isuper: Instance | None,
is_classmethod: bool,
mx: MemberContext,
original_vars: Sequence[TypeVarLikeType] | None = None,
is_trivial_self: bool = False,
)
| 1414 | |
| 1415 | |
| 1416 | def add_class_tvars( |
| 1417 | t: ProperType, |
| 1418 | isuper: Instance | None, |
| 1419 | is_classmethod: bool, |
| 1420 | mx: MemberContext, |
| 1421 | original_vars: Sequence[TypeVarLikeType] | None = None, |
| 1422 | is_trivial_self: bool = False, |
| 1423 | ) -> Type: |
| 1424 | """Instantiate type variables during analyze_class_attribute_access, |
| 1425 | e.g T and Q in the following: |
| 1426 | |
| 1427 | class A(Generic[T]): |
| 1428 | @classmethod |
| 1429 | def foo(cls: Type[Q]) -> Tuple[T, Q]: ... |
| 1430 | |
| 1431 | class B(A[str]): pass |
| 1432 | B.foo() |
| 1433 | |
| 1434 | Args: |
| 1435 | t: Declared type of the method (or property) |
| 1436 | isuper: Current instance mapped to the superclass where method was defined, this |
| 1437 | is usually done by map_instance_to_supertype() |
| 1438 | is_classmethod: True if this method is decorated with @classmethod |
| 1439 | original_vars: Type variables of the class callable on which the method was accessed |
| 1440 | is_trivial_self: if True, we can use fast path for bind_self(). |
| 1441 | Returns: |
| 1442 | Expanded method type with added type variables (when needed). |
| 1443 | """ |
| 1444 | # TODO: verify consistency between Q and T |
| 1445 | |
| 1446 | # We add class type variables if the class method is accessed on class object |
| 1447 | # without applied type arguments, this matches the behavior of __init__(). |
| 1448 | # For example (continuing the example in docstring): |
| 1449 | # A # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any] |
| 1450 | # A[int] # The type of callable is def () -> A[int] |
| 1451 | # and |
| 1452 | # A.foo # The type is generic def [T] () -> Tuple[T, A[T]] |
| 1453 | # A[int].foo # The type is non-generic def () -> Tuple[int, A[int]] |
| 1454 | # |
| 1455 | # This behaviour is useful for defining alternative constructors for generic classes. |
| 1456 | # To achieve such behaviour, we add the class type variables that are still free |
| 1457 | # (i.e. appear in the return type of the class object on which the method was accessed). |
| 1458 | if isinstance(t, CallableType): |
| 1459 | tvars = original_vars if original_vars is not None else [] |
| 1460 | if not mx.preserve_type_var_ids: |
| 1461 | t = freshen_all_functions_type_vars(t) |
| 1462 | if is_classmethod and not t.is_bound: |
| 1463 | if is_trivial_self: |
| 1464 | t = bind_self_fast(t, mx.self_type) |
| 1465 | else: |
| 1466 | t = bind_self(t, mx.self_type, is_classmethod=True) |
| 1467 | if isuper is not None: |
| 1468 | t = expand_type_by_instance(t, isuper) |
| 1469 | freeze_all_type_vars(t) |
| 1470 | return t.copy_modified(variables=list(tvars) + list(t.variables)) |
| 1471 | elif isinstance(t, Overloaded): |
| 1472 | return Overloaded( |
| 1473 | [ |
no test coverage detected
searching dependent graphs…