erdewit / nest_asyncio

Patch asyncio to allow nested event loops
BSD 2-Clause "Simplified" License
693 stars 79 forks source link

1.5.1: test is failing in project unit #54

Closed kloczek closed 3 years ago

kloczek commented 3 years ago

Just normal build, install and test cycle used on building package from non-root account:

. F [ 14%] tests/nest_test.py ...... [100%]

================================================================================= FAILURES ================================================================================= test session

cls = <class '_pytest.runner.CallInfo'>, func = <function call_runtest_hook.. at 0x7f589f0019d0>, 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]":
    excinfo = None
    start = timing.time()
    precise_start = timing.perf_counter()
    try:
      result: Optional[TResult] = func()

/usr/lib/python3.8/site-packages/_pytest/runner.py:311:


  lambda: ihook(item=item, **kwds), when=when, reraise=reraise

)

/usr/lib/python3.8/site-packages/_pytest/runner.py:255:


self = <_HookCaller 'pytest_runtest_call'>, args = (), kwargs = {'item': }, notincall = set()

def __call__(self, *args, **kwargs):
    if args:
        raise TypeError("hook calling supports only keyword arguments")
    assert not self.is_historic()
    if self.spec and self.spec.argnames:
        notincall = (
            set(self.spec.argnames) - set(["__multicall__"]) - set(kwargs.keys())
        )
        if notincall:
            warnings.warn(
                "Argument(s) {} which are declared in the hookspec "
                "can not be found in this hook call".format(tuple(notincall)),
                stacklevel=2,
            )
  return self._hookexec(self, self.get_hookimpls(), kwargs)

/usr/lib/python3.8/site-packages/pluggy/hooks.py:286:


self = <_pytest.config.PytestPluginManager object at 0x7f58a5e1e250>, hook = <_HookCaller 'pytest_runtest_call'> methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/lib/python3.8/site-packages/_pytest/runner...=None>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x7f589f402220>>, ...] kwargs = {'item': }

def _hookexec(self, hook, methods, kwargs):
    # called from all hookcaller instances.
    # enable_tracing will set its own wrapping function at self._inner_hookexec
  return self._inner_hookexec(hook, methods, kwargs)

/usr/lib/python3.8/site-packages/pluggy/manager.py:93:


hook = <_HookCaller 'pytest_runtest_call'> methods = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/lib/python3.8/site-packages/_pytest/runner...=None>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x7f589f402220>>, ...] kwargs = {'item': }

self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall( methods, kwargs, firstresult=hook.spec.opts.get("firstresult") if hook.spec else False, )

/usr/lib/python3.8/site-packages/pluggy/manager.py:84:


hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/lib/python3.8/site-packages/_pytest/runner...=None>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x7f589f402220>>, ...] caller_kwargs = {'item': }, firstresult = False

def _multicall(hook_impls, caller_kwargs, firstresult=False):
    """Execute a call into multiple python functions/methods and return the
    result(s).

    ``caller_kwargs`` comes from _HookCaller.__call__().
    """
    __tracebackhide__ = True
    results = []
    excinfo = None
    try:  # run impl and wrapper setup functions in a loop
        teardowns = []
        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(
                                "hook call must provide argument %r" % (argname,)
                            )

                if hook_impl.hookwrapper:
                    try:
                        gen = hook_impl.function(*args)
                        next(gen)  # first yield
                        teardowns.append(gen)
                    except StopIteration:
                        _raise_wrapfail(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:
            excinfo = sys.exc_info()
    finally:
        if firstresult:  # first result hooks return a single value
            outcome = _Result(results[0] if results else None, excinfo)
        else:
            outcome = _Result(results, excinfo)

        # run all wrapper post-yield blocks
        for gen in reversed(teardowns):
            try:
                gen.send(outcome)
                _raise_wrapfail(gen, "has second yield")
            except StopIteration:
                pass
      return outcome.get_result()

/usr/lib/python3.8/site-packages/pluggy/callers.py:208:


self = <pluggy.callers._Result object at 0x7f589efe3a00>

def get_result(self):
    """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
    if self._excinfo is None:
        return self._result
    else:
        ex = self._excinfo
        if _py3:
          raise ex[1].with_traceback(ex[2])

/usr/lib/python3.8/site-packages/pluggy/callers.py:80:


hook_impls = [<HookImpl plugin_name='runner', plugin=<module '_pytest.runner' from '/usr/lib/python3.8/site-packages/_pytest/runner...=None>>, <HookImpl plugin_name='logging-plugin', plugin=<_pytest.logging.LoggingPlugin object at 0x7f589f402220>>, ...] caller_kwargs = {'item': }, firstresult = False

def _multicall(hook_impls, caller_kwargs, firstresult=False):
    """Execute a call into multiple python functions/methods and return the
    result(s).

    ``caller_kwargs`` comes from _HookCaller.__call__().
    """
    __tracebackhide__ = True
    results = []
    excinfo = None
    try:  # run impl and wrapper setup functions in a loop
        teardowns = []
        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(
                                "hook call must provide argument %r" % (argname,)
                            )

                if hook_impl.hookwrapper:
                    try:
                        gen = hook_impl.function(*args)
                        next(gen)  # first yield
                        teardowns.append(gen)
                    except StopIteration:
                        _raise_wrapfail(gen, "did not yield")
                else:
                  res = hook_impl.function(*args)

/usr/lib/python3.8/site-packages/pluggy/callers.py:187:


item =

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
    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
        assert e.__traceback__ is not None
        # Skip *this* frame
        sys.last_traceback = e.__traceback__.tb_next
      raise e

/usr/lib/python3.8/site-packages/_pytest/runner.py:170:


item =

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
    except AttributeError:
        pass
    try:
      item.runtest()

/usr/lib/python3.8/site-packages/_pytest/runner.py:162:


self =

def runtest(self):
  desc = self.get_long_description()

/usr/lib/python3.8/site-packages/pytest_checkdocs/init.py:29:


self =

def get_long_description(self):
  return Description.from_md(ensure_clean(pep517.meta.load('.').metadata))

/usr/lib/python3.8/site-packages/pytest_checkdocs/init.py:60:


root = '.'

def load(root):
    """
    Given a source directory (root) of a package,
    return an importlib.metadata.Distribution object
    with metadata build from that package.
    """
    root = os.path.expanduser(root)
    system = compat_system(root)
    builder = functools.partial(build, source_dir=root, system=system)
  path = Path(build_as_zip(builder))

/usr/lib/python3.8/site-packages/pep517/meta.py:71:


builder = functools.partial(<function build at 0x7f58a2b64700>, source_dir='.', system={'requires': ['setuptools>=42', 'wheel', 'setuptools_scm[toml]>=3.4.3'], 'build-backend': 'setuptools.build_meta'})

def build_as_zip(builder=build):
    with tempdir() as out_dir:
      builder(dest=out_dir)

/usr/lib/python3.8/site-packages/pep517/meta.py:58:


source_dir = '.', dest = '/tmp/tmpaotef6ft', system = {'build-backend': 'setuptools.build_meta', 'requires': ['setuptools>=42', 'wheel', 'setuptools_scm[toml]>=3.4.3']}

def build(source_dir='.', dest=None, system=None):
    system = system or load_system(source_dir)
    dest = os.path.join(source_dir, dest or 'dist')
    mkdir_p(dest)
    validate_system(system)
    hooks = Pep517HookCaller(
        source_dir, system['build-backend'], system.get('backend-path')
    )

    with hooks.subprocess_runner(quiet_subprocess_runner):
        with BuildEnvironment() as env:
            env.pip_install(system['requires'])
          _prep_meta(hooks, env, dest)

/usr/lib/python3.8/site-packages/pep517/meta.py:53:


hooks = <pep517.wrappers.Pep517HookCaller object at 0x7f589ef11130>, env = <pep517.envbuild.BuildEnvironment object at 0x7f589ef114c0>, dest = '/tmp/tmpaotef6ft'

def _prep_meta(hooks, env, dest):
  reqs = hooks.get_requires_for_build_wheel({})

/usr/lib/python3.8/site-packages/pep517/meta.py:28:


self = <pep517.wrappers.Pep517HookCaller object at 0x7f589ef11130>, config_settings = {}

def get_requires_for_build_wheel(self, config_settings=None):
    """Identify packages required for building a wheel

    Returns a list of dependency specifications, e.g.::

        ["wheel >= 0.25", "setuptools"]

    This does not include requirements specified in pyproject.toml.
    It returns the result of calling the equivalently named hook in a
    subprocess.
    """
  return self._call_hook('get_requires_for_build_wheel', {

'config_settings': config_settings })

/usr/lib/python3.8/site-packages/pep517/wrappers.py:168:


self = <pep517.wrappers.Pep517HookCaller object at 0x7f589ef11130>, hook_name = 'get_requires_for_build_wheel', kwargs = {'config_settings': {}}

def _call_hook(self, hook_name, kwargs):
    # On Python 2, pytoml returns Unicode values (which is correct) but the
    # environment passed to check_call needs to contain string values. We
    # convert here by encoding using ASCII (the backend can only contain
    # letters, digits and _, . and : characters, and will be used as a
    # Python identifier, so non-ASCII content is wrong on Python 2 in
    # any case).
    # For backend_path, we use sys.getfilesystemencoding.
    if sys.version_info[0] == 2:
        build_backend = self.build_backend.encode('ASCII')
    else:
        build_backend = self.build_backend
    extra_environ = {'PEP517_BUILD_BACKEND': build_backend}

    if self.backend_path:
        backend_path = os.pathsep.join(self.backend_path)
        if sys.version_info[0] == 2:
            backend_path = backend_path.encode(sys.getfilesystemencoding())
        extra_environ['PEP517_BACKEND_PATH'] = backend_path

    with tempdir() as td:
        hook_input = {'kwargs': kwargs}
        compat.write_json(hook_input, pjoin(td, 'input.json'),
                          indent=2)

        # Run the hook in a subprocess
        with _in_proc_script_path() as script:
            python = self.python_executable
          self._subprocess_runner(

[python, abspath(str(script)), hook_name, td], cwd=self.source_dir, extra_environ=extra_environ )

/usr/lib/python3.8/site-packages/pep517/wrappers.py:265:


cmd = ['/usr/bin/python3', '/usr/lib/python3.8/site-packages/pep517/in_process/_in_process.py', 'get_requires_for_build_wheel', '/tmp/tmpdckmn8mo'] cwd = '/home/tkloczko/rpmbuild/BUILD/nest_asyncio-1.5.1', extra_environ = {'PEP517_BUILD_BACKEND': 'setuptools.build_meta'}

def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None):
    """A method of calling the wrapper subprocess while suppressing output."""
    env = os.environ.copy()
    if extra_environ:
        env.update(extra_environ)
  check_output(cmd, cwd=cwd, env=env, stderr=STDOUT)

/usr/lib/python3.8/site-packages/pep517/wrappers.py:75:


timeout = None, popenargs = (['/usr/bin/python3', '/usr/lib/python3.8/site-packages/pep517/in_process/_in_process.py', 'get_requires_for_build_wheel', '/tmp/tmpdckmn8mo'],) kwargs = {'cwd': '/home/tkloczko/rpmbuild/BUILD/nest_asyncio-1.5.1', 'env': {'AR': '/usr/bin/gcc-ar', 'BASH_FUNC_which%%': '() ...sh-protection -fcf-protection -fdata-sections -ffunction-sections -flto=auto -flto-partition=none', ...}, 'stderr': -2}

def check_output(*popenargs, timeout=None, **kwargs):
    r"""Run command with arguments and return its output.

    If the exit code was non-zero it raises a CalledProcessError.  The
    CalledProcessError object will have the return code in the returncode
    attribute and output in the output attribute.

    The arguments are the same as for the Popen constructor.  Example:

    >>> check_output(["ls", "-l", "/dev/null"])
    b'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'

    The stdout argument is not allowed as it is used internally.
    To capture standard error in the result, use stderr=STDOUT.

    >>> check_output(["/bin/sh", "-c",
    ...               "ls -l non_existent_file ; exit 0"],
    ...              stderr=STDOUT)
    b'ls: non_existent_file: No such file or directory\n'

    There is an additional optional argument, "input", allowing you to
    pass a string to the subprocess's stdin.  If you use this argument
    you may not also use the Popen constructor's "stdin" argument, as
    it too will be used internally.  Example:

    >>> check_output(["sed", "-e", "s/foo/bar/"],
    ...              input=b"when in the course of fooman events\n")
    b'when in the course of barman events\n'

    By default, all communication is in bytes, and therefore any "input"
    should be bytes, and the return value will be bytes.  If in text mode,
    any "input" should be a string, and the return value will be a string
    decoded according to locale encoding, or by "encoding" if set. Text mode
    is triggered by setting any of text, encoding, errors or universal_newlines.
    """
    if 'stdout' in kwargs:
        raise ValueError('stdout argument not allowed, it will be overridden.')

    if 'input' in kwargs and kwargs['input'] is None:
        # Explicitly passing input=None was previously equivalent to passing an
        # empty string. That is maintained here for backwards compatibility.
        if kwargs.get('universal_newlines') or kwargs.get('text'):
            empty = ''
        else:
            empty = b''
        kwargs['input'] = empty
  return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,

**kwargs).stdout

/usr/lib64/python3.8/subprocess.py:415:


input = None, capture_output = False, timeout = None, check = True popenargs = (['/usr/bin/python3', '/usr/lib/python3.8/site-packages/pep517/in_process/_in_process.py', 'get_requires_for_build_wheel', '/tmp/tmpdckmn8mo'],) kwargs = {'cwd': '/home/tkloczko/rpmbuild/BUILD/nest_asyncio-1.5.1', 'env': {'AR': '/usr/bin/gcc-ar', 'BASH_FUNC_which%%': '() ...-fcf-protection -fdata-sections -ffunction-sections -flto=auto -flto-partition=none', ...}, 'stderr': -2, 'stdout': -1} process = <subprocess.Popen object at 0x7f589eeb2a60> stdout = b'Traceback (most recent call last):\n File "/usr/lib/python3.8/site-packages/pep517/in_process/_in_process.py", line...ng pip, instead of https://github.com/user/proj/archive/master.zip use git+https://github.com/user/proj.git#egg=proj\n' stderr = None, retcode = 1

def run(*popenargs,
        input=None, capture_output=False, timeout=None, check=False, **kwargs):
    """Run command with arguments and return a CompletedProcess instance.

    The returned instance will have attributes args, returncode, stdout and
    stderr. By default, stdout and stderr are not captured, and those attributes
    will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.

    If check is True and the exit code was non-zero, it raises a
    CalledProcessError. The CalledProcessError object will have the return code
    in the returncode attribute, and output & stderr attributes if those streams
    were captured.

    If timeout is given, and the process takes too long, a TimeoutExpired
    exception will be raised.

    There is an optional argument "input", allowing you to
    pass bytes or a string to the subprocess's stdin.  If you use this argument
    you may not also use the Popen constructor's "stdin" argument, as
    it will be used internally.

    By default, all communication is in bytes, and therefore any "input" should
    be bytes, and the stdout and stderr will be bytes. If in text mode, any
    "input" should be a string, and stdout and stderr will be strings decoded
    according to locale encoding, or by "encoding" if set. Text mode is
    triggered by setting any of text, encoding, errors or universal_newlines.

    The other arguments are the same as for the Popen constructor.
    """
    if input is not None:
        if kwargs.get('stdin') is not None:
            raise ValueError('stdin and input arguments may not both be used.')
        kwargs['stdin'] = PIPE

    if capture_output:
        if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None:
            raise ValueError('stdout and stderr arguments may not be used '
                             'with capture_output.')
        kwargs['stdout'] = PIPE
        kwargs['stderr'] = PIPE

    with Popen(*popenargs, **kwargs) as process:
        try:
            stdout, stderr = process.communicate(input, timeout=timeout)
        except TimeoutExpired as exc:
            process.kill()
            if _mswindows:
                # Windows accumulates the output in a single blocking
                # read() call run on child threads, with the timeout
                # being done in a join() on those threads.  communicate()
                # _after_ kill() is required to collect that and add it
                # to the exception.
                exc.stdout, exc.stderr = process.communicate()
            else:
                # POSIX _communicate already populated the output so
                # far into the TimeoutExpired exception.
                process.wait()
            raise
        except:  # Including KeyboardInterrupt, communicate handled that.
            process.kill()
            # We don't call process.wait() as .__exit__ does that for us.
            raise
        retcode = process.poll()
        if check and retcode:
          raise CalledProcessError(retcode, process.args,

output=stdout, stderr=stderr) E subprocess.CalledProcessError: Command '['/usr/bin/python3', '/usr/lib/python3.8/site-packages/pep517/in_process/_in_process.py', 'get_requires_for_build_wheel', '/tmp/tmpdckmn8mo']' returned non-zero exit status 1.

/usr/lib64/python3.8/subprocess.py:516: CalledProcessError ========================================================================= short test summary info ========================================================================== FAILED ::project - subprocess.CalledProcessError: Command '['/usr/bin/python3', '/usr/lib/python3.8/site-packages/pep517/in_process/_in_process.py', 'get_requires_for_bu... ======================================================================= 1 failed, 6 passed in 4.32s ========================================================================

erdewit commented 3 years ago

It looks like the 6 tests are passed fine, but there is a failure in "get_requires_for_build_wheel", which is not related to this project.

kloczek commented 3 years ago

So pep517 has some issue?

erdewit commented 3 years ago

I don't know how pep517 gets involved here, i.e. why the pytest runner tries to use it.

Note that this projects uses unittest and running the test suite is done by running

python tests/nest_test.py

or

python setup.py test

from the main directory. This way the pytest runner is not needed.

kloczek commented 3 years ago

Looks like with new pep517 everything is OK.

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-nest_asyncio-1.5.1-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-nest_asyncio-1.5.1-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.11, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
benchmark: 3.4.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
Using --randomly-seed=2676331846
rootdir: /home/tkloczko/rpmbuild/BUILD/nest_asyncio-1.5.1
plugins: forked-1.3.0, shutil-1.7.0, virtualenv-1.7.0, expect-1.1.0, flake8-1.0.7, timeout-1.4.2, betamax-0.8.1, freezegun-0.4.2, aspectlib-1.5.2, toolbox-0.5, rerunfailures-9.1.1, requests-mock-1.9.3, cov-2.12.1, pyfakefs-4.5.0, flaky-3.7.0, benchmark-3.4.1, xdist-2.3.0, pylama-7.7.1, datadir-1.3.1, regressions-2.2.0, cases-3.6.3, xprocess-0.18.1, black-0.3.12, checkdocs-2.7.1, anyio-3.3.0, Faker-8.11.0, asyncio-0.15.1, trio-0.7.0, httpbin-1.0.0, subtests-0.5.0, isort-2.0.0, hypothesis-6.14.6, mock-3.6.1, profiling-1.7.0, randomly-3.8.0
collected 9 items

tests/nest_test.py ....                                                                                                                                              [ 57%]
. .                                                                                                                                                                  [ 71%]
tests/nest_test.py ..                                                                                                                                                [100%]

============================================================================ 7 passed in 7.23s =============================================================================
pytest-xprocess reminder::Be sure to terminate the started process by running 'pytest --xkill' if you have not explicitly done so in your fixture with 'xprocess.getinfo(<process_name>).terminate()'.

Closing.