From e2b7218652052ba541b8a2382742aa397b9a6c92 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 31 May 2021 21:53:40 -0400 Subject: [PATCH 1/7] Convert MessageBuilder.disable_errors to a context manager --- mypy/checker.py | 22 ++++++++++------------ mypy/checkexpr.py | 29 ++++++++++------------------- mypy/checkmember.py | 2 +- mypy/messages.py | 13 ++++++++----- 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index dde8a3441c35d..35dd2b246f936 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1995,10 +1995,9 @@ def is_raising_or_empty(self, s: Statement) -> bool: if isinstance(s.expr, EllipsisExpr): return True elif isinstance(s.expr, CallExpr): - self.expr_checker.msg.disable_errors() - typ = get_proper_type(self.expr_checker.accept( - s.expr, allow_none_return=True, always_allow_any=True)) - self.expr_checker.msg.enable_errors() + with self.expr_checker.msg.disable_errors(): + typ = get_proper_type(self.expr_checker.accept( + s.expr, allow_none_return=True, always_allow_any=True)) if isinstance(typ, UninhabitedType): return True @@ -3050,14 +3049,13 @@ def check_member_assignment(self, instance_type: Type, attribute_type: Type, # For non-overloaded setters, the result should be type-checked like a regular assignment. # Hence, we first only try to infer the type by using the rvalue as type context. type_context = rvalue - self.msg.disable_errors() - _, inferred_dunder_set_type = self.expr_checker.check_call( - dunder_set_type, - [TempNode(instance_type, context=context), type_context], - [nodes.ARG_POS, nodes.ARG_POS], - context, object_type=attribute_type, - callable_name=callable_name) - self.msg.enable_errors() + with self.msg.disable_errors(): + _, inferred_dunder_set_type = self.expr_checker.check_call( + dunder_set_type, + [TempNode(instance_type, context=context), type_context], + [nodes.ARG_POS, nodes.ARG_POS], + context, object_type=attribute_type, + callable_name=callable_name) # And now we in fact type check the call, to show errors related to wrong arguments # count, etc., replacing the type context for non-overloaded setters only. diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 3bfe0a306446f..23eab46bea8e6 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1,7 +1,7 @@ """Expression type checker. This file is conceptually part of TypeChecker.""" from mypy.ordered_dict import OrderedDict -from contextlib import contextmanager +from contextlib import contextmanager, nullcontext import itertools from typing import ( Any, cast, Dict, Set, List, Tuple, Callable, Union, Optional, Sequence, Iterator @@ -865,12 +865,11 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str res = [] # type: List[Type] for typ in object_type.relevant_items(): # Member access errors are already reported when visiting the member expression. - self.msg.disable_errors() - item = analyze_member_access(member, typ, e, False, False, False, - self.msg, original_type=object_type, chk=self.chk, - in_literal_context=self.is_literal_context(), - self_type=typ) - self.msg.enable_errors() + with self.msg.disable_errors(): + item = analyze_member_access(member, typ, e, False, False, False, + self.msg, original_type=object_type, chk=self.chk, + in_literal_context=self.is_literal_context(), + self_type=typ) narrowed = self.narrow_type_from_binder(e.callee, item, skip_non_overlapping=True) if narrowed is None: continue @@ -1203,12 +1202,9 @@ def infer_function_type_arguments(self, callee_type: CallableType, # due to partial available context information at this time, but # these errors can be safely ignored as the arguments will be # inferred again later. - self.msg.disable_errors() - - arg_types = self.infer_arg_types_in_context( - callee_type, args, arg_kinds, formal_to_actual) - - self.msg.enable_errors() + with self.msg.disable_errors(): + arg_types = self.infer_arg_types_in_context( + callee_type, args, arg_kinds, formal_to_actual) arg_pass_nums = self.get_arg_infer_passes( callee_type.arg_types, formal_to_actual, len(args)) @@ -2815,13 +2811,8 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: elif e.right_always: left_map = None - if right_map is None: - self.msg.disable_errors() - try: + with (self.msg.disable_errors() if right_map is None else nullcontext()): right_type = self.analyze_cond_branch(right_map, e.right, left_type) - finally: - if right_map is None: - self.msg.enable_errors() if right_map is None: # The boolean expression is statically known to be the left value diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 64e693d52c962..d97f10f14b7e8 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -268,7 +268,7 @@ def analyze_type_type_member_access(name: str, item = None fallback = mx.builtin_type('builtins.type') ignore_messages = mx.msg.copy() - ignore_messages.disable_errors() + ignore_messages.disable_errors().__enter__() if isinstance(typ.item, Instance): item = typ.item elif isinstance(typ.item, AnyType): diff --git a/mypy/messages.py b/mypy/messages.py index 0d10b9bef5d6e..20304a4207350 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -8,13 +8,14 @@ Historically we tried to avoid all message string literals in the type checker but we are moving away from this convention. """ +from contextlib import contextmanager from mypy.ordered_dict import OrderedDict import re import difflib from textwrap import dedent -from typing import cast, List, Dict, Any, Sequence, Iterable, Tuple, Set, Optional, Union +from typing import cast, List, Dict, Any, Sequence, Iterable, Tuple, Set, Optional, Union, ContextManager from typing_extensions import Final from mypy.erasetype import erase_type @@ -136,11 +137,13 @@ def add_errors(self, messages: 'MessageBuilder') -> None: for info in errs: self.errors.add_error_info(info) - def disable_errors(self) -> None: + @contextmanager + def disable_errors(self) -> ContextManager[None]: self.disable_count += 1 - - def enable_errors(self) -> None: - self.disable_count -= 1 + try: + yield + finally: + self.disable_count -= 1 def is_errors(self) -> bool: return self.errors.is_errors() From 71477699691452479deb23e1d8ca17d0c4393be4 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 31 May 2021 22:22:24 -0400 Subject: [PATCH 2/7] Fix disable_errors typing --- mypy/messages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 20304a4207350..67206819b9a71 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -15,7 +15,7 @@ import difflib from textwrap import dedent -from typing import cast, List, Dict, Any, Sequence, Iterable, Tuple, Set, Optional, Union, ContextManager +from typing import cast, List, Dict, Any, Sequence, Iterable, Tuple, Set, Optional, Union from typing_extensions import Final from mypy.erasetype import erase_type @@ -138,7 +138,7 @@ def add_errors(self, messages: 'MessageBuilder') -> None: self.errors.add_error_info(info) @contextmanager - def disable_errors(self) -> ContextManager[None]: + def disable_errors(self) -> Iterable[None]: self.disable_count += 1 try: yield From cb1ebcd4e32147bd76205ab0ab63431aade5a60a Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 31 May 2021 22:24:53 -0400 Subject: [PATCH 3/7] Move comment about error suppression to the right place --- mypy/checkexpr.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 23eab46bea8e6..98c6051b780d5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2794,10 +2794,6 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: if left_map is None: self.msg.redundant_left_operand(e.op, e.left) - # If right_map is None then we know mypy considers the right branch - # to be unreachable and therefore any errors found in the right branch - # should be suppressed. - # # Note that we perform these checks *before* we take into account # the analysis from the semanal phase below. We assume that nodes # marked as unreachable during semantic analysis were done so intentionally. @@ -2811,6 +2807,9 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: elif e.right_always: left_map = None + # If right_map is None then we know mypy considers the right branch + # to be unreachable and therefore any errors found in the right branch + # should be suppressed. with (self.msg.disable_errors() if right_map is None else nullcontext()): right_type = self.analyze_cond_branch(right_map, e.right, left_type) From 1aee3266bd21b385695ddaa2ed3bd00c60d63b4e Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 31 May 2021 22:42:57 -0400 Subject: [PATCH 4/7] add shim for nullcontext --- mypy/checkexpr.py | 3 ++- mypy/nullcontext.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 mypy/nullcontext.py diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 98c6051b780d5..de6d9a5943fbf 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1,7 +1,8 @@ """Expression type checker. This file is conceptually part of TypeChecker.""" +from mypy.nullcontext import nullcontext from mypy.ordered_dict import OrderedDict -from contextlib import contextmanager, nullcontext +from contextlib import contextmanager import itertools from typing import ( Any, cast, Dict, Set, List, Tuple, Callable, Union, Optional, Sequence, Iterator diff --git a/mypy/nullcontext.py b/mypy/nullcontext.py new file mode 100644 index 0000000000000..805350261c518 --- /dev/null +++ b/mypy/nullcontext.py @@ -0,0 +1,10 @@ +import sys +from contextlib import contextmanager +from typing import Iterator + +if sys.version_info < (3, 7): + @contextmanager + def nullcontext() -> Iterator[None]: + yield +else: + from contextlib import nullcontext as nullcontext, contextmanager # noqa: F401 From 6f72bb0c91f587b0f4e7725499e3c9e207b56986 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Mon, 31 May 2021 23:28:42 -0400 Subject: [PATCH 5/7] Iterable -> Iterator --- mypy/messages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 67206819b9a71..491eb16e82a56 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -15,7 +15,7 @@ import difflib from textwrap import dedent -from typing import cast, List, Dict, Any, Sequence, Iterable, Tuple, Set, Optional, Union +from typing import cast, List, Dict, Any, Sequence, Iterable, Iterator, Tuple, Set, Optional, Union from typing_extensions import Final from mypy.erasetype import erase_type @@ -138,7 +138,7 @@ def add_errors(self, messages: 'MessageBuilder') -> None: self.errors.add_error_info(info) @contextmanager - def disable_errors(self) -> Iterable[None]: + def disable_errors(self) -> Iterator[None]: self.disable_count += 1 try: yield From 824b205cdf123e29bf60e195dc83287928277c13 Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Tue, 1 Jun 2021 12:18:44 -0400 Subject: [PATCH 6/7] move to mypy.backports (also OrderedDict) --- mypy/backports.py | 18 ++++++++++++++++++ mypy/checkexpr.py | 3 +-- mypy/errors.py | 2 +- mypy/join.py | 2 +- mypy/meet.py | 2 +- mypy/messages.py | 2 +- mypy/nodes.py | 2 +- mypy/options.py | 2 +- mypy/ordered_dict.py | 9 --------- mypy/plugins/attrs.py | 2 +- mypy/semanal_typeddict.py | 2 +- mypy/type_visitor.py | 2 +- mypy/typeanal.py | 2 +- mypy/types.py | 2 +- mypyc/codegen/emit.py | 2 +- mypyc/codegen/emitclass.py | 2 +- mypyc/codegen/emitmodule.py | 2 +- mypyc/ir/class_ir.py | 2 +- mypyc/irbuild/builder.py | 2 +- mypyc/irbuild/main.py | 2 +- mypyc/test/test_emitfunc.py | 2 +- mypyc/test/test_serialization.py | 2 +- 22 files changed, 38 insertions(+), 30 deletions(-) create mode 100644 mypy/backports.py diff --git a/mypy/backports.py b/mypy/backports.py new file mode 100644 index 0000000000000..954c01e5e7c7f --- /dev/null +++ b/mypy/backports.py @@ -0,0 +1,18 @@ +import sys +from contextlib import contextmanager +from typing import Iterator + +if sys.version_info < (3, 6): + from collections import OrderedDict as OrderedDict +else: + # OrderedDict is kind of slow, so for most of our uses in Python 3.6 + # and later we'd rather just use dict + OrderedDict = dict + + +if sys.version_info < (3, 7): + @contextmanager + def nullcontext() -> Iterator[None]: + yield +else: + from contextlib import nullcontext as nullcontext diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index de6d9a5943fbf..f24b8dfc8ff93 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1,7 +1,6 @@ """Expression type checker. This file is conceptually part of TypeChecker.""" -from mypy.nullcontext import nullcontext -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict, nullcontext from contextlib import contextmanager import itertools from typing import ( diff --git a/mypy/errors.py b/mypy/errors.py index b3f99916b244d..e4cbcca36798c 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -1,7 +1,7 @@ import os.path import sys import traceback -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from collections import defaultdict from typing import Tuple, List, TypeVar, Set, Dict, Optional, TextIO, Callable diff --git a/mypy/join.py b/mypy/join.py index 53a1fce973dc1..2b574fa740a98 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -1,6 +1,6 @@ """Calculation of the least upper bound types (joins).""" -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import List, Optional from mypy.types import ( diff --git a/mypy/meet.py b/mypy/meet.py index 558de6ec92c9c..9d93c47832e40 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -1,4 +1,4 @@ -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import List, Optional, Tuple, Callable from mypy.join import ( diff --git a/mypy/messages.py b/mypy/messages.py index 491eb16e82a56..e2f40f3a188b2 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -10,7 +10,7 @@ """ from contextlib import contextmanager -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict import re import difflib from textwrap import dedent diff --git a/mypy/nodes.py b/mypy/nodes.py index a50c76ca06cd6..c849578c599e1 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2,7 +2,7 @@ import os from abc import abstractmethod -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from collections import defaultdict from typing import ( Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional, Callable, Sequence, Iterator diff --git a/mypy/options.py b/mypy/options.py index 9e41ad7d310d9..1e5d0d52f4975 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -1,4 +1,4 @@ -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict import re import pprint import sys diff --git a/mypy/ordered_dict.py b/mypy/ordered_dict.py index f1e78ac242f70..e69de29bb2d1d 100644 --- a/mypy/ordered_dict.py +++ b/mypy/ordered_dict.py @@ -1,9 +0,0 @@ -# OrderedDict is kind of slow, so for most of our uses in Python 3.6 -# and later we'd rather just use dict - -import sys - -if sys.version_info < (3, 6): - from collections import OrderedDict as OrderedDict -else: - OrderedDict = dict diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 0c4b4b0c701f1..8e3f77527d945 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1,6 +1,6 @@ """Plugin for supporting the attrs library (http://www.attrs.org)""" -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import Optional, Dict, List, cast, Tuple, Iterable from typing_extensions import Final diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 0b0891d83fb65..14e62b5929bd1 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -1,6 +1,6 @@ """Semantic analysis of TypedDict definitions.""" -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import Optional, List, Set, Tuple from typing_extensions import Final diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index a0e6299a5a8a0..aa61e58d4c67c 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -12,7 +12,7 @@ """ from abc import abstractmethod -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import Generic, TypeVar, cast, Any, List, Callable, Iterable, Optional, Set, Sequence from mypy_extensions import trait, mypyc_attr diff --git a/mypy/typeanal.py b/mypy/typeanal.py index d9e7764ba3f8e..084d955f7c08d 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -3,7 +3,7 @@ import itertools from itertools import chain from contextlib import contextmanager -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import Callable, List, Optional, Set, Tuple, Iterator, TypeVar, Iterable, Sequence from typing_extensions import Final diff --git a/mypy/types.py b/mypy/types.py index d9c71fbcf7ee3..7aa83a75f431b 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3,7 +3,7 @@ import copy import sys from abc import abstractmethod -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import ( Any, TypeVar, Dict, List, Tuple, cast, Set, Optional, Union, Iterable, NamedTuple, diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index e8b3c77be9d65..769888e5ad6ef 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -1,6 +1,6 @@ """Utilities for emitting C code.""" -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import List, Set, Dict, Optional, Callable, Union, Tuple import sys diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 0cfaab07b5730..080e72ce3dcd6 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -2,7 +2,7 @@ from typing import Optional, List, Tuple, Dict, Callable, Mapping, Set -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from mypyc.common import PREFIX, NATIVE_PREFIX, REG_PREFIX, use_fastcall from mypyc.codegen.emit import Emitter, HeaderDeclaration diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 476c435712bd4..ec89aa767ca1a 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -5,7 +5,7 @@ import os import json -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import List, Tuple, Dict, Iterable, Set, TypeVar, Optional from mypy.nodes import MypyFile diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index aeb0f8410c56c..6577c9a77e952 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -1,7 +1,7 @@ """Intermediate representation of classes.""" from typing import List, Optional, Set, Tuple, Dict, NamedTuple -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from mypyc.common import JsonDict from mypyc.ir.ops import Value, DeserMaps diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 0b2a6537c5ea7..e1a82287fe40a 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -13,7 +13,7 @@ from typing import Callable, Dict, List, Tuple, Optional, Union, Sequence, Set, Any from typing_extensions import overload -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from mypy.build import Graph from mypy.nodes import ( diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py index 457b535cdbfe1..c11ff967f357b 100644 --- a/mypyc/irbuild/main.py +++ b/mypyc/irbuild/main.py @@ -20,7 +20,7 @@ def f(x: int) -> int: below, mypyc.irbuild.builder, and mypyc.irbuild.visitor. """ -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from typing import List, Dict, Callable, Any, TypeVar, cast from mypy.nodes import MypyFile, Expression, ClassDef diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 11f46fc469190..5503c09d3c667 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -2,7 +2,7 @@ from typing import List, Optional -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from mypy.test.helpers import assert_string_arrays_equal diff --git a/mypyc/test/test_serialization.py b/mypyc/test/test_serialization.py index 338be1aedb85e..5737ccc7b6238 100644 --- a/mypyc/test/test_serialization.py +++ b/mypyc/test/test_serialization.py @@ -4,7 +4,7 @@ # contain its own tests so that pytest will rewrite the asserts... from typing import Any, Dict, Tuple -from mypy.ordered_dict import OrderedDict +from mypy.backports import OrderedDict from collections.abc import Iterable from mypyc.ir.ops import DeserMaps From e86b3ec7161c44a5228572c7a027f49df4716c6a Mon Sep 17 00:00:00 2001 From: Ilya Konstantinov Date: Tue, 1 Jun 2021 14:21:59 -0400 Subject: [PATCH 7/7] add noqa: F401 --- mypy/backports.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/backports.py b/mypy/backports.py index 954c01e5e7c7f..df5afcb2416f7 100644 --- a/mypy/backports.py +++ b/mypy/backports.py @@ -3,7 +3,7 @@ from typing import Iterator if sys.version_info < (3, 6): - from collections import OrderedDict as OrderedDict + from collections import OrderedDict as OrderedDict # noqa: F401 else: # OrderedDict is kind of slow, so for most of our uses in Python 3.6 # and later we'd rather just use dict @@ -15,4 +15,4 @@ def nullcontext() -> Iterator[None]: yield else: - from contextlib import nullcontext as nullcontext + from contextlib import nullcontext as nullcontext # noqa: F401