PEP 484 does not include a built-in way of specifying a "blacklisted" type.
Doing that kind of thing typically doesn't really make sense -- the only reason I can think of why you might want to do something like this is if you wanted to distinguish between str
and Iterable[str]
or Sequence[str]
in some fashion. If that's the case, PEP 484 unfortunately doesn't have a way of letting you do this -- str, for better or for worse, is a legitimate subclass of those two types.
Your best bet in this case is to modify your type signature to distinguish between str
and List[str]
which are legitimately distinguishable classes. Not ideal, but better then nothing.
That said, if you really do want to do something like this, it's sort of possible to do so, assuming you're using mypy and don't mind resorting to dirty hacks.
One hack is to basically modify your foo
function so it always returns something and abuse overload
and the semantics of non-strict optional mode, like so:
from typing import overload, Optional
@overload
def foo(bar: str) -> None: ...
# Alternatively, replace "object" below
# with the types you *do* want to allow
@overload
def foo(bar: object) -> int: ...
def foo(bar: object) -> Optional[int]:
assert not isinstance(bar, str)
print(type(bar))
return 0
def main() -> None:
x = 0
x = foo(3) # typechecks
x = foo("foo") # fails
If you try running this in mypy WITHOUT the --strict-optional
tag, mypy will flag the last line with a "foo" does not return a value
error. You would then need to remember that this error message occurs because you passed in a string argument. You would also need to remember to always assign the output of your foo
function to a value.
(If you do enable strict-optional, mypy will complain that your two overloads have incompatible signatures.)
What we're basically doing here is:
- Exploiting the fact that with strict-optional disabled,
None
is a legal value of every type (so the overload is legal)
- Exploiting the fact that mypy will (as an extra perk) warn you of cases where you assign a value from a function that doesn't return anything.
This is all pretty hacky and very likely a bad idea. I also make no promises that this interaction will continue to work in the future.
The second hack you could try is to write a mypy plugin to catch instances of your specific case. The mypy plugin system is, as of time of writing, an undocumented, tentative, and highly-prone-to-change feature, but if you really want to do this, that might be something to investigate.
If you're specifically trying to distinguish between str
and Sequence[str]
or Iterable[str]
(or something similar), something else you can try is to:
- Create a custom fork of Typeshed and/or the typing module
- Modify the class definition/stubs for
str
so it doesn't inherit from either Sequence[str]
or Iterable[str]
(or add phantom types or something)
- Make mypy use your custom type definitions using the
--custom-typeshed-dir
and --custom-typing
command line arguments.
Basically, if the default type hierarchy isn't quite what you want, invent a custom one.
Of course, if you're using some other PEP 484 compliant checker (such as Pycharm's built in checker), you're probably out of luck.