diff --git a/mypy/build.py b/mypy/build.py index 1720eedaad10..018a6abcd230 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -668,16 +668,10 @@ 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.abs_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..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(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 a6f50ff34de2..00f715a0c4d6 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.abs_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.abs_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/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 623f660010f6..4685a9b9da8f 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.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 406fd93139d1..9e3aeaa7fa4b 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.abs_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.abs_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..5bb130c255c4 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -769,9 +769,10 @@ 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: + typeshed_dir = typeshed_dir if typeshed_dir is not None else TYPESHED_DIR try: - return os.path.commonpath((TYPESHED_DIR, os.path.abspath(file))) == TYPESHED_DIR + return os.path.commonpath((typeshed_dir, os.path.abspath(file))) == typeshed_dir except ValueError: # Different drives on Windows return False