-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Spurious error with dynamic keyword arguments #9676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for the report! You're right that the Python version difference is due to typeshed. My guess is something in mypy is looking at keyword-only args when it should be looking at presence of argument name to see if something can be passed by keyword. |
@esoma Thanks again for your earlier kwargs fix. Would you be interested in taking a look at this one too? |
@hauntsaninja Can do. |
Hopefully this is not too rambling. I have a solution for this that involves only mapping non-TypedDict **kwargs to parameters which have compatible types, but it has a ripple effect that involves #4001, #9007 and #9629. So my current solution has the following behavior: class A: pass
class B: pass
class C: pass
ad: Dict[str, A] = {}
bd: Dict[str, B] = {}
cd: Dict[str, C] = {}
def f(a: A = A(), **bs: B): pass
f(**ad) # no error
f(**ad, **bd) # no error
f(a=A(), **ad) # no error
f(a=A(), **ad, **bd) # no error
f(**bd) # no error
f(**cd) # error (note that in master all these lines will produce the error related to this issue) In the error case we are saying that we must consider that a non-TypedDict **kwarg may be empty only if it would be able to map to at least 1 formal parameter. This is contrary to #4001 and #9007 which essentially say that a dict may always be considered empty. This is because: Case Adef f(**kwargs: int) -> None: pass
d: Dict[str, str] = {} # value should not matter here, ignore it
f(**d) # produces an error, good We can imagine two scenarios: one where So this seems like a false-positive sometimes. However, we can reason about the user's intentions. It is unlikely Case BIn #9007 we're given an example similar to this: def f() -> None: pass
d: Dict[str, str] = {} # value should not matter here, ignore it
f(**d) # produces an error, user in #9007 does not want one @JukkaL seemed to agree that mypy should not error here. However, I think this runs contrary to the reasoning above. Case CThis comes to a head in #4001 where we're given the class A:
def __init__(self, x: int = 0, **kwargs):
super().__init__(**kwargs) # produces an error, but shouldn't The reasoning here is that But, I think if we take a closer look we'll find that's not the case. The actual problem, to me, seems to be that we cannot statically analyze the call to There may be some way to statically say: # Case C...
A() # no error
A(x=0) # no error
A(y=0) # error But that's a different and probably very complicated issue. It may just be that So, basically, since mypy's handling of tl;dr
|
Thanks for the write up! My personal opinions: Multiple inheritance is a hard thing for type checkers to understand. I think there are already multiple places where mypy bets that you're not using multiple inheritance in order to give you useful errors most of the time. So I'd be happy to leave the Case C false positive unfixed for now or in general / consider special casing I personally agree we should not go out of our way to support passing empty dicts as kwargs in cases where it would only ever work if the dict was empty. That is, I'm happy to compromise on Case B if it gets in the way of providing useful errors. In fact I'd go a little further than your example and say errors in the following cases are valuable:
But we might be in the minority here (or at least, on the wrong side of the do-ocracy). It looks like mypy changed its behaviour for the *args case in #7392 and consistency between args and kwargs seems like a good thing. I see momohatt's thumbs-upped your comment, so it seems like if you opened a PR with your current solution we'd be able to discuss things your solution more concretely / peacefully resolve any incompatibilities with #9629 :-) And Jukka can take the final call on how important Case B support is. @bdarnell What's worse is that for many kinds of
It would be really great if we could infer a TypedDict here. |
I've posted a PR (#9705). I'm continuing this conversation there, since it's only incidentally related to this issue through implementation. |
* Fix compatibility with marshmallow>=3.13.0 * Ignore mypy error due to python/mypy#9676
Workaround the following errors in Galaxy package tests: ``` galaxy/web/framework/helpers/__init__.py:38: error: Argument 4 to "format_timedelta" has incompatible type "**Dict[str, str]"; expected "Literal['year', 'month', 'week', 'day', 'hour', 'minute', 'second']" [arg-type] ...elta(x - datetime.utcnow(), threshold=1, add_direction=True, **kwargs) ^ galaxy/web/framework/helpers/__init__.py:38: error: Argument 4 to "format_timedelta" has incompatible type "**Dict[str, str]"; expected "Literal['narrow', 'short', 'medium', 'long']" [arg-type] ...elta(x - datetime.utcnow(), threshold=1, add_direction=True, **kwargs) ^ ``` These started with the release of Babel 2.12.1, which includes type annotations. xref. python/mypy#9676
Bug Report
The expression
datetime.timedelta(**{"seconds": 1.0})
works correctly in python, but mypy reports it as an error if thepython-version
parameter is 3.6 or greater. (note that the typeshed definition oftimedelta.__init__
is conditioned on the python version, which explains why that parameter has this effect)To Reproduce
test.py
:Run
mypy --python-version 3.5 test.py
andmypy --python-version 3.6 test.py
Actual Behavior
With
--python-version 3.5
, there are no errors. With--python-version 3.6
, it reports:Your Environment
--python-version
mypy.ini
(and other config files): noneMinimal test case: https://repl.it/@bdarnell/mypy-dynamic-keyword-args-bug-report
CI build in which this was initially discovered: https://travis-ci.org/github/tornadoweb/tornado/jobs/740396440
The text was updated successfully, but these errors were encountered: