Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests failing on Windows Python 3.12 #4467

Closed
jaraco opened this issue Jul 10, 2024 · 6 comments
Closed

Tests failing on Windows Python 3.12 #4467

jaraco opened this issue Jul 10, 2024 · 6 comments

Comments

@jaraco
Copy link
Member

jaraco commented Jul 10, 2024

Since the recent updates to ruff and pytest-ruff, the tests are now failing on Windows on Python 3.12 only:

________________________________ test session _________________________________
[gw2] win32 -- Python 3.12.4 D:\a\setuptools\setuptools\.tox\py\Scripts\python.EXE

cls = <class '_pytest.runner.CallInfo'>
func = <function call_and_report.<locals>.<lambda> at 0x000001BB471DE0C0>
when = 'call'
reraise = (<class '_pytest.outcomes.Exit'>, <class 'KeyboardInterrupt'>)

    @classmethod
    def from_call(
        cls,
        func: Callable[[], TResult],
        when: Literal["collect", "setup", "call", "teardown"],
        reraise: Optional[
            Union[Type[BaseException], Tuple[Type[BaseException], ...]]
        ] = None,
    ) -> "CallInfo[TResult]":
        """Call func, wrapping the result in a CallInfo.
    
        :param func:
            The function to call. Called without arguments.
        :param when:
            The phase in which the function is called.
        :param reraise:
            Exception or exceptions that shall propagate if raised by the
            function, instead of being wrapped in the CallInfo.
        """
        excinfo = None
        start = timing.time()
        precise_start = timing.perf_counter()
        try:
>           result: Optional[TResult] = func()

.tox\py\Lib\site-packages\_pytest\runner.py:341: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>       lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise
    )

.tox\py\Lib\site-packages\_pytest\runner.py:241: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <HookCaller 'pytest_runtest_call'>, kwargs = {'item': <RuffItem ruff>}
firstresult = False

    def __call__(self, **kwargs: object) -> Any:
        """Call the hook.
    
        Only accepts keyword arguments, which should match the hook
        specification.
    
        Returns the result(s) of calling all registered plugins, see
        :ref:`calling`.
        """
        assert (
            not self.is_historic()
        ), "Cannot directly call a historic hook - use call_historic instead."
        self._verify_all_args_are_provided(kwargs)
        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
        # Copy because plugins may register other plugins during iteration (#438).
>       return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)

.tox\py\Lib\site-packages\pluggy\_hooks.py:513: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_pytest.config.PytestPluginManager object at 0x000001BB43005E80>
hook_name = 'pytest_runtest_call'
methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _hookexec(
        self,
        hook_name: str,
        methods: Sequence[HookImpl],
        kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        # called from all hookcaller instances.
        # enable_tracing will set its own wrapping function at self._inner_hookexec
>       return self._inner_hookexec(hook_name, methods, kwargs, firstresult)

.tox\py\Lib\site-packages\pluggy\_manager.py:120: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
caller_kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen, hook_impl))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            # Fast path - only new-style wrappers, no Result.
            if only_new_style_wrappers:
                if firstresult:  # first result hooks return a single value
                    result = results[0] if results else None
                else:
                    result = results
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    try:
                        if exception is not None:
                            teardown.throw(exception)  # type: ignore[union-attr]
                        else:
                            teardown.send(result)  # type: ignore[union-attr]
                        # Following is unreachable for a well behaved hook wrapper.
                        # Try to force finalizers otherwise postponed till GC action.
                        # Note: close() may raise if generator handles GeneratorExit.
                        teardown.close()  # type: ignore[union-attr]
                    except StopIteration as si:
                        result = si.value
                        exception = None
                        continue
                    except BaseException as e:
                        exception = e
                        continue
                    _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
    
                if exception is not None:
                    raise exception.with_traceback(exception.__traceback__)
                else:
                    return result
    
            # Slow path - need to support old-style wrappers.
            else:
                if firstresult:  # first result hooks return a single value
                    outcome: Result[object | list[object]] = Result(
                        results[0] if results else None, exception
                    )
                else:
                    outcome = Result(results, exception)
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    if isinstance(teardown, tuple):
                        try:
                            teardown[0].send(outcome)
                        except StopIteration:
                            pass
                        except BaseException as e:
                            _warn_teardown_exception(hook_name, teardown[1], e)
                            raise
                        else:
                            _raise_wrapfail(teardown[0], "has second yield")
                    else:
                        try:
                            if outcome._exception is not None:
                                teardown.throw(outcome._exception)
                            else:
                                teardown.send(outcome._result)
                            # Following is unreachable for a well behaved hook wrapper.
                            # Try to force finalizers otherwise postponed till GC action.
                            # Note: close() may raise if generator handles GeneratorExit.
                            teardown.close()
                        except StopIteration as si:
                            outcome.force_result(si.value)
                            continue
                        except BaseException as e:
                            outcome.force_exception(e)
                            continue
                        _raise_wrapfail(teardown, "has second yield")
    
>               return outcome.get_result()

.tox\py\Lib\site-packages\pluggy\_callers.py:182: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <pluggy._result.Result object at 0x000001BB44D944F0>

    def get_result(self) -> ResultType:
        """Get the result(s) for this hook call.
    
        If the hook was marked as a ``firstresult`` only a single value
        will be returned, otherwise a list of results.
        """
        __tracebackhide__ = True
        exc = self._exception
        if exc is None:
            return cast(ResultType, self._result)
        else:
>           raise exc.with_traceback(exc.__traceback__)

.tox\py\Lib\site-packages\pluggy\_result.py:100: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
caller_kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen, hook_impl))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            # Fast path - only new-style wrappers, no Result.
            if only_new_style_wrappers:
                if firstresult:  # first result hooks return a single value
                    result = results[0] if results else None
                else:
                    result = results
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    try:
                        if exception is not None:
                            teardown.throw(exception)  # type: ignore[union-attr]
                        else:
                            teardown.send(result)  # type: ignore[union-attr]
                        # Following is unreachable for a well behaved hook wrapper.
                        # Try to force finalizers otherwise postponed till GC action.
                        # Note: close() may raise if generator handles GeneratorExit.
                        teardown.close()  # type: ignore[union-attr]
                    except StopIteration as si:
                        result = si.value
                        exception = None
                        continue
                    except BaseException as e:
                        exception = e
                        continue
                    _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
    
                if exception is not None:
                    raise exception.with_traceback(exception.__traceback__)
                else:
                    return result
    
            # Slow path - need to support old-style wrappers.
            else:
                if firstresult:  # first result hooks return a single value
                    outcome: Result[object | list[object]] = Result(
                        results[0] if results else None, exception
                    )
                else:
                    outcome = Result(results, exception)
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    if isinstance(teardown, tuple):
                        try:
                            teardown[0].send(outcome)
                        except StopIteration:
                            pass
                        except BaseException as e:
                            _warn_teardown_exception(hook_name, teardown[1], e)
                            raise
                        else:
                            _raise_wrapfail(teardown[0], "has second yield")
                    else:
                        try:
                            if outcome._exception is not None:
>                               teardown.throw(outcome._exception)

.tox\py\Lib\site-packages\pluggy\_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    @pytest.hookimpl(wrapper=True, tryfirst=True)
    def pytest_runtest_call() -> Generator[None, None, None]:
>       yield from thread_exception_runtest_hook()

.tox\py\Lib\site-packages\_pytest\threadexception.py:87: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def thread_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_threading_exception() as cm:
            try:
>               yield

.tox\py\Lib\site-packages\_pytest\threadexception.py:63: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
caller_kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen, hook_impl))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            # Fast path - only new-style wrappers, no Result.
            if only_new_style_wrappers:
                if firstresult:  # first result hooks return a single value
                    result = results[0] if results else None
                else:
                    result = results
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    try:
                        if exception is not None:
                            teardown.throw(exception)  # type: ignore[union-attr]
                        else:
                            teardown.send(result)  # type: ignore[union-attr]
                        # Following is unreachable for a well behaved hook wrapper.
                        # Try to force finalizers otherwise postponed till GC action.
                        # Note: close() may raise if generator handles GeneratorExit.
                        teardown.close()  # type: ignore[union-attr]
                    except StopIteration as si:
                        result = si.value
                        exception = None
                        continue
                    except BaseException as e:
                        exception = e
                        continue
                    _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
    
                if exception is not None:
                    raise exception.with_traceback(exception.__traceback__)
                else:
                    return result
    
            # Slow path - need to support old-style wrappers.
            else:
                if firstresult:  # first result hooks return a single value
                    outcome: Result[object | list[object]] = Result(
                        results[0] if results else None, exception
                    )
                else:
                    outcome = Result(results, exception)
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    if isinstance(teardown, tuple):
                        try:
                            teardown[0].send(outcome)
                        except StopIteration:
                            pass
                        except BaseException as e:
                            _warn_teardown_exception(hook_name, teardown[1], e)
                            raise
                        else:
                            _raise_wrapfail(teardown[0], "has second yield")
                    else:
                        try:
                            if outcome._exception is not None:
>                               teardown.throw(outcome._exception)

.tox\py\Lib\site-packages\pluggy\_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    @pytest.hookimpl(wrapper=True, tryfirst=True)
    def pytest_runtest_call() -> Generator[None, None, None]:
>       yield from unraisable_exception_runtest_hook()

.tox\py\Lib\site-packages\_pytest\unraisableexception.py:90: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_unraisable_exception() as cm:
            try:
>               yield

.tox\py\Lib\site-packages\_pytest\unraisableexception.py:65: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
caller_kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen, hook_impl))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            # Fast path - only new-style wrappers, no Result.
            if only_new_style_wrappers:
                if firstresult:  # first result hooks return a single value
                    result = results[0] if results else None
                else:
                    result = results
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    try:
                        if exception is not None:
                            teardown.throw(exception)  # type: ignore[union-attr]
                        else:
                            teardown.send(result)  # type: ignore[union-attr]
                        # Following is unreachable for a well behaved hook wrapper.
                        # Try to force finalizers otherwise postponed till GC action.
                        # Note: close() may raise if generator handles GeneratorExit.
                        teardown.close()  # type: ignore[union-attr]
                    except StopIteration as si:
                        result = si.value
                        exception = None
                        continue
                    except BaseException as e:
                        exception = e
                        continue
                    _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
    
                if exception is not None:
                    raise exception.with_traceback(exception.__traceback__)
                else:
                    return result
    
            # Slow path - need to support old-style wrappers.
            else:
                if firstresult:  # first result hooks return a single value
                    outcome: Result[object | list[object]] = Result(
                        results[0] if results else None, exception
                    )
                else:
                    outcome = Result(results, exception)
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    if isinstance(teardown, tuple):
                        try:
                            teardown[0].send(outcome)
                        except StopIteration:
                            pass
                        except BaseException as e:
                            _warn_teardown_exception(hook_name, teardown[1], e)
                            raise
                        else:
                            _raise_wrapfail(teardown[0], "has second yield")
                    else:
                        try:
                            if outcome._exception is not None:
>                               teardown.throw(outcome._exception)

.tox\py\Lib\site-packages\pluggy\_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>
item = <RuffItem ruff>

    @hookimpl(wrapper=True)
    def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]:
        self.log_cli_handler.set_when("call")
    
>       yield from self._runtest_for(item, "call")

.tox\py\Lib\site-packages\_pytest\logging.py:850: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>
item = <RuffItem ruff>, when = 'call'

    def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]:
        """Implement the internals of the pytest_runtest_xxx() hooks."""
        with catching_logs(
            self.caplog_handler,
            level=self.log_level,
        ) as caplog_handler, catching_logs(
            self.report_handler,
            level=self.log_level,
        ) as report_handler:
            caplog_handler.reset()
            report_handler.reset()
            item.stash[caplog_records_key][when] = caplog_handler.records
            item.stash[caplog_handler_key] = caplog_handler
    
            try:
>               yield

.tox\py\Lib\site-packages\_pytest\logging.py:833: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
caller_kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen, hook_impl))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            # Fast path - only new-style wrappers, no Result.
            if only_new_style_wrappers:
                if firstresult:  # first result hooks return a single value
                    result = results[0] if results else None
                else:
                    result = results
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    try:
                        if exception is not None:
                            teardown.throw(exception)  # type: ignore[union-attr]
                        else:
                            teardown.send(result)  # type: ignore[union-attr]
                        # Following is unreachable for a well behaved hook wrapper.
                        # Try to force finalizers otherwise postponed till GC action.
                        # Note: close() may raise if generator handles GeneratorExit.
                        teardown.close()  # type: ignore[union-attr]
                    except StopIteration as si:
                        result = si.value
                        exception = None
                        continue
                    except BaseException as e:
                        exception = e
                        continue
                    _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
    
                if exception is not None:
                    raise exception.with_traceback(exception.__traceback__)
                else:
                    return result
    
            # Slow path - need to support old-style wrappers.
            else:
                if firstresult:  # first result hooks return a single value
                    outcome: Result[object | list[object]] = Result(
                        results[0] if results else None, exception
                    )
                else:
                    outcome = Result(results, exception)
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    if isinstance(teardown, tuple):
                        try:
                            teardown[0].send(outcome)
                        except StopIteration:
                            pass
                        except BaseException as e:
                            _warn_teardown_exception(hook_name, teardown[1], e)
                            raise
                        else:
                            _raise_wrapfail(teardown[0], "has second yield")
                    else:
                        try:
                            if outcome._exception is not None:
>                               teardown.throw(outcome._exception)

.tox\py\Lib\site-packages\pluggy\_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <CaptureManager _method='fd' _global_capturing=<MultiCapture out=<FDCapture 1 oldfd=9 _state='suspended' tmpfile=<_io...._io.TextIOWrapper name='nul' mode='r' encoding='utf-8'>> _state='suspended' _in_suspended=False> _capture_fixture=None>
item = <RuffItem ruff>

    @hookimpl(wrapper=True)
    def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]:
        with self.item_capture("call", item):
>           return (yield)

.tox\py\Lib\site-packages\_pytest\capture.py:878: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
caller_kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen, hook_impl))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
                        res = hook_impl.function(*args)
                        if res is not None:
                            results.append(res)
                            if firstresult:  # halt further impl calls
                                break
            except BaseException as exc:
                exception = exc
        finally:
            # Fast path - only new-style wrappers, no Result.
            if only_new_style_wrappers:
                if firstresult:  # first result hooks return a single value
                    result = results[0] if results else None
                else:
                    result = results
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    try:
                        if exception is not None:
                            teardown.throw(exception)  # type: ignore[union-attr]
                        else:
                            teardown.send(result)  # type: ignore[union-attr]
                        # Following is unreachable for a well behaved hook wrapper.
                        # Try to force finalizers otherwise postponed till GC action.
                        # Note: close() may raise if generator handles GeneratorExit.
                        teardown.close()  # type: ignore[union-attr]
                    except StopIteration as si:
                        result = si.value
                        exception = None
                        continue
                    except BaseException as e:
                        exception = e
                        continue
                    _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
    
                if exception is not None:
                    raise exception.with_traceback(exception.__traceback__)
                else:
                    return result
    
            # Slow path - need to support old-style wrappers.
            else:
                if firstresult:  # first result hooks return a single value
                    outcome: Result[object | list[object]] = Result(
                        results[0] if results else None, exception
                    )
                else:
                    outcome = Result(results, exception)
    
                # run all wrapper post-yield blocks
                for teardown in reversed(teardowns):
                    if isinstance(teardown, tuple):
                        try:
                            teardown[0].send(outcome)
                        except StopIteration:
                            pass
                        except BaseException as e:
                            _warn_teardown_exception(hook_name, teardown[1], e)
                            raise
                        else:
                            _raise_wrapfail(teardown[0], "has second yield")
                    else:
                        try:
                            if outcome._exception is not None:
>                               teardown.throw(outcome._exception)

.tox\py\Lib\site-packages\pluggy\_callers.py:167: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

item = <RuffItem ruff>

    @hookimpl(wrapper=True)
    def pytest_runtest_call(item: Item) -> Generator[None, None, None]:
        xfailed = item.stash.get(xfailed_key, None)
        if xfailed is None:
            item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item)
    
        if xfailed and not item.config.option.runxfail and not xfailed.run:
            xfail("[NOTRUN] " + xfailed.reason)
    
        try:
>           return (yield)

.tox\py\Lib\site-packages\_pytest\skipping.py:257: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

hook_name = 'pytest_runtest_call'
hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from 'D:\\a\\setuptools\\setuptools\\.tox\\py\\Lib\\s...0>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x000001BB44BA6270>>, ...]
caller_kwargs = {'item': <RuffItem ruff>}, firstresult = False

    def _multicall(
        hook_name: str,
        hook_impls: Sequence[HookImpl],
        caller_kwargs: Mapping[str, object],
        firstresult: bool,
    ) -> object | list[object]:
        """Execute a call into multiple python functions/methods and return the
        result(s).
    
        ``caller_kwargs`` comes from HookCaller.__call__().
        """
        __tracebackhide__ = True
        results: list[object] = []
        exception = None
        only_new_style_wrappers = True
        try:  # run impl and wrapper setup functions in a loop
            teardowns: list[Teardown] = []
            try:
                for hook_impl in reversed(hook_impls):
                    try:
                        args = [caller_kwargs[argname] for argname in hook_impl.argnames]
                    except KeyError:
                        for argname in hook_impl.argnames:
                            if argname not in caller_kwargs:
                                raise HookCallError(
                                    f"hook call must provide argument {argname!r}"
                                )
    
                    if hook_impl.hookwrapper:
                        only_new_style_wrappers = False
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            wrapper_gen = cast(Generator[None, Result[object], None], res)
                            next(wrapper_gen)  # first yield
                            teardowns.append((wrapper_gen, hook_impl))
                        except StopIteration:
                            _raise_wrapfail(wrapper_gen, "did not yield")
                    elif hook_impl.wrapper:
                        try:
                            # If this cast is not valid, a type error is raised below,
                            # which is the desired response.
                            res = hook_impl.function(*args)
                            function_gen = cast(Generator[None, object, object], res)
                            next(function_gen)  # first yield
                            teardowns.append(function_gen)
                        except StopIteration:
                            _raise_wrapfail(function_gen, "did not yield")
                    else:
>                       res = hook_impl.function(*args)

.tox\py\Lib\site-packages\pluggy\_callers.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

item = <RuffItem ruff>

    def pytest_runtest_call(item: Item) -> None:
        _update_current_test_var(item, "call")
        try:
            del sys.last_type
            del sys.last_value
            del sys.last_traceback
            if sys.version_info >= (3, 12, 0):
                del sys.last_exc  # type: ignore[attr-defined]
        except AttributeError:
            pass
        try:
            item.runtest()
        except Exception as e:
            # Store trace info to allow postmortem debugging
            sys.last_type = type(e)
            sys.last_value = e
            if sys.version_info >= (3, 12, 0):
                sys.last_exc = e  # type: ignore[attr-defined]
            assert e.__traceback__ is not None
            # Skip *this* frame
            sys.last_traceback = e.__traceback__.tb_next
>           raise e

.tox\py\Lib\site-packages\_pytest\runner.py:183: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

item = <RuffItem ruff>

    def pytest_runtest_call(item: Item) -> None:
        _update_current_test_var(item, "call")
        try:
            del sys.last_type
            del sys.last_value
            del sys.last_traceback
            if sys.version_info >= (3, 12, 0):
                del sys.last_exc  # type: ignore[attr-defined]
        except AttributeError:
            pass
        try:
>           item.runtest()

.tox\py\Lib\site-packages\_pytest\runner.py:173: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <RuffItem ruff>

    def runtest(self):
>       self.handler(path=self.fspath)

.tox\py\Lib\site-packages\pytest_ruff\__init__.py:141: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <RuffItem ruff>
path = local('D:\\a\\setuptools\\setuptools\\setuptools\\command\\build_py.py')

    def handler(self, path):
>       return check_file(path)

.tox\py\Lib\site-packages\pytest_ruff\__init__.py:151: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

path = local('D:\\a\\setuptools\\setuptools\\setuptools\\command\\build_py.py')

    def check_file(path):
        ruff = find_ruff_bin()
        command = [
            ruff,
            "check",
            path,
            "--quiet",
            "--output-format=full",
            "--force-exclude",
        ]
        child = Popen(command, stdout=PIPE, stderr=PIPE)
        stdout, stderr = child.communicate()
    
        if child.returncode == 1:
            raise RuffError(stdout.decode())
    
        if child.returncode == 2:
>           raise RuffError(stderr.decode())
E           pytest_ruff.RuffError: ruff failed
E             Cause: Failed to rename temporary cache file to D:\a\setuptools\setuptools\.ruff_cache\0.5.1\13567325068112760734
E             Cause: failed to persist temporary file: Access is denied. (os error 5)
E             Cause: Access is denied. (os error 5)

.tox\py\Lib\site-packages\pytest_ruff\__init__.py:104: RuffError
path = local('D:\\a\\setuptools\\setuptools\\setuptools\\command\\build_py.py')

    def check_file(path):
        ruff = find_ruff_bin()
        command = [
            ruff,
            "check",
            path,
            "--quiet",
            "--output-format=full",
            "--force-exclude",
        ]
        child = Popen(command, stdout=PIPE, stderr=PIPE)
        stdout, stderr = child.communicate()
    
        if child.returncode == 1:
            raise RuffError(stdout.decode())
    
        if child.returncode == 2:
>           raise RuffError(stderr.decode())
E           pytest_ruff.RuffError: ruff failed
E             Cause: Failed to rename temporary cache file to D:\a\setuptools\setuptools\.ruff_cache\0.5.1\13567325068112760734
E             Cause: failed to persist temporary file: Access is denied. (os error 5)
E             Cause: Access is denied. (os error 5)

.tox\py\Lib\site-packages\pytest_ruff\__init__.py:104: RuffError
@jaraco
Copy link
Member Author

jaraco commented Jul 10, 2024

If I run the tests with -p no:xdist, the errors go away, suggesting there's some interaction between pytest-xdist and pytest-ruff.

@jaraco
Copy link
Member Author

jaraco commented Jul 10, 2024

Wait what? After bringing back xdist, the tests are still passing. It seems that maybe the presence of xdist may not be relevant and it may be due to some state being present or not (maybe .ruff_cache).

@jaraco
Copy link
Member Author

jaraco commented Jul 10, 2024

Okay, so it seems I need to both:

  • remove .ruff_cache, and
  • run with xdist

for the failures to occur. They occur with -k ruff.

If I run pytest -k ruff repeatedly, the failures become less and less until all of the tests have cached results and pass.

I know pytest-ruff 0.4 added support for the returncode == 2, so probably these errors were happening before but were simply silently ignored.

@jaraco
Copy link
Member Author

jaraco commented Jul 10, 2024

My guess is that 13567325068112760734 is some sort of time stamp and due to xdist, it's somehow getting used in parallel across multiple interpreters. My guess is that it only happens on Windows because Unix systems will allow the move even when the destination exists.

@jaraco
Copy link
Member Author

jaraco commented Jul 10, 2024

Confirmed that downgrading to pytest-ruff 0.3.2 bypasses the failure.

@jaraco
Copy link
Member Author

jaraco commented Jul 10, 2024

I'm trying to figure out the best workaround here. Options include:

  • pin to pytest-ruff<0.4 (possibly only on Windows)
  • don't install pytest-ruff only on Windows
  • don't install pytest-xdist only on Windows
  • patch and rely on a patched version of pytest-ruff
  • monkeypatch pytest-ruff

I think I'm going to opt for option 1 for now, since it's the least intrusive and retains the value of running the checks on Windows and keeping the performance benefits of xdist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant