From 86c5b2183f24e2654ef242aa7ddcffee310153d4 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 8 Sep 2022 16:09:20 +0100 Subject: [PATCH 1/3] Fix custom typeshed dir handling by is_typeshed_file() --- mypy/build.py | 10 +--------- mypy/checker.py | 2 +- mypy/errors.py | 10 ++++++++-- mypy/semanal.py | 4 +++- mypy/semanal_main.py | 12 ++++++++++-- mypy/util.py | 8 ++++++-- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 1720eedaad10..006db108c35c 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -668,16 +668,8 @@ def __init__( raise CompileError( [f"Failed to find builtin module {module}, perhaps typeshed is broken?"] ) - if is_typeshed_file(path): + if is_typeshed_file(options.custom_typeshed_dir, path) or is_stub_package_file(path): continue - if is_stub_package_file(path): - continue - if options.custom_typeshed_dir is not None: - # Check if module lives under custom_typeshed_dir subtree - custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir) - path = os.path.abspath(path) - if os.path.commonpath((path, custom_typeshed_dir)) == custom_typeshed_dir: - continue raise CompileError( [ diff --git a/mypy/checker.py b/mypy/checker.py index 539cd7a443e0..5a20be601bc8 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -387,7 +387,7 @@ def __init__( self.pass_num = 0 self.current_node_deferred = False self.is_stub = tree.is_stub - self.is_typeshed_stub = is_typeshed_file(path) + self.is_typeshed_stub = is_typeshed_file(options.custom_typeshed_dir, path) self.inferred_attribute_types = None # If True, process function definitions. If False, don't. This is used diff --git a/mypy/errors.py b/mypy/errors.py index a6f50ff34de2..916d32c64ba2 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -617,7 +617,10 @@ def clear_errors_in_targets(self, path: str, targets: set[str]) -> None: self.has_blockers.remove(path) def generate_unused_ignore_errors(self, file: str) -> None: - if is_typeshed_file(file) or file in self.ignored_files: + if ( + is_typeshed_file(self.options.custom_typeshed_dir if self.options else None, file) + or file in self.ignored_files + ): return ignored_lines = self.ignored_lines[file] used_ignored_lines = self.used_ignored_lines[file] @@ -658,7 +661,10 @@ def generate_unused_ignore_errors(self, file: str) -> None: def generate_ignore_without_code_errors( self, file: str, is_warning_unused_ignores: bool ) -> None: - if is_typeshed_file(file) or file in self.ignored_files: + if ( + is_typeshed_file(self.options.custom_typeshed_dir if self.options else None, file) + or file in self.ignored_files + ): return used_ignored_lines = self.used_ignored_lines[file] diff --git a/mypy/semanal.py b/mypy/semanal.py index 623f660010f6..bd9c0558428b 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -741,7 +741,9 @@ def file_context( self.cur_mod_id = file_node.fullname with scope.module_scope(self.cur_mod_id): self._is_stub_file = file_node.path.lower().endswith(".pyi") - self._is_typeshed_stub_file = is_typeshed_file(file_node.path) + self._is_typeshed_stub_file = is_typeshed_file( + options.custom_typeshed_dir, file_node.path + ) self.globals = file_node.names self.tvar_scope = TypeVarLikeScope() diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index 406fd93139d1..b74bd6db48e8 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -367,7 +367,11 @@ def check_type_arguments(graph: Graph, scc: list[str], errors: Errors) -> None: for module in scc: state = graph[module] assert state.tree - analyzer = TypeArgumentAnalyzer(errors, state.options, is_typeshed_file(state.path or "")) + analyzer = TypeArgumentAnalyzer( + errors, + state.options, + is_typeshed_file(state.options.custom_typeshed_dir, state.path or ""), + ) with state.wrap_context(): with mypy.state.state.strict_optional_set(state.options.strict_optional): state.tree.accept(analyzer) @@ -381,7 +385,11 @@ def check_type_arguments_in_targets( This mirrors the logic in check_type_arguments() except that we process only some targets. This is used in fine grained incremental mode. """ - analyzer = TypeArgumentAnalyzer(errors, state.options, is_typeshed_file(state.path or "")) + analyzer = TypeArgumentAnalyzer( + errors, + state.options, + is_typeshed_file(state.options.custom_typeshed_dir, state.path or ""), + ) with state.wrap_context(): with mypy.state.state.strict_optional_set(state.options.strict_optional): for target in targets: diff --git a/mypy/util.py b/mypy/util.py index 686a71c4331b..ca2107162b0a 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -769,9 +769,13 @@ def format_error( return self.style(msg, "red", bold=True) -def is_typeshed_file(file: str) -> bool: +def is_typeshed_file(typeshed_dir: str | None, file: str) -> bool: + if typeshed_dir is None: + typeshed = TYPESHED_DIR + else: + typeshed = os.path.abspath(typeshed_dir) try: - return os.path.commonpath((TYPESHED_DIR, os.path.abspath(file))) == TYPESHED_DIR + return os.path.commonpath((typeshed, os.path.abspath(file))) == typeshed except ValueError: # Different drives on Windows return False From e02e8325ca147f0a0a22d8980778e50d45863309 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 8 Sep 2022 19:30:46 +0100 Subject: [PATCH 2/3] Compute abspath once --- mypy/build.py | 2 +- mypy/checker.py | 2 +- mypy/errors.py | 4 ++-- mypy/main.py | 4 ++++ mypy/options.py | 2 ++ mypy/semanal.py | 2 +- mypy/semanal_main.py | 4 ++-- mypy/util.py | 7 ++----- 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index 006db108c35c..4a8094f64b9f 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -668,7 +668,7 @@ def __init__( raise CompileError( [f"Failed to find builtin module {module}, perhaps typeshed is broken?"] ) - if is_typeshed_file(options.custom_typeshed_dir, path) or is_stub_package_file(path): + if is_typeshed_file(options.abs_custom_typeshed_dir, path) or is_stub_package_file(path): continue raise CompileError( diff --git a/mypy/checker.py b/mypy/checker.py index 5a20be601bc8..32b8d5a5a170 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -387,7 +387,7 @@ def __init__( self.pass_num = 0 self.current_node_deferred = False self.is_stub = tree.is_stub - self.is_typeshed_stub = is_typeshed_file(options.custom_typeshed_dir, path) + self.is_typeshed_stub = is_typeshed_file(options.abs_custom_typeshed_dir, path) self.inferred_attribute_types = None # If True, process function definitions. If False, don't. This is used diff --git a/mypy/errors.py b/mypy/errors.py index 916d32c64ba2..00f715a0c4d6 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -618,7 +618,7 @@ def clear_errors_in_targets(self, path: str, targets: set[str]) -> None: def generate_unused_ignore_errors(self, file: str) -> None: if ( - is_typeshed_file(self.options.custom_typeshed_dir if self.options else None, file) + is_typeshed_file(self.options.abs_custom_typeshed_dir if self.options else None, file) or file in self.ignored_files ): return @@ -662,7 +662,7 @@ def generate_ignore_without_code_errors( self, file: str, is_warning_unused_ignores: bool ) -> None: if ( - is_typeshed_file(self.options.custom_typeshed_dir if self.options else None, file) + is_typeshed_file(self.options.abs_custom_typeshed_dir if self.options else None, file) or file in self.ignored_files ): return diff --git a/mypy/main.py b/mypy/main.py index 3dce045be75b..dcae77f24f8a 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -1264,6 +1264,10 @@ def set_strict_flags() -> None: # Enabling an error code always overrides disabling options.disabled_error_codes -= options.enabled_error_codes + # Compute absolute path for custom typeshed (if present). + if options.custom_typeshed_dir is not None: + options.abs_custom_typeshed_dir = os.path.abspath(options.custom_typeshed_dir) + # Set build flags. if special_opts.find_occurrences: state.find_occurrences = special_opts.find_occurrences.split(".") diff --git a/mypy/options.py b/mypy/options.py index fb7bb8e43bbb..b129303c304c 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -77,6 +77,8 @@ def __init__(self) -> None: self.platform = sys.platform self.custom_typing_module: str | None = None self.custom_typeshed_dir: str | None = None + # The abspath() version of the above, we compute it once as an optimization. + self.abs_custom_typeshed_dir: str | None = None self.mypy_path: list[str] = [] self.report_dirs: dict[str, str] = {} # Show errors in PEP 561 packages/site-packages modules diff --git a/mypy/semanal.py b/mypy/semanal.py index bd9c0558428b..4685a9b9da8f 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -742,7 +742,7 @@ def file_context( with scope.module_scope(self.cur_mod_id): self._is_stub_file = file_node.path.lower().endswith(".pyi") self._is_typeshed_stub_file = is_typeshed_file( - options.custom_typeshed_dir, file_node.path + options.abs_custom_typeshed_dir, file_node.path ) self.globals = file_node.names self.tvar_scope = TypeVarLikeScope() diff --git a/mypy/semanal_main.py b/mypy/semanal_main.py index b74bd6db48e8..9e3aeaa7fa4b 100644 --- a/mypy/semanal_main.py +++ b/mypy/semanal_main.py @@ -370,7 +370,7 @@ def check_type_arguments(graph: Graph, scc: list[str], errors: Errors) -> None: analyzer = TypeArgumentAnalyzer( errors, state.options, - is_typeshed_file(state.options.custom_typeshed_dir, state.path or ""), + is_typeshed_file(state.options.abs_custom_typeshed_dir, state.path or ""), ) with state.wrap_context(): with mypy.state.state.strict_optional_set(state.options.strict_optional): @@ -388,7 +388,7 @@ def check_type_arguments_in_targets( analyzer = TypeArgumentAnalyzer( errors, state.options, - is_typeshed_file(state.options.custom_typeshed_dir, state.path or ""), + is_typeshed_file(state.options.abs_custom_typeshed_dir, state.path or ""), ) with state.wrap_context(): with mypy.state.state.strict_optional_set(state.options.strict_optional): diff --git a/mypy/util.py b/mypy/util.py index ca2107162b0a..5bb130c255c4 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -770,12 +770,9 @@ def format_error( def is_typeshed_file(typeshed_dir: str | None, file: str) -> bool: - if typeshed_dir is None: - typeshed = TYPESHED_DIR - else: - typeshed = os.path.abspath(typeshed_dir) + typeshed_dir = typeshed_dir if typeshed_dir is not None else TYPESHED_DIR try: - return os.path.commonpath((typeshed, os.path.abspath(file))) == typeshed + return os.path.commonpath((typeshed_dir, os.path.abspath(file))) == typeshed_dir except ValueError: # Different drives on Windows return False From 90683a6434dd0514df44b6f407a63b2a74eac7c5 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 8 Sep 2022 19:35:19 +0100 Subject: [PATCH 3/3] Black --- mypy/build.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/build.py b/mypy/build.py index 4a8094f64b9f..018a6abcd230 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -668,7 +668,9 @@ def __init__( raise CompileError( [f"Failed to find builtin module {module}, perhaps typeshed is broken?"] ) - if is_typeshed_file(options.abs_custom_typeshed_dir, path) or is_stub_package_file(path): + if is_typeshed_file(options.abs_custom_typeshed_dir, path) or is_stub_package_file( + path + ): continue raise CompileError(