I recently made the mistake of opening my $PYTHONSTARTUP file with mypy syntax checking enabled. As a result, I started getting this error:
startup.py|79 col 2 error| Incompatible types in assignment (expression has type "HistoryPrompt", variable has type "str")
On line 79:
sys.ps1 = HistoryPrompt()
I immediately thought, "By Jove, mypy! You're entirely correct! And yet, that is exactly what I want to do, so you're also wrong!"
I went looking to see if there was some kind of "stub" for the sys module, but couldn't find anything. I'm guessing that mypy is determining the type by looking at the value stored in the variable (default is ">>> ", which is a str).
In reality, of course, the type needs to be the non-existant typing.Stringifiable, indicating an object that will respond to str(x).
Having reached that dead end, I went looking for a way to tell mypy to suppress the error. So many of the other tools support # noqa: xxx that I figured there must be something, right?
Wrong. Or at least, I couldn't find it in my version, which is: mypy 0.670
So I devised a hack clever work-around:
import typing
# Work around mypy error: Incompatible types in assignment
suppress_mypy_error: typing.Any = HistoryPrompt()
sys.ps1 = suppress_mypy_error
My question is this: Is there a way to suppress this particular error in-line (best), or in mypy.ini, or by submitting a PR to python/mypy, or ...?
To explicitly suppress MyPy on a specific line, add a comment of the form # type: ignore.
To suppress MyPy for an entire module, add the same comment at the top of the module.
Source: https://mypy.readthedocs.io/en/latest/common_issues.html#spurious-errors-and-locally-silencing-the-checker
Overview
I prefer to suppress mypy errors based on 2 things:
specific lines (your question), and
specific error.
Example
For example, the # type: ignore [no-untyped-call]:
# ignore mypy error because azure has broken type hints.
# See https://github.com/Azure/azure-sdk-for-python/issues/20083 (the issue is closed but the problem remains)
exception = azure.core.exceptions.ResourceNotFoundError("Test") # type: ignore [no-untyped-call]
You can find out the "error code" (e.g. no-untyped-call) in the mypy output by configuring mypy with:
in pyproject.toml
[tool.mypy]
show_error_codes = true
or in mypy.ini
[mypy]
show_error_codes = True
Benefits
Documentation in code: You can see exactly what error is being suppressed.
You won't ignore other errors by mypy. Otherwise, that line could be a source of bugs in the future and mypy would not warn you.
Related
After looking at this question I learned that the type hints are, by default, not enforced whilst executing Python code.
One can detect some discrepancies between the type hints and actual argument types using a slightly convoluted process of running pyannotate to generate stubs whilst running Python code, and scanning for differences after applying these stubs to the code.
However, it would be more convenient/faster to directly raise an exception if an incoming argument is not of the type included in the type hint. This can be achieved by manually including:
if not isinstance(some_argument, the_type_hint_type):
raise TypeError("Argument:{argument} is not of type:{the_type_hint_type}")
However, that is quite labour intensive. Hence, I was curious, is it possible to make Python raise an error if a type-hint is violated, using an CLI argument or pip package or something like that?
Hope this helps you - https://typeguard.readthedocs.io/en/latest/userguide.html#using-the-decorator
Thanks..
The edit queue for the Answer by #Surya_1897 is full, hence I will include a more detailed description of the solution here.
Typeguard does exactly what I was looking for. The following requirements apply:
Install typeguard with:
pip install typeguard
Import typeguard into each script, and add the #typechecked property above each function.
Example:
Change:
"""Some file description."""
def add_two(x:int):
"""Adds two to an incoming int."""
return x+2
somevar:float=42.1
add_two(somevar)
To:
"""Some file description."""
from typeguard import typechecked
#typechecked
def add_two(x:int):
"""Adds two to an incoming int."""
return x+2
somevar:float=42.1
add_two(somevar)
The latter will than throw an err:
TypeError: type of argument "x" must be int; got float instead
I'm wondering what's the best approach to deal with unknown types of functions/methods associated with other modules. Note that I'm using strict mode
For example, I have the following:
rooms: List[str] = list(mongo_client.devices.distinct("room"))
mongo_client is just an instance of a MongoClient imported from pymongo. VSCode screams that it doesn't know the type of the distinct method:
Type of "distinct" is partially unknown
Type of "distinct" is "(key: Unknown, filter: Unknown = None, session: Unknown = None, **kwargs: Unknown) -> Unknown"PylancereportUnknownMemberType
Argument type is unknown
Argument corresponds to parameter "iterable" in function "__init__"PylancereportUnknownArgumentType
What I can do:
Add reportUnknownMemberType to pyrightconfig.json; however, while this removes the previous warning, it's also going to disable warnings that I probably really want
Add # type: ignore on the line with that distinct call; I usually hate having flying ignore-comments like this and I don't think it "fixes" anything
Create a stub file myself
What would you do? Should I not use strict mode? Most of the project was written with the strict mode activated to make sure I'm not missing anything. Is these some cast trick I can do?
Thanks!
This isn't an ideal solution since it ruins the import's IntelliSense, but it avoids disabling type checks you might want elsewhere and makes it so you don't have to write your own stubs.
from typing import Any, cast
from somewhere import Something # type: ignore
Something = cast(Any, Something)
Now you can use Something freely.
have been using the following code
import yaml
try:
filterwarnings(yaml.YAMLLoadWarning)
except AttributeError:
pass
But when I tried to run mypy today I got "module has no attribute YAMLLoadWarning". Which is true on some versions of python. Is there a better way to write this?
EDIT:
To be a little clearer, I know how to ignore the error (and catch the exception related to the python 3.6 version of pyyaml not including that exception). My question is more about working with the parser. Consider these examples-
I know that if you have a function that returns a more specific type
def bad(a: Optional[int]) -> int:
return a # Incompatible return value type (got "Optional[int]", expected "int")
You can use a branch to force only the correct type to be returned, and the parser notices
def good(a: Optional[int]) -> int:
if a:
return a
return 0
So in situations where you handle error situations using a try/catch statement, is there a way to construct this so that the parser realizes that the attribute error is handled?
def exception_branch(a: Optional[str])-> list:
try:
return a.split() # Item "None" of "Optional[str]" has no attribute "split"
except:
return []
So in situations where you handle error situations using a try/catch statement, is there a way to construct this so that the parser realizes that the attribute error is handled?
No, there is not, I'm afraid. The problem is that catch AttributeError does not indicate where from the exception comes. So if you had
try:
print(foo.barr)
return a.split()
except AttributeError:
return []
The typechecker can ignore the fact that a can be None, but it would have to ignore also the fact that you misspelled bar and there is no barr attribute in the object foo. See also here.
I'm assuming you're using PyYAML?
In that case, the best long-term fix is probably for you to submit a pull request to Typeshed including type hints for this class. (Typeshed is the repository of type hints for standard library modules and select third party modules. The stubs for PyYAML happen to be included within typeshed here.)
It seems PyYAML defines YAMLLoadWarning within the module's __init__.py file so you should probably add type hints for that class within the corresponding __init__.pyi file in Typeshed.
Then you wait for the next release of mypy -- it bakes in the latest available version of Typeshed the time of release.
I believe mypy is actually scheduled to release later today, so the timing might be a bit tight if you end up submitting a PR. But worst case scenario, you'll just need to wait for another month or two for the subsequent mypy release.
In the meantime, you can just add a # type: ignore comment to that line, as suggested by Georgy in the comments.
If you do this, I also recommend running mypy with the --warn-unused-ignores command line flag. This will help you find # type: ignore comments you no longer need as mypy releases/improves over time.
I wrote the code, but I get the following message in pycharm(2019.1):
"Parameterized generics cannot be used with class or instance checks"
def data_is_valid(data):
keys_and_types = {
'comment': (str, type(None)),
'from_budget': (bool, type(None)),
'to_member': (int, type(None)),
'survey_request': (int, type(None)),
}
def type_is_valid(test_key, test_value):
return isinstance(test_value, keys_and_types[test_key])
type_is_valid('comment', 3)
I really do not understand this message well. Did I do something wrong or is it a bug in pycharm?
The error disappears if I explicitly typecast to tuple.
def type_is_valid(test_key, test_value):
return isinstance(test_value, tuple(keys_and_types[test_key]))
That looks like a bug in pycharm where it's a bit overeager in assuming that you're using the typing module in an unintended way. See this example here where that assumption would have been correct:
The classes in the typing module are only useful in a type annotation context, not to inspect or compare to actual classes, which is what isinstance tries to do. Since pycharm sees a simple object with square brackets that do not contain a literal, it jumps to the wrong conclusion you are seeing.
Your code is fine, you can use it exactly as it is.
I will not repeat after others that this is a pycharm bug. Just if you are a perfectionist and the error hurts your eyes, add the comment
# noqa
to the line where the "error" is
This was a known bug in PyCharm 2018, reported here.
There are some related bugs still in more recent PyCharm versions, e.g. PyCharm 2021.2.2, here.
In general, when you found that some PyCharm warning is incorrect, I would first isolate a simple test case where it becomes more clear what PyCharm is actually incorrect about. When it is clear that PyCharm is wrong with the warning, then you should always fill a bug report about it (or maybe search for existing bug reports first). Here this is clear because PyCharm says you cannot do sth, while in fact you can, so sth is wrong.
Since it's agreed it's a bug, you can suppress it in Pycharm by the line:
# noinspection PyTypeHints
Right now, if I have some function like this and I'd like to be able to get the error about index not being defined, while ignoring the error that some_index is not defined.
def myfunction(ind, other):
"""
Parameters
----------
ind: Index
other: Index or set
Returns
-------
Index.
Examples
--------
>>> myfunction(some_index, other)
"""
return index + other
If I run this through flake8 I get:
file.py:15:1: F821 undefined name 'other'
file.py:15:1: F821 undefined name 'some_index'
file.py:17:1: F821 undefined name 'index'
But what I want to see is just the index error and ignore the others:
file.py:17:1: F821 undefined name 'index'
If I run pylint or pyflakes on it, gives an error about some_index and other not being defined (which is True, but not necessarily useful all the time). How do I tell the programs to skip those errors? I'm working on a big library with many examples scattered throughout, some of which aren't defined but are just set to be examples of how to call them. Yes, it isn't great to not be able to run the doctests, but for the moment, it adds a ton of noise to pylint or pyflakes output. Flake8 doesn't seem to offer the option to skip them either.
How can I make this work? Is there an easy way to detect which things are docstrings in Python and filter the results that way?
Checking docstrings is a regression in pyflakes; please go comment on that bug to add your voice to the discussion.
You can disable it for now by setting the environment variable PYFLAKES_NODOCTEST.
flake8 provides an option ignore:
flake8 --ignore=F821 a.py