Bug 2479756 - python-invoke fails to build with Python 3.15: AssertionError in Runner_.threading.io_thread_errors_str_has_details
Summary: python-invoke fails to build with Python 3.15: AssertionError in Runner_.thre...
Keywords:
Status: POST
Alias: None
Product: Fedora
Classification: Fedora
Component: python-invoke
Version: rawhide
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Jiri Kucera
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks: PYTHON3.15
TreeView+ depends on / blocked
 
Reported: 2026-05-19 08:54 UTC by Karolina Surma
Modified: 2026-05-21 11:13 UTC (History)
5 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Type: Bug
Embargoed:


Attachments (Terms of Use)

Description Karolina Surma 2026-05-19 08:54:54 UTC
python-invoke fails to build with Python 3.15.0b1.

______________ Runner_.threading.io_thread_errors_str_has_details ______________

self = <runners.Runner_.threading object at 0x7f7e16b7b390>

    def io_thread_errors_str_has_details(self):
        class Oops(_Dummy):
            def handle_stdout(self, **kwargs):
                raise OhNoz()
    
        runner = Oops(Context())
        try:
>           runner.run("nah")

tests/runners.py:953: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../BUILDROOT/usr/lib/python3.15/site-packages/invoke/runners.py:395: in run
    return self._run_body(command, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../BUILDROOT/usr/lib/python3.15/site-packages/invoke/runners.py:451: in _run_body
    return self.make_promise() if self._asynchronous else self._finish()
                                                          ^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <runners.Runner_.threading.io_thread_errors_str_has_details.<locals>.Oops object at 0x7f7e16cb56a0>

    def _finish(self) -> "Result":
        # Wait for subprocess to run, forwarding signals as we get them.
        try:
            while True:
                try:
                    self.wait()
                    break  # done waiting!
                # Don't locally stop on ^C, only forward it:
                # - if remote end really stops, we'll naturally stop after
                # - if remote end does not stop (eg REPL, editor) we don't want
                # to stop prematurely
                except KeyboardInterrupt as e:
                    self.send_interrupt(e)
                # TODO: honor other signals sent to our own process and
                # transmit them to the subprocess before handling 'normally'.
        # Make sure we tie off our worker threads, even if something exploded.
        # Any exceptions that raised during self.wait() above will appear after
        # this block.
        finally:
            # Inform stdin-mirroring worker to stop its eternal looping
            self.program_finished.set()
            # Join threads, storing inner exceptions, & set a timeout if
            # necessary. (Segregate WatcherErrors as they are "anticipated
            # errors" that want to show up at the end during creation of
            # Failure objects.)
            watcher_errors = []
            thread_exceptions = []
            for target, thread in self.threads.items():
                thread.join(self._thread_join_timeout(target))
                exception = thread.exception()
                if exception is not None:
                    real = exception.value
                    if isinstance(real, WatcherError):
                        watcher_errors.append(real)
                    else:
                        thread_exceptions.append(exception)
        # If any exceptions appeared inside the threads, raise them now as an
        # aggregate exception object.
        # NOTE: this is kept outside the 'finally' so that main-thread
        # exceptions are raised before worker-thread exceptions; they're more
        # likely to be Big Serious Problems.
        if thread_exceptions:
>           raise ThreadException(thread_exceptions)
E           invoke.exceptions.ThreadException: 
E           Saw 1 exceptions within threads (OhNoz):
E           
E           
E           Thread args: {
E               'kwargs': {
E                   'buffer_': [],
E                   'hide': False,
E                   'output': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>,
E               },
E               'target': <bound method Runner_.threading.io_thread_errors_str_has_details.<locals>.Oops.handle_stdout of <runners.Runner_.threading.io_thread_errors_str_has_details.<locals>.Oops object at 0x7f7e16cb56a0>>,
E           }
E           
E           Traceback (most recent call last):
E           
E             File "/builddir/build/BUILD/python-invoke-2.2.0-build/BUILDROOT/usr/lib/python3.15/site-packages/invoke/util.py", line 211, in run
E               super().run()
E               ~~~~~~~~~~~^^
E           
E             File "/usr/lib64/python3.15/threading.py", line 1160, in run
E               self._target(*self._args, **self._kwargs)
E               ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E           
E             File "/builddir/build/BUILD/python-invoke-2.2.0-build/invoke-2.2.0/tests/runners.py", line 949, in handle_stdout
E               raise OhNoz()
E           
E           _util.OhNoz

../BUILDROOT/usr/lib/python3.15/site-packages/invoke/runners.py:503: ThreadException

During handling of the above exception, another exception occurred:

self = <runners.Runner_.threading object at 0x7f7e16b7b390>

    def io_thread_errors_str_has_details(self):
        class Oops(_Dummy):
            def handle_stdout(self, **kwargs):
                raise OhNoz()
    
        runner = Oops(Context())
        try:
            runner.run("nah")
        except ThreadException as e:
            message = str(e)
            # Just make sure salient bits appear present, vs e.g. default
            # representation happening instead.
            assert "Saw 1 exceptions within threads" in message
>           assert "{'kwargs': " in message
E           assert "{'kwargs': " in '\nSaw 1 exceptions within threads (OhNoz):\n\n\nThread args: {\n    \'kwargs\': {\n        \'buffer_\': [],\n        ...on-invoke-2.2.0-build/invoke-2.2.0/tests/runners.py", line 949, in handle_stdout\n    raise OhNoz()\n\n_util.OhNoz\n\n'

tests/runners.py:959: AssertionError
=========================== short test summary info ============================
FAILED tests/runners.py::Runner_::threading::io_thread_errors_str_has_details - assert "{'kwargs': " in '\nSaw 1 exceptions within threads (OhNoz):\n\n\nTh...
================== 1 failed, 967 passed, 11 skipped in 4.60s ===================


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

For the build logs, see:
https://copr-be.cloud.fedoraproject.org/results/@python/python3.15-b1/fedora-rawhide-x86_64/10477936-python-invoke/

For all our attempts to build python-invoke with Python 3.15, see:
https://copr.fedorainfracloud.org/coprs/g/python/python3.15-b1/package/python-invoke/

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.15:
https://copr.fedorainfracloud.org/coprs/g/python/python3.15-b1/

Let us know here if you have any questions.

Python 3.15 is planned to be included in Fedora 45.
To make that update smoother, we're building Fedora packages with all pre-releases of Python 3.15.
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 Tomáš Hrnčiar 2026-05-21 11:13:43 UTC
PR: https://src.fedoraproject.org/rpms/python-invoke/pull-request/6


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