pytest-mypy icon indicating copy to clipboard operation
pytest-mypy copied to clipboard

v1.0.0: `FileNotFoundError: [Errno 2] No such file or directory` on Windows, Python 3.9

Open Avasam opened this issue 8 months ago • 2 comments

This doesn't affect Python 3.13 or other OSes. Other Python versions on Windows (3.10, 3.11, 3.12) untested.

_____________________________ [mypy] conftest.py ______________________________
[gw0] win32 -- Python 3.9.13 D:\a\setuptools\setuptools\.tox\py\Scripts\python.EXE

cls = <class 'pytest_mypy.MypyResults'>
session = <Session  exitstatus=<ExitCode.OK: 0> testsfailed=0 testscollected=1725>

    @classmethod
    def from_session(cls, session: pytest.Session) -> MypyResults:
        """Load (or generate) cached mypy results for a pytest session."""
        mypy_results_path = session.config.stash[stash_key["config"]].mypy_results_path
        with FileLock(str(mypy_results_path) + ".lock"):
            try:
>               with open(mypy_results_path, mode="rb") as results_f:
E               FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\RUNNER~1\\AppData\\Local\\Temp\\tmpz7dkhc13'

.tox\py\lib\site-packages\pytest_mypy\__init__.py:385: FileNotFoundError

Full logs:

  • https://github.com/pypa/setuptools/actions/runs/14180052613/job/39786912967?pr=4931#step:11:63
  • https://github.com/pypa/distutils/actions/runs/14137799549/job/39613515503?pr=338#step:6:139

Avasam avatar Apr 02 '25 03:04 Avasam

🤔 Looks like this is actual the error:

cls = <class 'pytest_mypy.MypyResults'>
session = <Session  exitstatus=<ExitCode.OK: 0> testsfailed=0 testscollected=1725>

    @classmethod
    def from_session(cls, session: pytest.Session) -> MypyResults:
        """Load (or generate) cached mypy results for a pytest session."""
        mypy_results_path = session.config.stash[stash_key["config"]].mypy_results_path
        with FileLock(str(mypy_results_path) + ".lock"):
            try:
                with open(mypy_results_path, mode="rb") as results_f:
                    results = cls.load(results_f)
            except FileNotFoundError:
                cwd = Path.cwd()
>               results = cls.from_mypy(
                    [
                        item.path.relative_to(cwd)
                        for item in session.items
                        if isinstance(item, MypyFileItem)
                    ],
                )

.tox\py\lib\site-packages\pytest_mypy\__init__.py:389: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

cls = <class 'pytest_mypy.MypyResults'>
paths = [WindowsPath('conftest.py'), WindowsPath('docs/conf.py'), WindowsPath('exercises.py'), WindowsPath('pkg_resources/__in...'), WindowsPath('pkg_resources/tests/__init__.py'), WindowsPath('pkg_resources/tests/test_find_distributions.py'), ...]

    @classmethod
    def from_mypy(
        cls,
        paths: List[Path],
        *,
        opts: Optional[List[str]] = None,
    ) -> MypyResults:
        """Generate results from mypy."""
    
        if opts is None:
            opts = mypy_argv[:]
        args = [str(path) for path in paths]
    
        stdout, stderr, status = mypy.api.run(opts + args)
    
        path_lines: Dict[Optional[Path], List[str]] = {
            path.resolve(): [] for path in paths
        }
        path_lines[None] = []
        for line in stdout.split("\n"):
            if not line:
                continue
>           path = Path(line.partition(":")[0]).resolve()

.tox\py\lib\site-packages\pytest_mypy\__init__.py:363: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = WindowsPath('\x1b[1m\x1b[92mSuccess'), strict = False

    def resolve(self, strict=False):
        """
        Make the path absolute, resolving all symlinks on the way and also
        normalizing it (for example turning slashes into backslashes under
        Windows).
        """
>       s = self._flavour.resolve(self, strict=strict)

C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\pathlib.py:1215: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pathlib._WindowsFlavour object at 0x00000181FCDADC10>
path = WindowsPath('\x1b[1m\x1b[92mSuccess'), strict = False

    def resolve(self, path, strict=False):
        s = str(path)
        if not s:
            return os.getcwd()
        previous_s = None
        if _getfinalpathname is not None:
            if strict:
                return self._ext_to_normal(_getfinalpathname(s))
            else:
                tail_parts = []  # End of the path after the first one not found
                while True:
                    try:
>                       s = self._ext_to_normal(_getfinalpathname(s))
E                       OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect: '\x1b[1m\x1b[92mSuccess'

C:\hostedtoolcache\windows\Python\3.9.13\x64\lib\pathlib.py:215: OSError

dmtucker avatar Apr 02 '25 04:04 dmtucker

Ah...

        for line in stdout.split("\n"):
            if not line:
                continue
>           path = Path(line.partition(":")[0]).resolve()

.tox\py\lib\site-packages\pytest_mypy\__init__.py:363: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = WindowsPath('\x1b[1m\x1b[92mSuccess'), strict = False

Looks like we're taking the success line e.g.

$ mypy demo/good.py 
Success: no issues found in 1 source file

(which is green)

$ MYPY_FORCE_COLOR=1 venv/bin/mypy demo/good.py | cat -v
^[[1m^[[32mSuccess: no issues found in 1 source file^[(B^[[m

and trying to .resolve() it as a Path (fine, but should be try-ed).

Thanks for the report! I'll have a fix tomorrow.

dmtucker avatar Apr 02 '25 04:04 dmtucker

Fixed in v1.0.1

dmtucker avatar Apr 02 '25 23:04 dmtucker