diff --git a/mypy/test/data.py b/mypy/test/data.py index a04c87f60a8d..2bb727e27e71 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -18,7 +18,7 @@ # File modify/create operation: copy module contents from source_path. UpdateFile = NamedTuple('UpdateFile', [('module', str), - ('source_path', str), + ('content', str), ('target_path', str)]) # File delete operation: delete module file. @@ -270,11 +270,35 @@ def setup(self) -> None: self.tmpdir = tempfile.TemporaryDirectory(prefix='mypy-test-') os.chdir(self.tmpdir.name) os.mkdir(test_temp_dir) + + # Precalculate steps for find_steps() + steps: Dict[int, List[FileOperation]] = {} + for path, content in self.files: - dir = os.path.dirname(path) - os.makedirs(dir, exist_ok=True) - with open(path, 'w', encoding='utf8') as f: - f.write(content) + m = re.match(r'.*\.([0-9]+)$', path) + if m: + # Skip writing subsequent incremental steps - rather + # store them as operations. + num = int(m.group(1)) + assert num >= 2 + target_path = re.sub(r'\.[0-9]+$', '', path) + module = module_from_path(target_path) + operation = UpdateFile(module, content, target_path) + steps.setdefault(num, []).append(operation) + else: + # Write the first incremental steps + dir = os.path.dirname(path) + os.makedirs(dir, exist_ok=True) + with open(path, 'w', encoding='utf8') as f: + f.write(content) + + for num, paths in self.deleted_paths.items(): + assert num >= 2 + for path in paths: + module = module_from_path(path) + steps.setdefault(num, []).append(DeleteFile(module, path)) + max_step = max(steps) if steps else 2 + self.steps = [steps.get(num, []) for num in range(2, max_step + 1)] def teardown(self) -> None: assert self.old_cwd is not None and self.tmpdir is not None, \ @@ -312,23 +336,7 @@ def find_steps(self) -> List[List[FileOperation]]: Defaults to having two steps if there aern't any operations. """ - steps: Dict[int, List[FileOperation]] = {} - for path, _ in self.files: - m = re.match(r'.*\.([0-9]+)$', path) - if m: - num = int(m.group(1)) - assert num >= 2 - target_path = re.sub(r'\.[0-9]+$', '', path) - module = module_from_path(target_path) - operation = UpdateFile(module, path, target_path) - steps.setdefault(num, []).append(operation) - for num, paths in self.deleted_paths.items(): - assert num >= 2 - for path in paths: - module = module_from_path(path) - steps.setdefault(num, []).append(DeleteFile(module, path)) - max_step = max(steps) if steps else 2 - return [steps.get(num, []) for num in range(2, max_step + 1)] + return self.steps def module_from_path(path: str) -> str: diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 2d7d5e59430c..fbd44bca868b 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -406,7 +406,7 @@ def split_lines(*streams: bytes) -> List[str]: ] -def copy_and_fudge_mtime(source_path: str, target_path: str) -> None: +def write_and_fudge_mtime(content: str, target_path: str) -> None: # In some systems, mtime has a resolution of 1 second which can # cause annoying-to-debug issues when a file has the same size # after a change. We manually set the mtime to circumvent this. @@ -418,8 +418,10 @@ def copy_and_fudge_mtime(source_path: str, target_path: str) -> None: if os.path.isfile(target_path): new_time = os.stat(target_path).st_mtime + 1 - # Use retries to work around potential flakiness on Windows (AppVeyor). - retry_on_error(lambda: shutil.copy(source_path, target_path)) + dir = os.path.dirname(target_path) + os.makedirs(dir, exist_ok=True) + with open(target_path, "w", encoding="utf-8") as target: + target.write(content) if new_time: os.utime(target_path, times=(new_time, new_time)) @@ -430,7 +432,7 @@ def perform_file_operations( for op in operations: if isinstance(op, UpdateFile): # Modify/create file - copy_and_fudge_mtime(op.source_path, op.target_path) + write_and_fudge_mtime(op.content, op.target_path) else: # Delete file/directory if os.path.isdir(op.path): diff --git a/test-data/unit/daemon.test b/test-data/unit/daemon.test index 862ae57bc096..c73be05e1be3 100644 --- a/test-data/unit/daemon.test +++ b/test-data/unit/daemon.test @@ -274,7 +274,7 @@ bar.py:3: (str) bar.py:4: (arg=str) $ dmypy suggest foo.foo (str) -> int -$ {python} -c "import shutil; shutil.copy('foo.py.2', 'foo.py')" +$ {python} -c "import shutil; shutil.copy('foo2.py', 'foo.py')" $ dmypy check foo.py bar.py bar.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str") == Return code: 1 @@ -284,7 +284,7 @@ def foo(arg): class Bar: def bar(self): pass var = 0 -[file foo.py.2] +[file foo2.py] def foo(arg: str) -> int: return 12 class Bar: diff --git a/test-data/unit/fine-grained-follow-imports.test b/test-data/unit/fine-grained-follow-imports.test index 1bb62adbe552..67b99abaf93f 100644 --- a/test-data/unit/fine-grained-follow-imports.test +++ b/test-data/unit/fine-grained-follow-imports.test @@ -638,6 +638,7 @@ import p2.m2 p1/m1.py:1: error: "int" not callable main.py:2: error: Cannot find implementation or library stub for module named "p2.m2" main.py:2: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports +main.py:2: error: Cannot find implementation or library stub for module named "p2" == p2/m2.py:1: error: "str" not callable p1/m1.py:1: error: "int" not callable