Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Allow mypy to flag str matching against Sequence[str] as an error #11001

Description

@sirosen

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

python/typing#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")])

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions