Bug 2102736
| Summary: | invoke fails under python 3.11 with AttributeError | ||
|---|---|---|---|
| Product: | [Fedora] Fedora | Reporter: | Dominik 'Rathann' Mierzejewski <dominik> |
| Component: | python-invoke | Assignee: | Jiri Kucera <jkucera> |
| Status: | CLOSED ERRATA | QA Contact: | Fedora Extras Quality Assurance <extras-qa> |
| Severity: | unspecified | Docs Contact: | |
| Priority: | unspecified | ||
| Version: | 37 | CC: | jkucera, mhroncok, thrnciar |
| Target Milestone: | --- | Keywords: | Reopened |
| Target Release: | --- | ||
| Hardware: | Unspecified | ||
| OS: | Unspecified | ||
| Whiteboard: | |||
| Fixed In Version: | python-invoke-1.7.0-5.fc38 python-invoke-1.7.0-6.fc37 | Doc Type: | If docs needed, set a value |
| Doc Text: | Story Points: | --- | |
| Clone Of: | Environment: | ||
| Last Closed: | 2023-02-14 01:52:15 UTC | Type: | Bug |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
| Bug Depends On: | |||
| Bug Blocks: | 2016048, 2098908, 2113626 | ||
|
Description
Dominik 'Rathann' Mierzejewski
2022-06-30 13:48:30 UTC
This bug appears to have been reported against 'rawhide' during the Fedora Linux 37 development cycle. Changing version to 37. s/inspect.getargspec/inspect.getfullargspec/g + necessary code tweaks should fix this. Since %check is disabled due to orphaned pytest-relaxed, I am afraid there can be more errors like this. I'll try to fix and unorphan pytest-relaxed first and then fix this and all remaining flaws. FEDORA-2022-50b50923b0 has been submitted as an update to Fedora 38. https://bodhi.fedoraproject.org/updates/FEDORA-2022-50b50923b0 FEDORA-2022-50b50923b0 has been pushed to the Fedora 38 stable repository. If problem still persists, please make note of it in this bug report. Sorry, didn't mean to close this with my update. Has there been some progress here? Thanks for the fix in Comment 3, Miro. I filed request for python-pytest-relaxed unretirement: bz#2127549 With tests and s/getargspec/getfullargspec/:
+ pytest-3
============================= test session starts ==============================
platform linux -- Python 3.11.0rc2, pytest-7.1.3, pluggy-1.0.0
rootdir: /builddir/build/BUILD/invoke-1.7.0, configfile: pytest.ini, testpaths: tests
plugins: relaxed-1.1.5
collected 980 items
tests/cli.py ...................... [ 2%]
tests/collection.py .................................................... [ 7%]
...................................... [ 11%]
tests/completion.py ........................ [ 13%]
tests/concurrency.py ...... [ 14%]
tests/config.py ........................................................ [ 20%]
............................................................. [ 26%]
tests/context.py .................................s.s....FF.F........... [ 32%]
............................. [ 35%]
tests/executor.py ..................................... [ 38%]
tests/init.py ............................ [ 41%]
tests/loader.py ............... [ 43%]
tests/merge_dicts.py ............ [ 44%]
tests/parser_argument.py ..................s................... [ 48%]
tests/parser_context.py ............................................. [ 52%]
tests/parser_parser.py ................................................. [ 57%]
........... [ 58%]
tests/program.py ....................................................... [ 64%]
...ss.............................................................. [ 71%]
tests/runners.py ....sssssssssssssssssssssssssssssssssssF..sss..ssssssss [ 77%]
..ssssssssssss......ss..sssssssssssssssssssssssssss.........s.sssss.ssss [ 84%]
s............ssssss.ssssss.ss.s..s.......................... [ 90%]
tests/task.py ......s................................................... [ 96%]
............. [ 97%]
tests/terminals.py ......ss [ 98%]
tests/util.py ....... [ 99%]
tests/watchers.py ....... [100%]
=================================== FAILURES ===================================
_ Context_.sudo.auto_response_merges_with_other_responses.kwarg_only_adds_to_kwarg _
self = <context.Context_.sudo.auto_response_merges_with_other_responses object at 0x7f232f5fbb90>
Local = <MagicMock name='Local' id='139789090720208'>
@patch(local_path)
def kwarg_only_adds_to_kwarg(self, Local):
runner = Local.return_value
context = Context()
watcher = self.watcher_klass()
context.sudo("whoami", watchers=[watcher])
# When sudo() called w/ user-specified watchers, we add ours to
# that list
watchers = runner.run.call_args[1]["watchers"]
# Will raise ValueError if not in the list
watchers.remove(watcher)
# Only remaining item in list should be our sudo responder
assert len(watchers) == 1
assert isinstance(watchers[0], FailingResponder)
> assert watchers[0].pattern == self.escaped_prompt
E AttributeError: 'auto_response_merges_with_other_responses' object has no attribute 'escaped_prompt'
tests/context.py:431: AttributeError
_____ Context_.sudo.auto_response_merges_with_other_responses.config_only ______
self = <context.Context_.sudo.auto_response_merges_with_other_responses object at 0x7f232f64db10>
Local = <MagicMock name='Local' id='139789093869200'>
@patch(local_path)
def config_only(self, Local):
runner = Local.return_value
# Set a config-driven list of watchers
watcher = self.watcher_klass()
overrides = {"run": {"watchers": [watcher]}}
config = Config(overrides=overrides)
Context(config=config).sudo("whoami")
# Expect that sudo() extracted that config value & put it into
# the kwarg level. (See comment in sudo() about why...)
watchers = runner.run.call_args[1]["watchers"]
# Will raise ValueError if not in the list
watchers.remove(watcher)
# Only remaining item in list should be our sudo responder
assert len(watchers) == 1
assert isinstance(watchers[0], FailingResponder)
> assert watchers[0].pattern == self.escaped_prompt
E AttributeError: 'auto_response_merges_with_other_responses' object has no attribute 'escaped_prompt'
tests/context.py:449: AttributeError
_ Context_.sudo.auto_response_merges_with_other_responses.both_kwarg_and_config _
self = <context.Context_.sudo.auto_response_merges_with_other_responses object at 0x7f232f64d990>
Local = <MagicMock name='Local' id='139789086725904'>
@patch(local_path)
def both_kwarg_and_config(self, Local):
runner = Local.return_value
# Set a config-driven list of watchers
conf_watcher = self.watcher_klass()
overrides = {"run": {"watchers": [conf_watcher]}}
config = Config(overrides=overrides)
# AND supply a DIFFERENT kwarg-driven list of watchers
kwarg_watcher = self.watcher_klass()
Context(config=config).sudo("whoami", watchers=[kwarg_watcher])
# Expect that the kwarg watcher and our internal one were the
# final result.
watchers = runner.run.call_args[1]["watchers"]
# Will raise ValueError if not in the list. .remove() uses
# identity testing, so two instances of self.watcher_klass will
# be different values here.
watchers.remove(kwarg_watcher)
# Only remaining item in list should be our sudo responder
assert len(watchers) == 1
assert conf_watcher not in watchers # Extra sanity
assert isinstance(watchers[0], FailingResponder)
> assert watchers[0].pattern == self.escaped_prompt
E AttributeError: 'auto_response_merges_with_other_responses' object has no attribute 'escaped_prompt'
tests/context.py:493: AttributeError
__________________ Runner_.command_echoing.uses_custom_format __________________
self = <runners.Runner_.command_echoing object at 0x7f232ed30990>
@trap
def uses_custom_format(self):
> self._run(
"my command",
echo=True,
settings={"run": {"echo_format": "AA{command}ZZ"}},
)
tests/runners.py:385:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/runners.py:138: in _run
return _run(*args, **kwargs)
tests/runners.py:73: in _run
return klass(context).run(*args, **kwargs)
../../BUILDROOT/python-invoke-1.7.0-3.fc38.x86_64/usr/lib/python3.11/site-packages/invoke/runners.py:379: in run
return self._run_body(command, **kwargs)
../../BUILDROOT/python-invoke-1.7.0-3.fc38.x86_64/usr/lib/python3.11/site-packages/invoke/runners.py:441: in _run_body
return self.make_promise() if self._asynchronous else self._finish()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <_util._Dummy object at 0x7f232f1883d0>
def _finish(self):
# 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 six.iteritems(self.threads):
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 (OSError):
E
E
E Thread args: {'kwargs': {'echo': None,
E 'input_': <_pytest.capture.DontReadFromInput object at 0x7f23302703d0>,
E 'output': <pytest_relaxed.trap.CarbonCopy object at 0x7f232eee14e0>},
E 'target': <bound method Runner.handle_stdin of <_util._Dummy object at 0x7f232f1883d0>>}
E
E Traceback (most recent call last):
E
E File "/builddir/build/BUILDROOT/python-invoke-1.7.0-3.fc38.x86_64/usr/lib/python3.11/site-packages/invoke/util.py", line 237, in run
E super(ExceptionHandlingThread, self).run()
E
E File "/usr/lib64/python3.11/threading.py", line 975, in run
E self._target(*self._args, **self._kwargs)
E
E File "/builddir/build/BUILDROOT/python-invoke-1.7.0-3.fc38.x86_64/usr/lib/python3.11/site-packages/invoke/runners.py", line 834, in handle_stdin
E data = self.read_our_stdin(input_)
E ^^^^^^^^^^^^^^^^^^^^^^^^^^^
E
E File "/builddir/build/BUILDROOT/python-invoke-1.7.0-3.fc38.x86_64/usr/lib/python3.11/site-packages/invoke/runners.py", line 793, in read_our_stdin
E bytes_ = input_.read(bytes_to_read(input_))
E ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E
E File "/usr/lib/python3.11/site-packages/_pytest/capture.py", line 192, in read
E raise OSError(
E
E OSError: pytest: reading from stdin while output is captured! Consider using `-s`.
../../BUILDROOT/python-invoke-1.7.0-3.fc38.x86_64/usr/lib/python3.11/site-packages/invoke/runners.py:493: ThreadException
=========================== short test summary info ============================
FAILED tests/context.py::Context_::sudo::auto_response_merges_with_other_responses::kwarg_only_adds_to_kwarg
FAILED tests/context.py::Context_::sudo::auto_response_merges_with_other_responses::config_only
FAILED tests/context.py::Context_::sudo::auto_response_merges_with_other_responses::both_kwarg_and_config
FAILED tests/runners.py::Runner_::command_echoing::uses_custom_format - invok...
=======
It is on the road to rawhide: https://koji.fedoraproject.org/koji/taskinfo?taskID=92628723 Note that the tests failed but it did not stop the build:
+ expect -c 'spawn pytest-3 -s; expect default'
spawn pytest-3 -s
ImportError while loading conftest '/builddir/build/BUILD/invoke-1.7.0/tests/conftest.py'.
tests/conftest.py:14: in <module>
from _util import support
tests/_util.py:20: in <module>
from invoke import Program, Runner
E ModuleNotFoundError: No module named 'invoke'
I suspect the invocation does not pass the non-zero exit code.
This makes the tests run:
%check
-PYTHONDONTWRITEBYTECODE=1 \
-PYTHONPATH=%{buildroot}%{python3_sitelib} \
+export PYTHONDONTWRITEBYTECODE=1
+export PYTHONPATH=%{buildroot}%{python3_sitelib}
But I have no idea how to make %check fail if the tests fail.
I wrote explanatory comment in the wrong place *after* a successful scratch build, fixed now. I also added few lines to the expect script to propagate spawned process' exit code. https://koji.fedoraproject.org/koji/taskinfo?taskID=92631369 Thanks. I verified it actually fails if pytest fails. Leaving open to get the getfullargspec fix propagated to F37. FEDORA-2022-a017e4a880 has been submitted as an update to Fedora 38. https://bodhi.fedoraproject.org/updates/FEDORA-2022-a017e4a880 FEDORA-2022-a017e4a880 has been pushed to the Fedora 38 stable repository. If problem still persists, please make note of it in this bug report. Can we get a fix in F37? invoke is still failing in F37: https://koji.fedoraproject.org/koji/taskinfo?taskID=97002196 . FEDORA-2023-6491d37a85 has been submitted as an update to Fedora 37. https://bodhi.fedoraproject.org/updates/FEDORA-2023-6491d37a85 I forgot to build this after Fedora 37 release, sorry. FEDORA-2023-6491d37a85 has been pushed to the Fedora 37 testing repository. Soon you'll be able to install the update with the following command: `sudo dnf upgrade --enablerepo=updates-testing --refresh --advisory=FEDORA-2023-6491d37a85` You can provide feedback for this update here: https://bodhi.fedoraproject.org/updates/FEDORA-2023-6491d37a85 See also https://fedoraproject.org/wiki/QA:Updates_Testing for more information on how to test updates. FEDORA-2023-6491d37a85 has been pushed to the Fedora 37 stable repository. If problem still persists, please make note of it in this bug report. |