diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 44f07bd77b7e..572a787a96dd 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -144,6 +144,7 @@ TypeAliasType, TypedDictType, TypeOfAny, + TypeOfLiteralString, TypeType, TypeVarType, UninhabitedType, @@ -2727,7 +2728,8 @@ def infer_literal_expr_type(self, value: LiteralValue, fallback_name: str) -> Ty return typ.copy_modified( last_known_value=LiteralType( value=value, fallback=typ, line=typ.line, column=typ.column - ) + ), + literal_string=TypeOfLiteralString.implicit, ) def concat_tuples(self, left: TupleType, right: TupleType) -> TupleType: diff --git a/mypy/messages.py b/mypy/messages.py index a5294c47e201..c57321db207d 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -79,6 +79,7 @@ TypeAliasType, TypedDictType, TypeOfAny, + TypeOfLiteralString, TypeType, TypeVarType, UnboundType, @@ -2240,6 +2241,11 @@ def format_literal_value(typ: LiteralType) -> str: if itype.extra_attrs and itype.extra_attrs.mod_name and module_names: return f"{base_str} {itype.extra_attrs.mod_name}" return base_str + elif ( + itype.type.fullname == "builtins.str" + and itype.literal_string == TypeOfLiteralString.explicit + ): + return "LiteralString" if verbosity >= 2 or (fullnames and itype.type.fullname in fullnames): base_str = itype.type.fullname else: diff --git a/mypy/nodes.py b/mypy/nodes.py index 7334d9114346..40920cf17a73 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -138,8 +138,6 @@ def get_column(self) -> int: "typing.DefaultDict": "collections.defaultdict", "typing.Deque": "collections.deque", "typing.OrderedDict": "collections.OrderedDict", - # HACK: a lie in lieu of actual support for PEP 675 - "typing.LiteralString": "builtins.str", } # This keeps track of the oldest supported Python version where the corresponding @@ -154,15 +152,12 @@ def get_column(self) -> int: "typing.DefaultDict": (2, 7), "typing.Deque": (2, 7), "typing.OrderedDict": (3, 7), - "typing.LiteralString": (3, 11), } # This keeps track of aliases in `typing_extensions`, which we treat specially. typing_extensions_aliases: Final = { # See: https://github.com/python/mypy/issues/11528 - "typing_extensions.OrderedDict": "collections.OrderedDict", - # HACK: a lie in lieu of actual support for PEP 675 - "typing_extensions.LiteralString": "builtins.str", + "typing_extensions.OrderedDict": "collections.OrderedDict" } reverse_builtin_aliases: Final = { @@ -176,8 +171,6 @@ def get_column(self) -> int: _nongen_builtins.update((name, alias) for alias, name in type_aliases.items()) # Drop OrderedDict from this for backward compatibility del _nongen_builtins["collections.OrderedDict"] -# HACK: consequence of hackily treating LiteralString as an alias for str -del _nongen_builtins["builtins.str"] def get_nongen_builtins(python_version: tuple[int, int]) -> dict[str, str]: diff --git a/mypy/semanal.py b/mypy/semanal.py index 9fca74b71872..e85ff826227b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -234,6 +234,7 @@ ASSERT_TYPE_NAMES, FINAL_DECORATOR_NAMES, FINAL_TYPE_NAMES, + LITERAL_STRING_NAMES, NEVER_NAMES, OVERLOAD_NAMES, PROTOCOL_NAMES, @@ -2679,7 +2680,7 @@ def is_type_ref(self, rv: Expression, bare: bool = False) -> bool: if bare: # These three are valid even if bare, for example # A = Tuple is just equivalent to A = Tuple[Any, ...]. - valid_refs = {"typing.Any", "typing.Tuple", "typing.Callable"} + valid_refs = {"typing.Any", "typing.Tuple", "typing.Callable", *LITERAL_STRING_NAMES} else: valid_refs = type_constructors diff --git a/mypy/subtypes.py b/mypy/subtypes.py index b922d784af88..c9e5f1783ce5 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -49,6 +49,7 @@ TypeAliasType, TypedDictType, TypeOfAny, + TypeOfLiteralString, TypeType, TypeVarTupleType, TypeVarType, @@ -463,7 +464,20 @@ def visit_instance(self, left: Instance) -> bool: # and we can't have circular promotions. if left.type.alt_promote is right.type: return True + rname = right.type.fullname + + # Check `LiteralString` special case: + if ( + rname == "builtins.str" + and right.literal_string == TypeOfLiteralString.explicit + and left.type.fullname == rname + ): + return left.literal_string is not None or ( + left.last_known_value is not None + and isinstance(left.last_known_value.value, str) + ) + # Always try a nominal check if possible, # there might be errors that a user wants to silence *once*. # NamedTuples are a special case, because `NamedTuple` is not listed @@ -773,6 +787,13 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: def visit_literal_type(self, left: LiteralType) -> bool: if isinstance(self.right, LiteralType): return left == self.right + elif ( + isinstance(left.value, str) + and isinstance(self.right, Instance) + and self.right.type.fullname == "builtins.str" + and self.right.literal_string is not None + ): + return True else: return self._is_subtype(left.fallback, self.right) diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index fe404cda0bec..143f150c9771 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -211,12 +211,8 @@ def visit_instance(self, t: Instance) -> Type: raw_last_known_value = t.last_known_value.accept(self) assert isinstance(raw_last_known_value, LiteralType) # type: ignore[misc] last_known_value = raw_last_known_value - return Instance( - typ=t.type, - args=self.translate_types(t.args), - line=t.line, - column=t.column, - last_known_value=last_known_value, + return t.copy_modified( + args=self.translate_types(t.args), last_known_value=last_known_value ) def visit_type_var(self, t: TypeVarType) -> Type: diff --git a/mypy/typeanal.py b/mypy/typeanal.py index adf58a3d7341..e75f0f3b91b4 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -45,6 +45,7 @@ from mypy.types import ( ANNOTATED_TYPE_NAMES, FINAL_TYPE_NAMES, + LITERAL_STRING_NAMES, LITERAL_TYPE_NAMES, NEVER_NAMES, TYPE_ALIAS_NAMES, @@ -74,6 +75,7 @@ TypedDictType, TypeList, TypeOfAny, + TypeOfLiteralString, TypeQuery, TypeType, TypeVarLikeType, @@ -575,6 +577,10 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ self.fail("Unpack[...] requires exactly one type argument", t) return AnyType(TypeOfAny.from_error) return UnpackType(self.anal_type(t.args[0]), line=t.line, column=t.column) + elif fullname in LITERAL_STRING_NAMES: + inst = self.named_type("builtins.str") + inst.literal_string = TypeOfLiteralString.explicit + return inst return None def get_omitted_any(self, typ: Type, fullname: str | None = None) -> AnyType: @@ -1602,7 +1608,10 @@ def expand_type_alias( assert isinstance(node.target, Instance) # type: ignore[misc] # Note: this is the only case where we use an eager expansion. See more info about # no_args aliases like L = List in the docstring for TypeAlias class. - return Instance(node.target.type, [], line=ctx.line, column=ctx.column) + inst = node.target.copy_modified(args=[]) + inst.line = ctx.line + inst.column = ctx.column + return inst return TypeAliasType(node, [], line=ctx.line, column=ctx.column) if ( exp_len == 0 diff --git a/mypy/types.py b/mypy/types.py index e322cf02505f..7f006bb99312 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -133,6 +133,8 @@ OVERLOAD_NAMES: Final = ("typing.overload", "typing_extensions.overload") +LITERAL_STRING_NAMES: Final = ("typing.LiteralString", "typing_extensions.LiteralString") + # Attributes that can optionally be defined in the body of a subclass of # enum.Enum but are removed from the class __dict__ by EnumMeta. ENUM_REMOVED_PROPS: Final = ("_ignore_", "_order_", "__order__") @@ -1196,6 +1198,15 @@ def __repr__(self) -> str: return f"ExtraAttrs({self.attrs!r}, {self.immutable!r}, {self.mod_name!r})" +class TypeOfLiteralString: + """Used to specify what kind of `LiteralString` are we dealing with.""" + + __slots__ = () + + explicit: Final = 1 + implicit: Final = 2 + + class Instance(ProperType): """An instance type of form C[T1, ..., Tn]. @@ -1206,7 +1217,16 @@ class Instance(ProperType): fallbacks for all "non-special" (like UninhabitedType, ErasedType etc) types. """ - __slots__ = ("type", "args", "invalid", "type_ref", "last_known_value", "_hash", "extra_attrs") + __slots__ = ( + "type", + "args", + "invalid", + "type_ref", + "last_known_value", + "_hash", + "extra_attrs", + "literal_string", + ) def __init__( self, @@ -1217,6 +1237,7 @@ def __init__( *, last_known_value: LiteralType | None = None, extra_attrs: ExtraAttrs | None = None, + literal_string: int | None = None, ) -> None: super().__init__(line, column) self.type = typ @@ -1279,6 +1300,11 @@ def __init__( # to be "short-lived", we don't serialize it, and even don't store as variable type. self.extra_attrs = extra_attrs + # Is set to `1` when explicit `LiteralString` type is used. + # Is set to `2` when implicit `LiteralString` is used, like `'a'` + # Is `None` by default. + self.literal_string = literal_string + def accept(self, visitor: TypeVisitor[T]) -> T: return visitor.visit_instance(self) @@ -1295,6 +1321,7 @@ def __eq__(self, other: object) -> bool: and self.args == other.args and self.last_known_value == other.last_known_value and self.extra_attrs == other.extra_attrs + and self.literal_string == self.literal_string ) def serialize(self) -> JsonDict | str: @@ -1307,6 +1334,7 @@ def serialize(self) -> JsonDict | str: data["args"] = [arg.serialize() for arg in self.args] if self.last_known_value is not None: data["last_known_value"] = self.last_known_value.serialize() + data["literal_string"] = self.literal_string return data @classmethod @@ -1325,6 +1353,7 @@ def deserialize(cls, data: JsonDict | str) -> Instance: inst.type_ref = data["type_ref"] # Will be fixed up by fixup.py later. if "last_known_value" in data: inst.last_known_value = LiteralType.deserialize(data["last_known_value"]) + inst.literal_string = data["literal_string"] return inst def copy_modified( @@ -1332,15 +1361,19 @@ def copy_modified( *, args: Bogus[list[Type]] = _dummy, last_known_value: Bogus[LiteralType | None] = _dummy, + literal_string: Bogus[int | None] = _dummy, ) -> Instance: new = Instance( self.type, args if args is not _dummy else self.args, self.line, self.column, - last_known_value=last_known_value - if last_known_value is not _dummy - else self.last_known_value, + last_known_value=( + last_known_value if last_known_value is not _dummy else self.last_known_value + ), + literal_string=( + literal_string if literal_string is not _dummy else self.literal_string + ), ) # We intentionally don't copy the extra_attrs here, so they will be erased. new.can_be_true = self.can_be_true @@ -2904,6 +2937,9 @@ def visit_instance(self, t: Instance) -> str: else: s = t.type.fullname or t.type.name or "" + if t.literal_string == TypeOfLiteralString.explicit: + s = "LiteralString" + if t.args: if t.type.fullname == "builtins.tuple": assert len(t.args) == 1 diff --git a/mypy/typestate.py b/mypy/typestate.py index a5d65c4b4ea3..ba68dc15389f 100644 --- a/mypy/typestate.py +++ b/mypy/typestate.py @@ -141,6 +141,8 @@ def is_cached_subtype_check(kind: SubtypeKind, left: Instance, right: Instance) # will be an unbounded number of potential types to cache, # making caching less effective. return False + if left.literal_string or right.literal_string: + return False info = right.type cache = TypeState._subtype_caches.get(info) if cache is None: diff --git a/test-data/unit/check-literal-string.test b/test-data/unit/check-literal-string.test new file mode 100644 index 000000000000..056b72ec6cf2 --- /dev/null +++ b/test-data/unit/check-literal-string.test @@ -0,0 +1,250 @@ +-- LiteralString tests +-- See https://peps.python.org/pep-0675/ + +[case testLiteralStringInference] +from typing_extensions import LiteralString + +x: LiteralString = 'a' +reveal_type(x) # N: Revealed type is "LiteralString" +raw_str: str = x # Ok, can be narrowed +reveal_type(raw_str) # N: Revealed type is "builtins.str" + +some_str: str +y: LiteralString = some_str # E: Incompatible types in assignment (expression has type "str", variable has type "LiteralString") + +z: LiteralString = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "LiteralString") +[builtins fixtures/literal_string.pyi] + + +[case testLiteralStringAndFString] +from typing_extensions import LiteralString +x: LiteralString = f'Value: {1}' # E: Incompatible types in assignment (expression has type "str", variable has type "LiteralString") +[builtins fixtures/literal_string.pyi] + + +[case testLiteralStringAndStrImplicitTypes] +reveal_type('a') # N: Revealed type is "Literal['a']?" + +x = 'a' +reveal_type(x) # N: Revealed type is "builtins.str" +x = f'Value: {x}' +reveal_type(x) # N: Revealed type is "builtins.str" + +if int(): + cond = 'abc' # Literal +else: + cond = ','.join(['a', 'b']) # Dynamic + +literal_names = ["native"] +reveal_type(literal_names) # N: Revealed type is "builtins.list[builtins.str]" +literal_names.append(str()) +[builtins fixtures/literal_string.pyi] + + +[case testLiteralTypeAndLiteralString] +from typing_extensions import LiteralString, Literal + +l1: Literal['a'] +l2: Literal[1] + +ls1: LiteralString = l1 +ls2: LiteralString = l2 # E: Incompatible types in assignment (expression has type "Literal[1]", variable has type "LiteralString") + +ls3: LiteralString +l3: Literal['a'] = ls3 # E: Incompatible types in assignment (expression has type "LiteralString", variable has type "Literal['a']") + +def expects_literal_string(x: LiteralString): ... +def expects_literal_a(x: Literal['a']): ... + +expects_literal_string(l1) +expects_literal_string(ls1) + +expects_literal_a(l1) +expects_literal_a(ls1) # E: Argument 1 to "expects_literal_a" has incompatible type "LiteralString"; expected "Literal['a']" +[builtins fixtures/literal_string.pyi] + + +[case testLiteralStringFallbackToString] +from typing_extensions import LiteralString +def expects_literal_string(x: LiteralString): ... + +x: LiteralString +expects_literal_string(x.format(1)) # E: Argument 1 to "expects_literal_string" has incompatible type "str"; expected "LiteralString" +[builtins fixtures/literal_string.pyi] + + +-- TODO: this is not supported yet +-- All cases here must pass +-- But, we need literal type math for this +[case testLiteralStringTypeMath-skip] +from typing_extensions import LiteralString +def expects_literal_string(x: LiteralString): ... + +expects_literal_string('a') +expects_literal_string('a' + 'b') +expects_literal_string('a' * 2) +[builtins fixtures/literal_string.pyi] + + +[case testLiteralStringBoundTypeVar] +from typing_extensions import LiteralString +from typing import TypeVar + +T = TypeVar('T', bound=LiteralString) +def expects_literal_string(x: T): ... + +expects_literal_string('a') + +x: LiteralString +y: str +expects_literal_string(x) +expects_literal_string(y) # E: Value of type variable "T" of "expects_literal_string" cannot be "str" +[builtins fixtures/literal_string.pyi] + + +[case testLiteralStringAsMethodSig] +from typing_extensions import LiteralString + +class Base: + def method1(self, arg: LiteralString) -> str: ... + def method2(self, arg: str) -> LiteralString: ... + def method3(self, arg: LiteralString) -> LiteralString: ... + def method4(self, arg: str) -> str: ... + +class Correct(Base): + def method1(self, arg: str) -> LiteralString: ... + def method3(self, arg: str) -> LiteralString: ... + def method4(self, arg: str) -> LiteralString: ... + +class Wrong(Base): + def method2(self, arg: LiteralString) -> str: ... + def method3(self, arg: str) -> str: ... + def method4(self, arg: LiteralString) -> LiteralString: ... +[out] +main:15: error: Argument 1 of "method2" is incompatible with supertype "Base"; supertype defines the argument type as "str" +main:15: note: This violates the Liskov substitution principle +main:15: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +main:15: error: Return type "str" of "method2" incompatible with return type "LiteralString" in supertype "Base" +main:16: error: Return type "str" of "method3" incompatible with return type "LiteralString" in supertype "Base" +main:17: error: Argument 1 of "method4" is incompatible with supertype "Base"; supertype defines the argument type as "str" +main:17: note: This violates the Liskov substitution principle +main:17: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides +[builtins fixtures/literal_string.pyi] + + +[case testLiteralStringErrorMessages] +from typing_extensions import LiteralString + +class Base: + x: int +class Sub(Base): + x = 'a' # E: Incompatible types in assignment (expression has type "str", base class "Base" defined the type as "int") +class ExcplicitSub1(Base): + x: LiteralString = 'a' # E: Incompatible types in assignment (expression has type "LiteralString", base class "Base" defined the type as "int") +class ExcplicitSub2(Base): + x: LiteralString # E: Incompatible types in assignment (expression has type "LiteralString", base class "Base" defined the type as "int") + +def accepts_int(arg: int): ... +accepts_int('b') # E: Argument 1 to "accepts_int" has incompatible type "str"; expected "int" +ls1: LiteralString +ls2: LiteralString = 'a' +accepts_int(ls1) # E: Argument 1 to "accepts_int" has incompatible type "LiteralString"; expected "int" +accepts_int(ls2) # E: Argument 1 to "accepts_int" has incompatible type "LiteralString"; expected "int" +[builtins fixtures/literal_string.pyi] + + +[case testConditionalLiteralString] +from typing_extensions import LiteralString +if int(): + cond: LiteralString = 'abc' +else: + cond = ','.join(['a', 'b']) # E: Incompatible types in assignment (expression has type "str", variable has type "LiteralString") +[builtins fixtures/literal_string.pyi] + + +[case testUnionOfLiteralString] +from typing_extensions import LiteralString +from typing import Union + +x: Union[str, LiteralString] = 'a' +y: Union[str, LiteralString] = str() +z: Union[str, LiteralString] = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Union[str, LiteralString]") +[builtins fixtures/literal_string.pyi] + + +[case testTypeAliasOfLiteralString] +from typing_extensions import LiteralString, TypeAlias + +LS1: TypeAlias = LiteralString +LS2 = LiteralString + +def ls1(arg: LS1) -> LS1: ... +def ls2(arg: LS2) -> LS2: ... + +reveal_type(ls1('abc')) # N: Revealed type is "LiteralString" +reveal_type(ls2("abc")) # N: Revealed type is "LiteralString" + +x: LiteralString +reveal_type(ls1(x)) # N: Revealed type is "LiteralString" +reveal_type(ls2(x)) # N: Revealed type is "LiteralString" +y: str +ls1(y) # E: Argument 1 to "ls1" has incompatible type "str"; expected "LiteralString" +ls2(y) # E: Argument 1 to "ls2" has incompatible type "str"; expected "LiteralString" +[builtins fixtures/literal_string.pyi] + + +[case testOverloadsWithLiteralString] +from over import some + +x: str +y: str +reveal_type(some('a', 'b')) # N: Revealed type is "LiteralString" +reveal_type(some(x, y)) # N: Revealed type is "builtins.str" +reveal_type(some('a', y)) # N: Revealed type is "builtins.str" +reveal_type(some(x, 'b')) # N: Revealed type is "builtins.str" + +[file over.pyi] +from typing_extensions import LiteralString +from typing import Union, overload + +@overload +def some(x: LiteralString, y: LiteralString) -> LiteralString: ... +@overload +def some(x: str, y: str) -> str: ... +[builtins fixtures/literal_string.pyi] + + +[case testLiteralStringInsideList] +from typing_extensions import LiteralString +from typing import List + +literal_names: List[LiteralString] = ["native", "literal"] +literal_names.append(str()) # E: Argument 1 to "append" of "list" has incompatible type "str"; expected "LiteralString" + +literal_names2: List[LiteralString] = ["literal", str()] # E: List item 1 has incompatible type "str"; expected "LiteralString" +[builtins fixtures/list.pyi] + +[case testLiteralStringInsideTuple] +from typing_extensions import LiteralString +from typing import Tuple + +literal_names: Tuple[LiteralString, ...] = ("native", "literal") +literal_names2: Tuple[LiteralString, ...] = ("literal", str()) # E: Incompatible types in assignment (expression has type "Tuple[str, str]", variable has type "Tuple[LiteralString, ...]") +[builtins fixtures/tuple.pyi] + +[case testLiteralStringInsideSet] +from typing_extensions import LiteralString +from typing import Set + +literal_names: Set[LiteralString] = {"native", "literal"} +literal_names2: Set[LiteralString] = {"literal", str()} # E: Argument 2 to has incompatible type "str"; expected "LiteralString" +[builtins fixtures/set.pyi] + +[case testLiteralStringInsideDict] +from typing_extensions import LiteralString +from typing import Dict + +literal_names: Dict[LiteralString, LiteralString] = {"native": "literal"} +literal_names2: Dict[LiteralString, int] = {"literal": 1, str(): 2} # E: Dict entry 1 has incompatible type "str": "int"; expected "LiteralString": "int" +literal_names3: Dict[int, LiteralString] = {2: str(), 1: "literal"} # E: Dict entry 0 has incompatible type "int": "str"; expected "int": "LiteralString" +[builtins fixtures/dict.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 8dafc8f47a6c..6196162f818c 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -759,14 +759,8 @@ from typing import LiteralString as tpLS from typing_extensions import LiteralString as tpxLS def f(a: tpLS, b: tpxLS) -> None: - reveal_type(a) # N: Revealed type is "builtins.str" - reveal_type(b) # N: Revealed type is "builtins.str" - -# This isn't the correct behaviour, but should unblock use of LiteralString in typeshed -f("asdf", "asdf") -string: str -f(string, string) - + reveal_type(a) # N: Revealed type is "LiteralString" + reveal_type(b) # N: Revealed type is "LiteralString" [builtins fixtures/tuple.pyi] [typing fixtures/typing-medium.pyi] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index 05361924ed89..5a1fe19859f8 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -10029,6 +10029,31 @@ class C(B): ... == main.py:4: note: Revealed type is "def () -> builtins.str" +[case testLiteralStringCache] +import main +[file main.py] +from typing_extensions import LiteralString + +def some(x: LiteralString) -> LiteralString: + return x + +x: LiteralString = 'a' +some(x) +some('b') +[file main.py.2] +from typing_extensions import LiteralString + +def some(x: LiteralString) -> str: + return x + +x: str +some(x) # error +some('b') +[builtins fixtures/tuple.pyi] +[out] +== +main.py:7: error: Argument 1 to "some" has incompatible type "str"; expected "LiteralString" + [case testAbstractBodyTurnsEmpty] # flags: --strict-optional from b import Base diff --git a/test-data/unit/fixtures/literal_string.pyi b/test-data/unit/fixtures/literal_string.pyi new file mode 100644 index 000000000000..082c82ad9f60 --- /dev/null +++ b/test-data/unit/fixtures/literal_string.pyi @@ -0,0 +1,57 @@ +# Builtins stub used in tuple-related test cases. + +from typing import Iterable, Iterator, TypeVar, Generic, Sequence, Any, overload, Tuple, Type + +T = TypeVar("T") +Tco = TypeVar('Tco', covariant=True) + +class object: + def __init__(self) -> None: pass + +class type: + def __init__(self, *a: object) -> None: pass + def __call__(self, *a: object) -> object: pass +class tuple(Sequence[Tco], Generic[Tco]): + def __new__(cls: Type[T], iterable: Iterable[Tco] = ...) -> T: ... + def __iter__(self) -> Iterator[Tco]: pass + def __contains__(self, item: object) -> bool: pass + @overload + def __getitem__(self, x: int) -> Tco: pass + @overload + def __getitem__(self, x: slice) -> Tuple[Tco, ...]: ... + def __mul__(self, n: int) -> Tuple[Tco, ...]: pass + def __rmul__(self, n: int) -> Tuple[Tco, ...]: pass + def __add__(self, x: Tuple[Tco, ...]) -> Tuple[Tco, ...]: pass + def count(self, obj: object) -> int: pass +class function: pass +class ellipsis: pass +class classmethod: pass + +# We need int and slice for indexing tuples. +class int: + def __neg__(self) -> 'int': pass +class float: pass +class slice: pass +class bool(int): pass +class str: + def __add__(self, __other: str) -> str: pass + def __mul__(self, __num: int) -> str: pass + def format(self, *args: Any) -> str: pass + def join(self, __items: Iterable[str]) -> str: pass +class bytes: pass +class unicode: pass + +class list(Sequence[T], Generic[T]): + @overload + def __getitem__(self, i: int) -> T: ... + @overload + def __getitem__(self, s: slice) -> list[T]: ... + def __contains__(self, item: object) -> bool: ... + def __iter__(self) -> Iterator[T]: ... + def append(self, __item: T) -> None: ... + +def isinstance(x: object, t: type) -> bool: pass + +def sum(iterable: Iterable[T], start: T = None) -> T: pass + +class BaseException: pass diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index e92f7e913502..e1819a8058fb 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -36,6 +36,8 @@ Never: _SpecialForm TypeVarTuple: _SpecialForm Unpack: _SpecialForm +LiteralString: _SpecialForm + # Fallback type for all typed dicts (does not exist at runtime). class _TypedDict(Mapping[str, object]): # Needed to make this class non-abstract. It is explicitly declared abstract in