Skip to content
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

Fix crash when annotating callable definition location #3375

Merged
merged 3 commits into from May 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 19 additions & 7 deletions mypy/messages.py
Expand Up @@ -612,13 +612,10 @@ def unexpected_keyword_argument(self, callee: CallableType, name: str,
if callee.name:
msg += ' for {}'.format(callee.name)
self.fail(msg, context)
if callee.definition:
fullname = callee.definition.fullname()
if fullname is not None and '.' in fullname:
module_name = fullname.rsplit('.', 1)[0]
path = self.modules[module_name].path
self.note('{} defined here'.format(callee.name), callee.definition,
file=path, origin=context)
module = find_defining_module(self.modules, callee)
if module:
self.note('{} defined here'.format(callee.name), callee.definition,
file=module.path, origin=context)

def duplicate_argument_value(self, callee: CallableType, index: int,
context: Context) -> None:
Expand Down Expand Up @@ -946,6 +943,21 @@ def callable_name(type: CallableType) -> str:
return 'function'


def find_defining_module(modules: Dict[str, MypyFile], typ: CallableType) -> MypyFile:
if not typ.definition:
return None
fullname = typ.definition.fullname()
if fullname is not None and '.' in fullname:
for i in range(fullname.count('.')):
module_name = fullname.rsplit('.', i + 1)[0]
try:
return modules[module_name]
except KeyError:
pass
assert False, "Couldn't determine module from CallableType"
return None


def temp_message_builder() -> MessageBuilder:
"""Return a message builder usable for throwaway errors (which may not format properly)."""
return MessageBuilder(Errors(), {})
Expand Down
2 changes: 2 additions & 0 deletions mypy/semanal.py
Expand Up @@ -328,6 +328,8 @@ def visit_func_def(self, defn: FuncDef) -> None:
self.function_stack.append(defn)
# First phase of analysis for function.
self.errors.push_function(defn.name())
if not defn._fullname:
defn._fullname = self.qualified_name(defn.name())
if defn.type:
assert isinstance(defn.type, CallableType)
self.update_function_type_variables(defn.type, defn)
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-functions.test
Expand Up @@ -1932,7 +1932,7 @@ main:3: note: "f" defined here

[case testMagicMethodPositionalOnlyArg]
class A(object):
def __eq__(self, other) -> bool: return True # We are all equal.
def __eq__(self, other) -> bool: return True # We are all equal. # N: "__eq__" of "A" defined here

a = A()
a.__eq__(a)
Expand All @@ -1944,7 +1944,7 @@ a.__eq__(other=a) # E: Unexpected keyword argument "other" for "__eq__" of "A"


class A(object):
def __eq__(self, other) -> bool: return True # We are all equal.
def __eq__(self, other) -> bool: return True # We are all equal. # N: "__eq__" of "A" defined here

a = A()
a.__eq__(a)
Expand Down
17 changes: 16 additions & 1 deletion test-data/unit/check-kwargs.test
Expand Up @@ -320,7 +320,7 @@ f(y=0) # E: Unexpected keyword argument "y" for "f"
[case testKeywordArgumentAndCommentSignature2]
import typing
class A:
def f(self, x): # type: (int) -> str
def f(self, x): # type: (int) -> str # N: "f" of "A" defined here
pass
A().f(x='') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "int"
A().f(x=0)
Expand Down Expand Up @@ -359,3 +359,18 @@ f(**c) # E: Keywords must be strings
def f(**k): pass
f(*(1, 2)) # E: Too many arguments for "f"
[builtins fixtures/dict.pyi]

[case testUnexpectedMethodKwargInNestedClass]
class A:
class B:
def __init__(self) -> None: # N: "B" defined here
pass
A.B(x=1) # E: Unexpected keyword argument "x" for "B"

[case testUnexpectedMethodKwargFromOtherModule]
import m
m.A(x=1) # E: Unexpected keyword argument "x" for "A"
[file m.py]
class A:
def __init__(self) -> None: # N: "A" defined here
pass
2 changes: 1 addition & 1 deletion test-data/unit/semanal-classes.test
Expand Up @@ -270,7 +270,7 @@ MypyFile:1(
PassStmt:2()))
AssignmentStmt:3(
NameExpr(g* [m])
NameExpr(f [m]))))
NameExpr(f [__main__.A.f]))))

[case testIfStatementInClassBody]
class A:
Expand Down