Skip to content

Commit 1564365

Browse files
committed
PEP 675: add exhaustive list of str methods to update.
These methods will need to have an overload for the `Literal[str]` type.
1 parent f534f69 commit 1564365

File tree

1 file changed

+245
-0
lines changed

1 file changed

+245
-0
lines changed

pep-0675.rst

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ if they evaluate to the same value (``str``), such as
300300
Type Inference
301301
==============
302302

303+
.. _inferring_literal_str:
303304

304305
Inferring ``Literal[str]``
305306
--------------------------
@@ -327,6 +328,10 @@ following cases:
327328
has type ``Literal[str]`` if and only if ``s`` and the arguments have
328329
types compatible with ``Literal[str]``.
329330

331+
+ Literal-preserving methods: In `Appendix C <appendix_C_>`_, we have
332+
provided an exhaustive list of ``str`` methods that preserve the
333+
``Literal[str]`` type.
334+
330335
In all other cases, if one or more of the composed values has a
331336
non-literal type ``str``, the composition of types will have type
332337
``str``. For example, if ``s`` has type ``str``, then ``"hello" + s``
@@ -955,6 +960,246 @@ is documentation, which is easily ignored and often not seen. With
955960
``Literal[str]``, API misuse requires conscious thought and artifacts
956961
in the code that reviewers and future developers can notice.
957962

963+
.. _appendix_C:
964+
965+
Appendix C: ``str`` methods that preserve ``Literal[str]``
966+
==========================================================
967+
968+
The ``str`` class has several methods that would benefit from
969+
``Literal[str]``. For example, users might expect
970+
``"hello".capitalize()`` to have the type ``Literal[str]`` similar to
971+
the other examples we have seen in the `Inferring Literal[str] section
972+
<inferring_literal_str>`_ section. Inferring the type ``Literal[str]``
973+
is correct because the string is not an arbitrary user-supplied
974+
string. In other words, the ``capitalize`` method preserves the
975+
``Literal[str]`` type. There are several other methods that preserve
976+
the ``Literal[str]``.
977+
978+
We face a tradeoff in this PEP. We could require type checkers to
979+
preserve ``Literal[str]`` either (a) only for the four cases mentioned
980+
in the `Inferring Literal[str] section <inferring_literal_str_>`_
981+
section or (b) for all the ``str`` methods for which it would be
982+
valid. Option (a) might surprise users by losing the ``Literal[str]``
983+
type in innocuous uses, e.g., with ``my_literal.capitalize()``. Option
984+
(b) would be more user-friendly but would require some more work from
985+
type checkers.
986+
987+
We decided to favor user-friendliness and go with option (b). However,
988+
if the Steering Council feels the other way, we are willing to go with
989+
option (a).
990+
991+
Further, we propose updating the stub for ``str`` in typeshed so that
992+
the methods are overloads with the ``Literal[str]``-preserving
993+
versions. This means type checkers do not have hardcode
994+
``Literal[str]`` behavior for each method. This also lets us easily
995+
support new methods in the future by updating the typeshed stub.
996+
997+
For example, to preserve literal types for the ``capitalize`` method,
998+
we would change the stub as below:
999+
1000+
::
1001+
1002+
# before
1003+
def capitalize(self) -> str: ...
1004+
1005+
# after
1006+
@overload
1007+
def capitalize(self: Literal[str]) -> Literal[str]: ...
1008+
@overload
1009+
def capitalize(self) -> str: ...
1010+
1011+
The downside of changing ``str`` stub is that the stub becomes more
1012+
complicated and can make error messages harder to understand. Type
1013+
checkers may need to special-case ``str`` so that error messages are
1014+
understandable for users.
1015+
1016+
If the Steering Council is opposed to this typeshed stub change, we
1017+
will require type checkers to hardcode these methods.
1018+
1019+
Below is an exhaustive list of ``str`` methods which, when called as
1020+
indicated with ``Literal[str]``(s) must be treated as returning a
1021+
``Literal[str]``. If this PEP is accepted, we will update these method
1022+
signatures in typeshed:
1023+
1024+
::
1025+
1026+
@overload
1027+
def __new__(cls, object: Literal[str] = ...) -> Literal[str]: ...
1028+
@overload
1029+
1030+
@overload
1031+
def capitalize(self: Literal[str]) -> Literal[str]: ...
1032+
@overload
1033+
def capitalize(self) -> str: ...
1034+
1035+
@overload
1036+
def casefold(self: Literal[str]) -> Literal[str]: ...
1037+
@overload
1038+
def casefold(self) -> str: ...
1039+
1040+
@overload
1041+
def center(self: Literal[str], __width: SupportsIndex, __fillchar: Literal[str] = ...) -> Literal[str]: ...
1042+
@overload
1043+
def center(self, __width: SupportsIndex, __fillchar: str = ...) -> str: ...
1044+
1045+
if sys.version_info >= (3, 8):
1046+
@overload
1047+
def expandtabs(self: Literal[str], tabsize: SupportsIndex = ...) -> Literal[str]: ...
1048+
@overload
1049+
def expandtabs(self, tabsize: SupportsIndex = ...) -> str: ...
1050+
1051+
else:
1052+
@overload
1053+
def expandtabs(self: Literal[str], tabsize: int = ...) -> Literal[str]: ...
1054+
@overload
1055+
def expandtabs(self, tabsize: int = ...) -> str: ...
1056+
1057+
@overload
1058+
def format(self: Literal[str], *args: Literal[str], **kwargs: Literal[str]) -> Literal[str]: ...
1059+
@overload
1060+
def format(self, *args: str, **kwargs: str) -> str: ...
1061+
1062+
@overload
1063+
def format_map(self: Literal[str], map: Mapping[str, Literal[str]]) -> Literal[str]: ...
1064+
@overload
1065+
def format_map(self, map: Mapping[str, str]) -> str: ...
1066+
1067+
@overload
1068+
def join(self: Literal[str], __iterable: Iterable[Literal[str]]) -> Literal[str]: ...
1069+
@overload
1070+
def join(self, __iterable: Iterable[str]) -> str: ...
1071+
1072+
@overload
1073+
def ljust(self: Literal[str], __width: SupportsIndex, __fillchar: Literal[str] = ...) -> Literal[str]: ...
1074+
@overload
1075+
def ljust(self, __width: SupportsIndex, __fillchar: str = ...) -> str: ...
1076+
1077+
@overload
1078+
def lower(self: Literal[str]) -> Literal[str]: ...
1079+
@overload
1080+
def lower(self) -> Literal[str]: ...
1081+
1082+
@overload
1083+
def lstrip(self: Literal[str], __chars: Literal[str] | None = ...) -> Literal[str]: ...
1084+
@overload
1085+
def lstrip(self, __chars: str | None = ...) -> str: ...
1086+
1087+
@overload
1088+
def partition(self: Literal[str], __sep: Literal[str]) -> tuple[Literal[str], Literal[str], Literal[str]]: ...
1089+
@overload
1090+
def partition(self, __sep: str) -> tuple[str, str, str]: ...
1091+
1092+
@overload
1093+
def replace(self: Literal[str], __old: Literal[str], __new: Literal[str], __count: SupportsIndex = ...) -> Literal[str]: ...
1094+
@overload
1095+
def replace(self, __old: str, __new: str, __count: SupportsIndex = ...) -> str: ...
1096+
1097+
if sys.version_info >= (3, 9):
1098+
@overload
1099+
def removeprefix(self: Literal[str], __prefix: Literal[str]) -> Literal[str]: ...
1100+
@overload
1101+
def removeprefix(self, __prefix: str) -> str: ...
1102+
1103+
@overload
1104+
def removesuffix(self: Literal[str], __suffix: Literal[str]) -> Literal[str]: ...
1105+
@overload
1106+
def removesuffix(self, __suffix: str) -> str: ...
1107+
1108+
@overload
1109+
def rjust(self: Literal[str], __width: SupportsIndex, __fillchar: Literal[str] = ...) -> Literal[str]: ...
1110+
@overload
1111+
def rjust(self, __width: SupportsIndex, __fillchar: str = ...) -> str: ...
1112+
1113+
@overload
1114+
def rpartition(self: Literal[str], __sep: Literal[str]) -> tuple[Literal[str], Literal[str], Literal[str]]: ...
1115+
@overload
1116+
def rpartition(self, __sep: str) -> tuple[str, str, str]: ...
1117+
1118+
@overload
1119+
def rsplit(self: Literal[str], sep: Literal[str] | None = ..., maxsplit: SupportsIndex = ...) -> list[Literal[str]]: ...
1120+
@overload
1121+
def rsplit(self, sep: str | None = ..., maxsplit: SupportsIndex = ...) -> list[str]: ...
1122+
1123+
@overload
1124+
def rstrip(self: Literal[str], __chars: Literal[str] | None = ...) -> Literal[str]: ...
1125+
@overload
1126+
def rstrip(self, __chars: str | None = ...) -> str: ...
1127+
1128+
@overload
1129+
def split(self: Literal[str], sep: Literal[str] | None = ..., maxsplit: SupportsIndex = ...) -> list[Literal[str]]: ...
1130+
@overload
1131+
def split(self, sep: str | None = ..., maxsplit: SupportsIndex = ...) -> list[str]: ...
1132+
1133+
@overload
1134+
def splitlines(self: Literal[str], keepends: bool = ...) -> list[Literal[str]]: ...
1135+
@overload
1136+
def splitlines(self, keepends: bool = ...) -> list[str]: ...
1137+
1138+
@overload
1139+
def strip(self: Literal[str], __chars: Literal[str] | None = ...) -> Literal[str]: ...
1140+
@overload
1141+
def strip(self, __chars: str | None = ...) -> str: ...
1142+
1143+
@overload
1144+
def swapcase(self: Literal[str]) -> Literal[str]: ...
1145+
@overload
1146+
def swapcase(self) -> str: ...
1147+
1148+
@overload
1149+
def title(self: Literal[str]) -> Literal[str]: ...
1150+
@overload
1151+
def title(self) -> str: ...
1152+
1153+
@overload
1154+
def upper(self: Literal[str]) -> Literal[str]: ...
1155+
@overload
1156+
def upper(self) -> str: ...
1157+
1158+
@overload
1159+
def zfill(self: Literal[str], __width: SupportsIndex) -> Literal[str]: ...
1160+
@overload
1161+
def zfill(self, __width: SupportsIndex) -> str: ...
1162+
1163+
@overload
1164+
def __add__(self: Literal[str], __s: Literal[str]) -> Literal[str]: ...
1165+
@overload
1166+
def __add__(self, __s: str) -> str: ...
1167+
1168+
@overload
1169+
def __iter__(self: Literal[str]) -> Iterator[str]: ...
1170+
@overload
1171+
def __iter__(self) -> Iterator[str]: ...
1172+
1173+
@overload
1174+
def __mod__(self: Literal[str], __x: Union[Literal[str], Tuple[Literal[str], ...]]) -> str: ...
1175+
@overload
1176+
def __mod__(self, __x: Union[str, Tuple[str, ...]]) -> str: ...
1177+
1178+
@overload
1179+
def __mul__(self: Literal[str], __n: SupportsIndex) -> Literal[str]: ...
1180+
@overload
1181+
def __mul__(self, __n: SupportsIndex) -> str: ...
1182+
1183+
@overload
1184+
def __repr__(self: Literal[str]) -> Literal[str]: ...
1185+
@overload
1186+
def __repr__(self) -> str: ...
1187+
1188+
@overload
1189+
def __rmul__(self: Literal[str], n: SupportsIndex) -> Literal[str]: ...
1190+
@overload
1191+
def __rmul__(self, n: SupportsIndex) -> str: ...
1192+
1193+
@overload
1194+
def __str__(self: Literal[str]) -> Literal[str]: ...
1195+
@overload
1196+
def __str__(self) -> str: ...
1197+
1198+
@overload
1199+
def __getnewargs__(self: Literal[str]) -> tuple[Literal[str]]: ...
1200+
@overload
1201+
def __getnewargs__(self) -> tuple[str]: ...
1202+
9581203
Resources
9591204
=========
9601205

0 commit comments

Comments
 (0)