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

Update for compatibility with mypy 0.750 #1058

Merged
merged 2 commits into from
Dec 2, 2019
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
1 change: 1 addition & 0 deletions changes/1057-dmontagu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update mypy to version 0.750
45 changes: 34 additions & 11 deletions pydantic/mypy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from configparser import ConfigParser
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type as TypingType
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type as TypingType, Union

from mypy.errorcodes import ErrorCode
from mypy.nodes import (
Expand All @@ -17,6 +17,7 @@
Context,
Decorator,
EllipsisExpr,
FuncBase,
FuncDef,
JsonDict,
MemberExpr,
Expand All @@ -25,6 +26,7 @@
PlaceholderNode,
RefExpr,
StrExpr,
SymbolNode,
SymbolTableNode,
TempNode,
TypeInfo,
Expand All @@ -47,6 +49,7 @@
TypeVarDef,
TypeVarType,
UnionType,
get_proper_type,
)
from mypy.typevars import fill_typevars
from mypy.util import get_unique_redefinition_name
Expand Down Expand Up @@ -78,7 +81,7 @@ def get_base_class_hook(self, fullname: str) -> 'Optional[Callable[[ClassDefCont
sym = self.lookup_fully_qualified(fullname)
if sym and isinstance(sym.node, TypeInfo): # pragma: no branch
# No branching may occur if the mypy cache has not been cleared
if any(base.fullname() == BASEMODEL_FULLNAME for base in sym.node.mro):
if any(get_fullname(base) == BASEMODEL_FULLNAME for base in sym.node.mro):
return self._pydantic_model_class_maker_callback
return None

Expand Down Expand Up @@ -132,7 +135,7 @@ def from_orm_callback(ctx: MethodContext) -> Type:
return ctx.default_return_type
orm_mode = pydantic_metadata.get('config', {}).get('orm_mode')
if orm_mode is not True:
error_from_orm(model_type.type.name(), ctx.api, ctx.context)
error_from_orm(get_name(model_type.type), ctx.api, ctx.context)
return ctx.default_return_type


Expand Down Expand Up @@ -168,7 +171,7 @@ def transform(self) -> None:
if info[field.name].type is None:
if not ctx.api.final_iteration:
ctx.api.defer()
is_settings = any(base.fullname() == BASESETTINGS_FULLNAME for base in info.mro[:-1])
is_settings = any(get_fullname(base) == BASESETTINGS_FULLNAME for base in info.mro[:-1])
self.add_initializer(fields, config, is_settings)
self.add_construct_method(fields)
self.set_frozen(fields, frozen=config.allow_mutation is False)
Expand Down Expand Up @@ -203,7 +206,7 @@ def collect_config(self) -> 'ModelConfigData':
continue

# Each class depends on the set of fields in its ancestors
ctx.api.add_plugin_dependency(make_wildcard_trigger(info.fullname()))
ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info)))
for name, value in info.metadata[METADATA_KEY]['config'].items():
config.setdefault(name, value)
return config
Expand Down Expand Up @@ -275,7 +278,7 @@ def collect_fields(self, model_config: 'ModelConfigData') -> List['PydanticModel

superclass_fields = []
# Each class depends on the set of fields in its ancestors
ctx.api.add_plugin_dependency(make_wildcard_trigger(info.fullname()))
ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info)))

for name, data in info.metadata[METADATA_KEY]['fields'].items():
if name not in known_fields:
Expand Down Expand Up @@ -357,8 +360,8 @@ def set_frozen(self, fields: List['PydanticModelField'], frozen: bool) -> None:
var = field.to_var(info, use_alias=False)
var.info = info
var.is_property = frozen
var._fullname = info.fullname() + '.' + var.name()
info.names[var.name()] = SymbolTableNode(MDEF, var)
var._fullname = get_fullname(info) + '.' + get_name(var)
info.names[get_name(var)] = SymbolTableNode(MDEF, var)

def get_config_update(self, substmt: AssignmentStmt) -> Optional['ModelConfigData']:
"""
Expand Down Expand Up @@ -396,7 +399,7 @@ def get_is_required(cls: ClassDef, stmt: AssignmentStmt, lhs: NameExpr) -> bool:
expr = stmt.rvalue
if isinstance(expr, TempNode):
# TempNode means annotation-only, so only non-required if Optional
value_type = cls.info[lhs.name].type
value_type = get_proper_type(cls.info[lhs.name].type)
if isinstance(value_type, UnionType) and any(isinstance(item, NoneType) for item in value_type.items):
# Annotated as Optional, or otherwise having NoneType in the union
return False
Expand Down Expand Up @@ -618,7 +621,7 @@ def add_method(
for arg in args:
assert arg.type_annotation, 'All arguments must be fully typed.'
arg_types.append(arg.type_annotation)
arg_names.append(arg.variable.name())
arg_names.append(get_name(arg.variable))
arg_kinds.append(arg.kind)

function_type = ctx.api.named_type('__builtins__.function')
Expand All @@ -631,7 +634,7 @@ def add_method(
func.type = set_callable_name(signature, func)
func.is_class = is_classmethod
# func.is_static = is_staticmethod
func._fullname = info.fullname() + '.' + name
func._fullname = get_fullname(info) + '.' + name
func.line = info.line

# NOTE: we would like the plugin generated node to dominate, but we still
Expand Down Expand Up @@ -661,3 +664,23 @@ def add_method(

info.names[name] = sym
info.defn.defs.body.append(func)


def get_fullname(x: Union[FuncBase, SymbolNode]) -> str:
"""
Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped.
"""
fn = x.fullname
if callable(fn): # pragma: no cover
return fn()
return fn


def get_name(x: Union[FuncBase, SymbolNode]) -> str:
"""
Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped.
"""
fn = x.name
if callable(fn): # pragma: no cover
return fn()
return fn
6 changes: 3 additions & 3 deletions tests/mypy/test_mypy.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ def test_mypy_results(config_filename, python_filename, output_filename):
# Specifying a different cache dir for each configuration dramatically speeds up subsequent execution
# It also prevents cache-invalidation-related bugs in the tests
cache_dir = f'.mypy_cache/test-{config_filename[:-4]}'
actual_result = mypy_api.run(
[full_filename, '--config-file', full_config_filename, '--cache-dir', cache_dir, '--show-error-codes']
)
command = [full_filename, '--config-file', full_config_filename, '--cache-dir', cache_dir, '--show-error-codes']
print(f"\nExecuting: mypy {' '.join(command)}") # makes it easier to debug as necessary
actual_result = mypy_api.run(command)
actual_out, actual_err, actual_returncode = actual_result
# Need to strip filenames due to differences in formatting by OS
actual_out = '\n'.join(['.py:'.join(line.split('.py:')[1:]) for line in actual_out.split('\n') if line]).strip()
Expand Down
2 changes: 1 addition & 1 deletion tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Cython==0.29.14;sys_platform!='win32'
flake8==3.7.9
flake8-quotes==2.1.1
isort==4.3.21
mypy==0.740
mypy==0.750
pycodestyle==2.5.0
pyflakes==2.1.1
pytest==5.3.1
Expand Down