Allow mypy to flag `str` matching against `Sequence[str]` as an error
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 20.6k
- Forks
- 3.3k
- PR merge metrics
- PR metrics pending
Description
Feature
A new optional setting for mypy which forbids matching str against Sequence[str] or Iterable[str]. This will catch a wide variety of bugs which users encounter when defining APIs which consume these types and are unintentionally passed strings. It also allows correct overloading of str vs Sequence[str] to describe behavior.
The option would require the explicit (but "redundant") Union[str, Sequence[str]] or Union[str, Iterable[str]] for values which can be a string or non-string iterable of strings.
Pitch
https://github.com/python/typing/issues/256 has been an open issue for a while, with no clear resolution within the type system itself.
However the pytype maintainers recently weighed in to mention that they took this approach, forbidding str from matching Sequence[str], and their users have been happy with the results. I think mypy should match this behavior if possible/feasible.
In answer to Guido's question about whether or not this should apply to AnyStr, the answer is "ideally yes". However, if that's significantly harder, only applying to str is probably fine: str is likely the most common case for python developers.
Use Cases
# simple function which the author doesn't realize can consume a single string
# count_starts_lowercase("some string") should fail
def count_starts_lowercase(x: Sequence[str]) -> int:
return len([c for c in x if c[0] in string.ascii_lowercase])
# function with overloaded definitions based on str/Sequence[str]
# the following definition should work
@typing.overload
def poly_getlen(value: str) -> int: ...
@typing.overload
def poly_getlen(value: Sequence[str]) -> List[int]: ...
def poly_getlen(value: Union[str, Sequence[str]]) -> Union[int, List[int]]:
if isinstance(value, str):
return len(value)
return [len(x) for x in value]
# a sequence of unions containing str should be carefully considered
# ideally `count_terminal_digit_is_0("00011101")` would fail
# if this adds too much complexity, it can be allowed
def count_terminal_digit_is_0(x: Sequence[Union[str, int]]) -> int:
terminal_digits = [(n % 10 if isinstance(n, int) else n[-1]) for n in x]
return len([d for d in terminal_digits if d in (0, "0")])
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No files, tests, or entry points are named. Start with the issue's examples and determine where mypy handles subtype compatibility and overload resolution; done means an optional setting rejects the described str-to-Sequence[str] and str-to-Iterable[str] matches while permitting the explicit unions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers, devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100