Bug 2246354 - python-trio fails to build with Python 3.13: TypeError: ('pathmod', <class 'module'>)
Summary: python-trio fails to build with Python 3.13: TypeError: ('pathmod', <class 'm...
Keywords:
Status: CLOSED RAWHIDE
Alias: None
Product: Fedora
Classification: Fedora
Component: python-trio
Version: 40
Hardware: Unspecified
OS: Unspecified
high
high
Target Milestone: ---
Assignee: Miro Hrončok
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks: PYTHON3.13
TreeView+ depends on / blocked
 
Reported: 2023-10-26 11:18 UTC by Karolina Surma
Modified: 2024-06-15 12:12 UTC (History)
6 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed: 2024-06-15 12:12:40 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Fedora Package Sources python-trio pull-request 7 0 None None None 2024-05-24 12:11:55 UTC
Github python-trio trio issues 2903 0 None open Test invocation fails with Python 3.13.0~alpha2 2023-12-12 14:20:57 UTC
Github python-trio trio issues 2944 0 None open [Heads up] Test failures with Python 3.13.0a3 2024-02-02 09:42:33 UTC
Github python-trio trio issues 3004 0 None open Remaining test issues with Python 3.13.0b1: not ki_protected, pathlib.Path.resolve siganture 2024-05-24 12:11:55 UTC

Description Karolina Surma 2023-10-26 11:18:54 UTC
python-trio fails to build with Python 3.13.0a1.

This report is automated and not very verbose, but we'll try to get back here with details.

+ /usr/bin/pytest trio/_core/tests -k 'not test_nursery_cancel_doesnt_create_cyclic_garbage'
ImportError while loading conftest '/builddir/build/BUILD/trio-0.22.0/trio/_core/tests/conftest.py'.
trio/__init__.py:70: in <module>
    from ._path import Path
trio/_path.py:134: in <module>
    class Path(metaclass=AsyncAutoWrapperType):
trio/_path.py:85: in __init__
    type(cls).generate_forwards(cls, attrs)
trio/_path.py:102: in generate_forwards
    raise TypeError(attr_name, type(attr))
E   TypeError: ('pathmod', <class 'module'>)



https://docs.python.org/3.13/whatsnew/3.13.html

For the build logs, see:
https://copr-be.cloud.fedoraproject.org/results/@python/python3.13/fedora-rawhide-x86_64/06559373-python-trio/

For all our attempts to build python-trio with Python 3.13, see:
https://copr.fedorainfracloud.org/coprs/g/python/python3.13/package/python-trio/

Testing and mass rebuild of packages is happening in copr.
You can follow these instructions to test locally in mock if your package builds with Python 3.13:
https://copr.fedorainfracloud.org/coprs/g/python/python3.13/

Let us know here if you have any questions.

Python 3.13 is planned to be included in Fedora 41.
To make that update smoother, we're building Fedora packages with all pre-releases of Python 3.13.
A build failure prevents us from testing all dependent packages (transitive [Build]Requires),
so if this package is required a lot, it's important for us to get it fixed soon.

We'd appreciate help from the people who know this package best,
but if you don't want to work on this now, let us know so we can try to work around it on our side.

Comment 1 Karolina Surma 2023-12-12 11:36:36 UTC
With the current python-trio from Rawhide the failure is slightly different:

+ /usr/bin/pytest --pyargs trio -p trio._tests.pytest_plugin --verbose --skip-optional-imports
Traceback (most recent call last):
  File "/usr/bin/pytest", line 8, in <module>
    sys.exit(console_main())
             ^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 192, in console_main
    code = main()
           ^^^^^^
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 150, in main
    config = _prepareconfig(args, plugins)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 331, in _prepareconfig
    config = pluginmanager.hook.pytest_cmdline_parse(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/pluggy/_hooks.py", line 493, in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/pluggy/_manager.py", line 115, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/pluggy/_callers.py", line 130, in _multicall
    teardown[0].send(outcome)
  File "/usr/lib/python3.13/site-packages/_pytest/helpconfig.py", line 104, in pytest_cmdline_parse
    config: Config = outcome.get_result()
                     ^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/pluggy/_result.py", line 114, in get_result
    raise exc.with_traceback(exc.__traceback__)
  File "/usr/lib/python3.13/site-packages/pluggy/_callers.py", line 77, in _multicall
    res = hook_impl.function(*args)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 1075, in pytest_cmdline_parse
    self.parse(args)
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 1425, in parse
    self._preparse(args, addopts=addopts)
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 1301, in _preparse
    self.pluginmanager.consider_preparse(args, exclude_only=False)
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 709, in consider_preparse
    self.consider_pluginarg(parg)
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 735, in consider_pluginarg
    self.import_plugin(arg, consider_entry_points=True)
  File "/usr/lib/python3.13/site-packages/_pytest/config/__init__.py", line 781, in import_plugin
    __import__(importspec)
  File "/builddir/build/BUILDROOT/python-trio-0.23.1-1.fc40.x86_64/usr/lib/python3.13/site-packages/trio/__init__.py", line 77, in <module>
    from ._path import Path as Path
  File "/builddir/build/BUILDROOT/python-trio-0.23.1-1.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py", line 201, in <module>
    class Path(metaclass=AsyncAutoWrapperType):
  File "/builddir/build/BUILDROOT/python-trio-0.23.1-1.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py", line 145, in __init__
    type(cls).generate_forwards(cls, attrs)
  File "/builddir/build/BUILDROOT/python-trio-0.23.1-1.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py", line 162, in generate_forwards
    raise TypeError(attr_name, type(attr))
TypeError: ('pathmod', <class 'module'>)

Comment 2 Karolina Surma 2024-01-31 14:00:53 UTC
I built trio with this change locally in mock: https://github.com/python-trio/trio/pull/2918

Tests run now, but 8 of them fail.

=================================== FAILURES ===================================
___________________ test_compare_async_stat_methods[is_dir] ____________________

method_name = 'is_dir'

    @pytest.mark.parametrize("method_name", ["is_dir", "is_file"])
    async def test_compare_async_stat_methods(method_name: str) -> None:
>       method, async_method = method_pair(".", method_name)

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:119: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:26: in method_pair
    return getattr(sync_path, method_name), getattr(async_path, method_name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = trio.Path('.'), name = 'is_dir'

    def __getattr__(self, name):
        if name in self._forward:
            value = getattr(self._wrapped, name)
            return rewrap_path(value)
>       raise AttributeError(name)
E       AttributeError: is_dir

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py:236: AttributeError
___________________ test_compare_async_stat_methods[is_file] ___________________

method_name = 'is_file'

    @pytest.mark.parametrize("method_name", ["is_dir", "is_file"])
    async def test_compare_async_stat_methods(method_name: str) -> None:
>       method, async_method = method_pair(".", method_name)

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:119: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:26: in method_pair
    return getattr(sync_path, method_name), getattr(async_path, method_name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = trio.Path('.'), name = 'is_file'

    def __getattr__(self, name):
        if name in self._forward:
            value = getattr(self._wrapped, name)
            return rewrap_path(value)
>       raise AttributeError(name)
E       AttributeError: is_file

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py:236: AttributeError
_________________________ test_forward_methods_rewrap __________________________

path = trio.Path('/tmp/pytest-of-mockbuild/pytest-0/test_forward_methods_rewrap0/test')
tmp_path = PosixPath('/tmp/pytest-of-mockbuild/pytest-0/test_forward_methods_rewrap0')

    async def test_forward_methods_rewrap(path: trio.Path, tmp_path: pathlib.Path) -> None:
        with_name = path.with_name("foo")
>       with_suffix = path.with_suffix(".py")

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:145: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = trio.Path('/tmp/pytest-of-mockbuild/pytest-0/test_forward_methods_rewrap0/test')
name = 'with_suffix'

    def __getattr__(self, name):
        if name in self._forward:
            value = getattr(self._wrapped, name)
            return rewrap_path(value)
>       raise AttributeError(name)
E       AttributeError: with_suffix

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py:236: AttributeError
_____________________ test_forward_methods_without_rewrap ______________________

path = trio.Path('/tmp/pytest-of-mockbuild/pytest-0/test_forward_methods_without_r0')

    async def test_forward_methods_without_rewrap(path: trio.Path) -> None:
        path = await path.parent.resolve()
    
>       assert path.as_uri().startswith("file:///")
E       AttributeError: 'coroutine' object has no attribute 'startswith'

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:160: AttributeError
_______________________________ test_globmethods _______________________________

path = trio.Path('/tmp/pytest-of-mockbuild/pytest-0/test_globmethods0/test')

    async def test_globmethods(path: trio.Path) -> None:
        # Populate a directory tree
        await path.mkdir()
        await (path / "foo").mkdir()
>       await (path / "foo" / "_bar.txt").write_bytes(b"")

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:231: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = trio.Path('/tmp/pytest-of-mockbuild/pytest-0/test_globmethods0/test/foo/_bar.txt')
name = 'write_bytes'

    def __getattr__(self, name):
        if name in self._forward:
            value = getattr(self._wrapped, name)
            return rewrap_path(value)
>       raise AttributeError(name)
E       AttributeError: write_bytes. Did you mean: 'write_text'?

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py:236: AttributeError
_________________________________ test_iterdir _________________________________

path = trio.Path('/tmp/pytest-of-mockbuild/pytest-0/test_iterdir0/test')

    async def test_iterdir(path: trio.Path) -> None:
        # Populate a directory
        await path.mkdir()
        await (path / "foo").mkdir()
>       await (path / "bar.txt").write_bytes(b"")

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:260: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = trio.Path('/tmp/pytest-of-mockbuild/pytest-0/test_iterdir0/test/bar.txt')
name = 'write_bytes'

    def __getattr__(self, name):
        if name in self._forward:
            value = getattr(self._wrapped, name)
            return rewrap_path(value)
>       raise AttributeError(name)
E       AttributeError: write_bytes. Did you mean: 'write_text'?

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_path.py:236: AttributeError
______________________________ test_classmethods _______________________________

    async def test_classmethods() -> None:
>       assert isinstance(await trio.Path.home(), trio.Path)
E       AttributeError: type object 'Path' has no attribute 'home'

../../BUILDROOT/python-trio-0.23.1-4.fc40.x86_64/usr/lib/python3.13/site-packages/trio/_tests/test_path.py:271: AttributeError
_______________________ test_timeouts_raise_value_error ________________________

cls = <class '_pytest.runner.CallInfo'>
func = <function call_runtest_hook.<locals>.<lambda> at 0x7f3b396119e0>
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()

/usr/lib/python3.13/site-packages/_pytest/runner.py:341: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/lib/python3.13/site-packages/_pytest/runner.py:262: in <lambda>
    lambda: ihook(item=item, **kwds), when=when, reraise=reraise
/usr/lib/python3.13/site-packages/pluggy/_hooks.py:493: in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
/usr/lib/python3.13/site-packages/pluggy/_manager.py:115: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/usr/lib/python3.13/site-packages/_pytest/unraisableexception.py:88: in pytest_runtest_call
    yield from unraisable_exception_runtest_hook()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def unraisable_exception_runtest_hook() -> Generator[None, None, None]:
        with catch_unraisable_exception() as cm:
            yield
            if cm.unraisable:
                if cm.unraisable.err_msg is not None:
                    err_msg = cm.unraisable.err_msg
                else:
                    err_msg = "Exception ignored in"
                msg = f"{err_msg}: {cm.unraisable.object!r}\n\n"
                msg += "".join(
                    traceback.format_exception(
                        cm.unraisable.exc_type,
                        cm.unraisable.exc_value,
                        cm.unraisable.exc_traceback,
                    )
                )
>               warnings.warn(pytest.PytestUnraisableExceptionWarning(msg))
E               pytest.PytestUnraisableExceptionWarning: Exception ignored in: <coroutine object Path.as_uri at 0x7f3b393ba140>
E               
E               Traceback (most recent call last):
E                 File "/usr/lib64/python3.13/warnings.py", line 688, in _warn_unawaited_coroutine
E                   warn(msg, category=RuntimeWarning, stacklevel=2, source=coro)
E                   ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E               RuntimeWarning: coroutine 'Path.as_uri' was never awaited

/usr/lib/python3.13/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning
=========================== short test summary info ============================
FAILED _tests/test_path.py::test_compare_async_stat_methods[is_dir] - Attribu...
FAILED _tests/test_path.py::test_compare_async_stat_methods[is_file] - Attrib...
FAILED _tests/test_path.py::test_forward_methods_rewrap - AttributeError: wit...
FAILED _tests/test_path.py::test_forward_methods_without_rewrap - AttributeEr...
FAILED _tests/test_path.py::test_globmethods - AttributeError: write_bytes. D...
FAILED _tests/test_path.py::test_iterdir - AttributeError: write_bytes. Did y...
FAILED _tests/test_path.py::test_classmethods - AttributeError: type object '...
FAILED _tests/test_timeouts.py::test_timeouts_raise_value_error - pytest.Pyte...
================== 8 failed, 579 passed, 75 skipped in 4.26s ===================

Comment 3 Aoife Moloney 2024-02-15 23:02:18 UTC
This bug appears to have been reported against 'rawhide' during the Fedora Linux 40 development cycle.
Changing version to 40.

Comment 4 Karolina Surma 2024-04-25 09:30:06 UTC
Support for Python 3.13 was added in trio 0.25, which requires anyio to be updated to version 4, but we've got some packages which requires anyio < 4 in Fedora:

python-fastapi.src : (python3dist(anyio) < 4~~ with python3dist(anyio) >= 3.2.1)
python-sentry-sdk.src : python3dist(anyio) < 4~~
python-watchgod.src : (python3dist(anyio) < 4~~ with python3dist(anyio) >= 3)

Comment 5 Ben Beasley 2024-04-25 11:56:01 UTC
FastAPI should actually be OK with anyio 4 at runtime: it’s used only in fastapi.concurrency, and the dependency is indirect via Starlette, which *does* support anyio 4. The upper-bound "anyio[trio] >=3.2.1,<4.0.0" is in requirements-tests.txt, and it’s very likely that it can be removed once trio supports anyio; if not, it is very likely that any tests with problems are simple enough to patch. I’m happy to test it against a PR or a COPR with trio 0.25 and anyio 4.

See https://src.fedoraproject.org/rpms/python-anyio/pull-request/9, where I tried to get anyio updated to 4.x but there were just too many things not ready at the time.

In https://src.fedoraproject.org/rpms/python-anyio/pull-request/9#comment-181751, Carl George indicated he would be happy to retire python-watchgod if it can’t be easily made to work with anyio 4, since it’s been replaced upstream by python-watchfiles anyway.

I don’t know python-sentry-sdk as well, but it doesn’t use anyio directly, and the version bound was added over six months ago “because new major 4.0.0 breaks tests.” It would be worth checking if current dependency versions have made this bound unnecessary too.

Comment 6 Ben Beasley 2024-04-26 17:12:13 UTC
Please check https://src.fedoraproject.org/rpms/python-trio/pull-request/6 and https://src.fedoraproject.org/rpms/python-anyio/pull-request/9 for a current analysis. The number of packages blocking anyio 4 and trio 0.25 is now very small.

I don’t have any concrete plans for further work on this in the immediate future, but please let me know if there *is* something I can do to help.

Comment 7 Miro Hrončok 2024-05-23 15:33:38 UTC
We might need to backport https://github.com/python-trio/trio/pull/2955 if we cannot update.

Comment 8 Miro Hrončok 2024-05-23 15:34:07 UTC
I've meant https://github.com/python-trio/trio/pull/2959 (2955 does not work)

Comment 9 Miro Hrončok 2024-05-24 11:23:06 UTC
Looking into it.

Comment 10 Miro Hrončok 2024-05-24 12:11:56 UTC
https://src.fedoraproject.org/rpms/python-trio/pull-request/7 backports that Path refactor but there are more failures, reported upstream as https://github.com/python-trio/trio/issues/3004


Note You need to log in before you can comment on or make changes to this bug.