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

6.2.4: pytest is failing when setuptools produces DSO module #8731

Closed
kloczek opened this issue Jun 5, 2021 · 37 comments
Closed

6.2.4: pytest is failing when setuptools produces DSO module #8731

kloczek opened this issue Jun 5, 2021 · 37 comments

Comments

@kloczek
Copy link
Contributor

kloczek commented Jun 5, 2021

It is quite regular cases and there are many such modules which are producucing dururing python setup.py build command DSO loadable module for example using cython. As I wrote it is quite regular that such modules are failing on testing using pytest because pytest cannot locate DSO module.
Such example could be yarl (https://github.com/aio-libs/yarl/) but there are many more of them
To reproduce could be used below script:

wget -O - https://github.com/aio-libs/yarl//archive/v1.6.3/yarl-1.6.3.tar.gz| tar xz
cd yarl-1.6.3
python3 -m cython -3 -o yarl/_quoting_c.c yarl/_quoting_c.pyx -I yarl
python3 setup.py  build
python3 -Bm pytest -ra

In my case above fails with:

 python3 -Bm pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
Using --randomly-seed=3950045766
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/tkloczko/rpmbuild/BUILD/yarl-1.6.3/yarl-1.6.3/.hypothesis/examples')
rootdir: /home/tkloczko/rpmbuild/BUILD/yarl-1.6.3/yarl-1.6.3, configfile: setup.cfg, testpaths: tests/
plugins: forked-1.3.0, shutil-1.7.0, virtualenv-1.7.0, expect-1.1.0, httpbin-1.0.0, xdist-2.2.1, flake8-1.0.7, timeout-1.4.2, betamax-0.8.1, freezegun-0.4.2, cases-3.4.6, case-1.5.3, isort-1.3.0, aspectlib-1.5.2, asyncio-0.15.1, toolbox-0.5, xprocess-0.17.1, aiohttp-0.3.0, checkdocs-2.7.0, mock-3.6.1, rerunfailures-9.1.1, requests-mock-1.9.3, Faker-8.4.0, cov-2.12.1, randomly-3.8.0, pyfakefs-4.5.0, hypothesis-6.13.14
collected 498 items / 1 error / 497 selected
Coverage.py warning: No data was collected. (no-data-collected)

================================================================================== ERRORS ==================================================================================
__________________________________________________________________ ERROR collecting tests/test_quoting.py __________________________________________________________________
ImportError while importing test module '/home/tkloczko/rpmbuild/BUILD/yarl-1.6.3/yarl-1.6.3/tests/test_quoting.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib64/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_quoting.py:9: in <module>
    from yarl._quoting_c import _Quoter as _CQuoter, _Unquoter as _CUnquoter
E   ModuleNotFoundError: No module named 'yarl._quoting_c'

----------- coverage: platform linux, python 3.8.9-final-0 -----------
Name                  Stmts   Miss  Cover
-----------------------------------------
yarl/__init__.py          3      3     0%
yarl/_quoting.py         10     10     0%
yarl/_quoting_py.py     155    155     0%
yarl/_url.py            569    569     0%
-----------------------------------------
TOTAL                   737    737     0%

========================================================================= short test summary info ==========================================================================
ERROR tests/test_quoting.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================================= 1 error in 1.34s =============================================================================

After build it is produces build/lib.-/yarl/_quoting_c.cpython-*.so

[tkloczko@barrel yarl-1.6.3]$ ls -l build/lib.linux-x86_64-3.8/yarl/_quoting_c.cpython-38-x86_64-linux-gnu.so
-rwxrwxr-x 1 tkloczko tkloczko 475824 Jun  5 07:13 build/lib.linux-x86_64-3.8/yarl/_quoting_c.cpython-38-x86_64-linux-gnu.so

Something is missing in such cases or it some bug in pytest?
Because it is very common case I suppose that it could be something wrong with pytest (?)

@The-Compiler
Copy link
Member

I can't reproduce - can you please try to reproduce (and ideally also provide reproduction steps) based on a clean virtualenv?

@RonnyPfannschmidt
Copy link
Member

The build command has a inplace option to manage this, same goes for editable installs

IMHO no pytest issue

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

I think that this pytest issue may shares some root cause with failing setuptools build_sphinx when module builds DSOs and sphinx needs access to not only .py but DSO files.
Example of the such issue python-pillow/Pillow#5525.
In this case it is about not testing but use build_sphinx setuptool command.
I think that testing and building documentation when sphinx requires access to just build DSOs are sharing the same root cause.

AFAIK pytest is reading setup.cfg in which (I suppose) should be some paths to just build resources.
As pytest reads setup.cfg directly build_sphins should receive from setuptools proper env with $PYTHONPATH pointing on build directory (which by default is build/lib-<os>.<arch>/ which is not always the same)

In other words in this case it may not be pytest per se issue but more setuptools.

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

I can't reproduce - can you please try to reproduce (and ideally also provide reproduction steps) based on a clean virtualenv?

In this case it is not about virtual env. It is about fact that pytest is not able to locate where are DSO modules.
There is no such problem in case modules which are producing only .py files because AFAIK pytest is by default trying to find files in $PWD/<module_name> directory.
setup.py build command when DSO module is build puts all files in build/lib-<os>.<arch>/ directory.
AFAIK tox by default grabs content of the build/lib-<os>.<arch>/ and copies or add symlinks to own base build env sdirectory.
As pytest is kind of in-place testing framework it shouild know about build/lib-<os>.<arch>/ or at least should receive that directory from setuptools settings.
I have no precise knowledge about setuptools, pytest and tox because I'm still learning about those frameworks however I think that at least I'm pointing in right direction about possible root cause.

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

IMHO no pytest issue

Basing on IMO similar case with setuptools build_sphinx command issue I suppose that you may be right.
If pytest expects that setuptools should deliver $PYTHONPATH=$PWD/buikd/lib-.` this could be setuptools issue.
However I have no precise knowledge about pytest<>setuptools interactions :/

@RonnyPfannschmidt
Copy link
Member

The general suggestion is that if you want to to test either install, build inplace or do a editableinstall

Thereis no pytest to setuptools integration a spart of pytest

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

The general suggestion is that if you want to to test either install, build inplace or do a editableinstall

Thereis no pytest to setuptools integration a spart of pytest

That is OK. May I ask how it should be done?

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

Another observation.
On Building my packages when I'm using pytest typical sequence is like below:

%build
%py3_build

%install
%py3_install

%check
%pytest

All is executet in order: forst %build, %insytall and than %check.
%install prapares all files in buildroot and %pytest macro is using that path

[tkloczko@barrel SPECS]$ rpm -E %pytest
[.not relevant part.]

        PATH="/home/tkloczko/rpmbuild/BUILDROOT/%{NAME}-%{VERSION}-%{RELEASE}.x86_64/usr/bin:$PATH" \
        PYTHONPATH="${PYTHONPATH:-/home/tkloczko/rpmbuild/BUILDROOT/%{NAME}-%{VERSION}-%{RELEASE}.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/%{NAME}-%{VERSION}-%{RELEASE}.x86_64/usr/lib/python3.8/site-packages}" \
         \
        /usr/bin/python3 -Bm pytest -ra

desptite fact that I'm setting up $PYTHONPATH to the path where all files with DSOs are available pytest is not able to find DSOI modules:

+ PATH=/home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/bin:/usr/bin:/usr/sbin:/usr/local/sbin
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/python3 -Bm pytest -ra -q
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.9, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /home/tkloczko/rpmbuild/BUILD/yarl-1.6.3, configfile: setup.cfg, testpaths: tests/
plugins: forked-1.3.0, shutil-1.7.0, virtualenv-1.7.0, expect-1.1.0, httpbin-1.0.0, xdist-2.2.1, flake8-1.0.7, timeout-1.4.2, betamax-0.8.1, freezegun-0.4.2, cases-3.4.6, case-1.5.3, isort-1.3.0, aspectlib-1.5.2, asyncio-0.15.1, toolbox-0.5, xprocess-0.17.1, aiohttp-0.3.0, checkdocs-2.7.0, mock-3.6.1, rerunfailures-9.1.1, requests-mock-1.9.3, Faker-8.4.0, cov-2.12.1, pyfakefs-4.5.0, hypothesis-6.13.14
collected 496 items / 1 error / 495 selected
Coverage.py warning: No data was collected. (no-data-collected)

================================================================================== ERRORS ==================================================================================
__________________________________________________________________ ERROR collecting tests/test_quoting.py __________________________________________________________________
ImportError while importing test module '/home/tkloczko/rpmbuild/BUILD/yarl-1.6.3/tests/test_quoting.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib64/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_quoting.py:9: in <module>
    from yarl._quoting_c import _Quoter as _CQuoter, _Unquoter as _CUnquoter
E   ModuleNotFoundError: No module named 'yarl._quoting_c'
[tkloczko@barrel SPECS]$ ls -lR /home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/lib64/python3.8/site-packages
/home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/lib64/python3.8/site-packages:
total 0
drwxr-xr-x 1 tkloczko tkloczko 310 Jun  7 17:55 yarl
drwxr-xr-x 1 tkloczko tkloczko 128 Jun  7 17:55 yarl-1.6.3-py3.8.egg-info

/home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/lib64/python3.8/site-packages/yarl:
total 612
-rw-r--r-- 1 tkloczko tkloczko    154 Nov 14  2020 __init__.py
-rw-r--r-- 1 tkloczko tkloczko   3702 Nov 14  2020 __init__.pyi
drwxr-xr-x 1 tkloczko tkloczko    642 Jun  7 17:55 __pycache__
-rw-r--r-- 1 tkloczko tkloczko     13 Nov 14  2020 py.typed
-rw-r--r-- 1 tkloczko tkloczko 454357 Jun  7 17:54 _quoting_c.c
-rwxr-xr-x 1 tkloczko tkloczko  94016 Jun  7 17:55 _quoting_c.cpython-38-x86_64-linux-gnu.so
-rw-r--r-- 1 tkloczko tkloczko    447 Nov 14  2020 _quoting_c.pyi
-rw-r--r-- 1 tkloczko tkloczko  11498 Nov 14  2020 _quoting_c.pyx
-rw-r--r-- 1 tkloczko tkloczko    519 Nov 14  2020 _quoting.py
-rw-r--r-- 1 tkloczko tkloczko   6386 Nov 14  2020 _quoting_py.py
-rw-r--r-- 1 tkloczko tkloczko  36016 Nov 14  2020 _url.py

/home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/lib64/python3.8/site-packages/yarl/__pycache__:
total 128
-rw-r--r-- 1 tkloczko tkloczko   284 Jun  7 17:55 __init__.cpython-38.opt-1.pyc
-rw-r--r-- 1 tkloczko tkloczko   284 Jun  7 17:55 __init__.cpython-38.opt-2.pyc
-rw-r--r-- 1 tkloczko tkloczko   284 Jun  7 17:55 __init__.cpython-38.pyc
-rw-r--r-- 1 tkloczko tkloczko   478 Jun  7 17:55 _quoting.cpython-38.opt-1.pyc
-rw-r--r-- 1 tkloczko tkloczko   478 Jun  7 17:55 _quoting.cpython-38.opt-2.pyc
-rw-r--r-- 1 tkloczko tkloczko   478 Jun  7 17:55 _quoting.cpython-38.pyc
-rw-r--r-- 1 tkloczko tkloczko  4293 Jun  7 17:55 _quoting_py.cpython-38.opt-1.pyc
-rw-r--r-- 1 tkloczko tkloczko  4293 Jun  7 17:55 _quoting_py.cpython-38.opt-2.pyc
-rw-r--r-- 1 tkloczko tkloczko  4293 Jun  7 17:55 _quoting_py.cpython-38.pyc
-rw-r--r-- 1 tkloczko tkloczko 26831 Jun  7 17:55 _url.cpython-38.opt-1.pyc
-rw-r--r-- 1 tkloczko tkloczko 22032 Jun  7 17:55 _url.cpython-38.opt-2.pyc
-rw-r--r-- 1 tkloczko tkloczko 26831 Jun  7 17:55 _url.cpython-38.pyc

/home/tkloczko/rpmbuild/BUILDROOT/python-yarl-1.6.3-5.fc35.x86_64/usr/lib64/python3.8/site-packages/yarl-1.6.3-py3.8.egg-info:
total 44
-rw-r--r-- 1 tkloczko tkloczko     1 Jun  7 17:55 dependency_links.txt
-rw-r--r-- 1 tkloczko tkloczko 25050 Jun  7 17:55 PKG-INFO
-rw-r--r-- 1 tkloczko tkloczko    77 Jun  7 17:55 requires.txt
-rw-r--r-- 1 tkloczko tkloczko   861 Jun  7 17:55 SOURCES.txt
-rw-r--r-- 1 tkloczko tkloczko     5 Jun  7 17:55 top_level.txt

@RonnyPfannschmidt
Copy link
Member

I just realized the error

Its a Pytest invocation error

Invocation as module changes pythonpath

That needs to be accounted for, for example by using a src folder

Yarl does not

A potential workaround is to invoke the tests in the test subdirectory

@webknjaz
Copy link
Member

webknjaz commented Jun 7, 2021

@kloczek I wrote this elsewhere but have you tried https://src.fedoraproject.org/rpms/pyproject-rpm-macros? There's a good chance that it'll help you with this.

OTOH @RonnyPfannschmidt is right, this is likely because pytest changes the import path.

@RonnyPfannschmidt
Copy link
Member

@webknjaz pytest does not, python -m does

It creates a lot of issues im wondering about deprecation

@webknjaz
Copy link
Member

webknjaz commented Jun 7, 2021

@RonnyPfannschmidt oh, I didn't realize that. I've always thought that it's probably something in __main__.py but never bothered to check that if it might've been runpy. I wonder if it's because it's worded in a certain way in the pytest docs...

FWIW I do like (and prefer) python -m pytest style invocations.

@RonnyPfannschmidt
Copy link
Member

It has certain advantages, but messes up pythonpath

So I'm very conflicted about it

@webknjaz
Copy link
Member

webknjaz commented Jun 7, 2021

Maybe add a mandatory --i-know-it-adds-cwd?

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

@kloczek I wrote this elsewhere but have you tried https://src.fedoraproject.org/rpms/pyproject-rpm-macros? There's a good chance that it'll help you with this.

OTOH @RonnyPfannschmidt is right, this is likely because pytest changes the import path.

Hmm .. maybe I misunderstood intentions pointing on that package but that set of macros has nothing to do with pytest (?)

I'm trying to use only setuptools and builder/pep517 + installer because Fedora macros are using pip which themselves has gigantic dependencies and by this in Fedora methodology is forces whole python stack build with additional boostrap cycle (which so far I was able to avoid in all places where Fedora python packages been thinking that it was unavoidable). In other words that approach creates non-planar dependencies graph which makes all that way more harder to maintain :/

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

@webknjaz pytest does not, python -m does

It creates a lot of issues im wondering about deprecation

Moment .. so what is the difference?
And/or is it possible to pass some option to python -m pytest to force the same behaviour?🤔

@webknjaz
Copy link
Member

webknjaz commented Jun 7, 2021

Moment .. so what is the difference?

with python -m pytest, import yarl will pick up the folder in your current directory if that directory is in front of PYTHONPATH, instead of loading the installed copy from site-packages. Meaning that you may end up getting green tests in the check out but the actually installed version wouldn't be working which you'd not notice because of this.

And/or is it possible to pass some option to python -m pytest to force the same behaviour?

No. You can achieve this by either using pytest instead of python -m pytest or by removing the directory (rm -rf yarl) before the test run. Or you may change the directory so that your current dir doesn't have a folder named yarl with files that Python may recognize as importable modules — this way, it'll find the next available thing in site-packages (assuming it's installed there, of course).

Hmm .. maybe I misunderstood intentions pointing on that package but that set of macros has nothing to do with pytest (?)

Oh, it integrates tox but not pytest directly. So yes, you'll need to invoke it yourself, then. Maybe @hroncok or @encukou know better macros, I don't 🤷‍♂️.

Still, you'll be better off using those macros to build from the source correctly.

Fedora macros are using pip

The macro I pointed at doesn't. It implements extraction of PEP517 build deps on its own and generates dynamic build dependencies which is easier to maintain because you don't have to hardcode them in the spec file.

In other words that approach creates non-planar dependencies graph which makes all that way more harder to maintain :/

This sounds like you're talking about some other macros, perhaps older ones. The bootstrap complaint doesn't sound right either: the macros extract the build backend deps and turn them into fedora-packaged names so they can be extracted from src or nosrc rpm and installed after rpmbuild -br. If you don't believe me, find at least one invocation of pip here https://src.fedoraproject.org/rpms/pyproject-rpm-macros/blob/rawhide/f/pyproject_buildrequires.py — there's none.

@hroncok
Copy link
Member

hroncok commented Jun 7, 2021

Note that in Fedora, %pytest does most certainly not eval to /usr/bin/python3 -Bm pytest -ra. In fact, it evals to:

\
  CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\
  PATH="%{buildroot}/usr/bin:$PATH"\
  PYTHONPATH="${PYTHONPATH:-%{buildroot}/usr/lib64/python3.9/site-packages:%{buildroot}/usr/lib/python3.9/site-packages}"\
  PYTHONDONTWRITEBYTECODE=1\
  /usr/bin/pytest

We use /usr/bin/pytest on purpose (despite me not liking it) exactly because python -m pytest behaves the way it does. I've also started this discussion a year ago because of this: https://discuss.python.org/t/python-flag-envvar-not-to-put-current-directory-to-sys-path-but-dont-ignore-pythonpath/4235

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

with python -m pytest, import yarl will pick up the folder in your current directory if that directory is in front of PYTHONPATH, instead of loading the installed copy from site-packages.

To be hones I don't want any of those options.
I want to use what is specified in PYTHONPATH.
All because packages building process must stay as 100% non-root process.

So looks like now pytest or "python -m pytest" are not able to work with $PYTHONPATH?
If that is correct that is huge obstacle on use pytest in typical rpm packages processes (or any other package manager which rellies on processes running from non-root account).

So .. Q: how to solve that?

The macro I pointed at doesn't. It implements extraction of PEP517 build deps on its own and generates dynamic build dependencies which is easier to maintain because you don't have to hardcode them in the spec file.

That depends on point of view what is better.
With hardcoded build dependencies in spec files it is possible to use that as metadata (for example querying using rpmspec command).
Other issue is that IMO majority of the modules have under specified or over specified those dependencies so relaying on those autogenerated dependencies IMO so far not worth of outcome.

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

Note that in Fedora, %pytest does most certainly not eval to /usr/bin/python3 -Bm pytest -ra. In fact, it evals to:

That is Fedora problem .. not mine :)

Fedora macro does not in few cases with LTO or with pytest which is trying to compile something during testing.
Why? If you will look closer you may see that in Fedora %pytest macro there is no LDFLAGS.
I've fixed that issue in my macros (IIRC ~half year ago).

@kloczek
Copy link
Contributor Author

kloczek commented Jun 7, 2021

We use /usr/bin/pytest on purpose (despite me not liking it) exactly because python -m pytest behaves the way it does. I've also started this discussion a year ago because of this: https://discuss.python.org/t/python-flag-envvar-not-to-put-current-directory-to-sys-path-but-dont-ignore-pythonpath/4235

From point of view someone like me who is using pytest mainly to package python stuff from non-root account I would expect exactly the same behaviour in both cases, and in both cases IMO should be used first $PYTHONPATH, than by default not project root but build/lib or build/lib-<os>.<arch>/ depend on where setuptools install command installs all files (in sitelib or sitearch).
Someone can point on pytest code where modification to have above behaviour should be implemented?

Only functional difference between use pytest script and <python> -m pytest is that in second variant depends on python main binary exact module could be chosen automatically so in theory it would allow to have multiple python trees in the same image system.

@hroncok
Copy link
Member

hroncok commented Jun 7, 2021

That is Fedora problem .. not mine :)

In fact, it is your problem, not Fedora's. So I'll just back off.

@RonnyPfannschmidt
Copy link
Member

closing as user error when setting up python paths

@nicoddemus i think we should start to consider deprecating python -m pytest for its averse effects

@kloczek
Copy link
Contributor Author

kloczek commented Jun 8, 2021

closing as user error when setting up python paths

So how it suppose to be tested using pytest stuff just installed in rpm buildroot?

@RonnyPfannschmidt
Copy link
Member

in your particular use-case - doing something like renaming the importable name in your cwd to something not mattering should be enough
pytest will not change how python -m works - so change how you run your tests - either use the command, or fix up whats importable

for example upstream a src layout (which no longer triggers the issue)

@webknjaz
Copy link
Member

webknjaz commented Jun 8, 2021

So looks like now pytest or "python -m pytest" are not able to work with $PYTHONPATH?

It does work with $PYTHONPATH, it just adds one more directory in front of what's in there and that directory takes precedence.

So how it suppose to be tested using pytest stuff just installed in rpm buildroot?

cd tests?

@webknjaz
Copy link
Member

webknjaz commented Jun 8, 2021

@nicoddemus i think we should start to consider deprecating python -m pytest for its averse effects

Maybe issue a recommendation to cd tests/ instead?

@kloczek
Copy link
Contributor Author

kloczek commented Jun 8, 2021

@RonnyPfannschmidt

in your particular use-case - doing something like renaming the importable name in your cwd to something not mattering should be enough

It is not entirely true. It is not only my particular use-case.
It is use case which is used now or could be used by all packaging software (not only Linux one but (BSD*/MacOS/Solaris/Windows/WhateverOS) which uses non-priviledged user account to build installable end user package.
I'm packagin as well time to time some stuff for Solatis IPS and on Solaris is possible to build package using rpm spec file (using pkgbuild tool written as same as original rpm in perl).

@webknjaz

So looks like now pytest or "python -m pytest" are not able to work with $PYTHONPATH?

It does work with $PYTHONPATH, it just adds one more directory in front of what's in there and that directory takes precedence.

So rephrasing: it does not work because it adds own path on the front 😊
Am I right?
Q: why adding that path on the front is necessary? And/or or what it is used?

And another Q: why like cmake, meson., GNU autotools (when they are used to off-tree build or not) are able to organise testing without actual installation of just build resources and setuptools and other python tooling seems cannot use in-place just build files (typically under build/) and wants to use current directory or refuses to test just installed stuff in install prefix?

Sorry for asking maybe even very dumb and many questions .. I'm only trying to understand whole approach since none of the available sources I can squeeze the answer (or even vector in which I should be looking for the answers).

So how it suppose to be tested using pytest stuff just installed in rpm buildroot?

cd tests?

Many of the modules have test units not under for example the tests/.
Also it looks like only workaround because current pytest behaviour.
Also using tests/ or other directory seems is nothing more than workaround .. only because pytest (and setuptools `build_sphinx) wants to have tested resources in current directory).

Simple I have impression that it is kind of pytest inconsistency as it is mostly used after using setup tools which by default puts all stuff which on next stet will be installed under build and than some parts of the setuptools (like build_sphinx) and pytest don't want to know that it is already some stuff under the build/` which could be used to generate documentation or to test.
Why it is like that?

After rethinking one more time now I'm more or less convinced that use in Fedora %pytest macro (which at thhe momnent looks like blow):

%pytest %{expand:\\\
  CFLAGS="${CFLAGS:-${RPM_OPT_FLAGS}}" LDFLAGS="${LDFLAGS:-${RPM_LD_FLAGS}}"\\\
  PATH="%{buildroot}%{_bindir}:$PATH"\\\
  PYTHONPATH="${PYTHONPATH:-%{buildroot}%{python3_sitearch}:%{buildroot}%{python3_sitelib}}"\\\
  PYTHONDONTWRITEBYTECODE=1\\\
  %__pytest}

$PYTHONPATH is kind of wrong and by default it should be using build/ .. only because in that path all files are on next stage will be installed under install prefix or system image (if install prefix is not used).
I can understand that using build/ in current form probably is difficult because under that directory files are installed in the tree not representing the same pattern or hierarchy like all that stuff will be installed in final destination.
So under the build/ we have here build/lib/, build/lib-.arch/, build/sphinx and build/scripts- .. that looks messy or made up without thinking that that stuff will be used on next stages.
Maybe here is The Problem?🤔

Using (somehow) build/ could give 100% chance test all that stuff before actual install installation or packaging resources which with some issues which is possible to detect using pytest
.. And the same by default (IMO) should be used by setup.py build_sphinx and other setuptools commands

Am I pointing in at least the right direction or not? If I'm not please .. can someone explain why?

Thank you guys to all your time.

@webknjaz
Copy link
Member

webknjaz commented Jun 8, 2021

So rephrasing: it does not work because it adds own path on the front
Am I right?

This is half correct. The correct part is that the path gets prepended.
But I must point out that "it" is not pytest, it's python -m that adds CWD in front.

Q: why adding that path on the front is necessary? And/or or what it is used?

It is not necessary. It's just what happens when you invoke Python modules using python -m <module_name> syntax. pytest cannot influence this behavior. By the time pytest's own entrypoint gets executed, $PYTHONPATH (or sys.path in runtime) already has CWD at the beginning of the list.

Many of the modules have test units not under for example the tests/.
Also it looks like only workaround because current pytest behaviour.

As Ronny mentioned earlier, there's also mv yarl{,.bak} && python -m pytest && mv yarl{.bak,}.

Why it is like that?

Because you'll end up testing the wrong thing, not what the end-users will get. Also, note that it's highly discouraged to abuse setup.py as a test runner. Regarding sphinx, it's usually best to invoke it directly. Some projects still have autogenerated Makefile wrappers invoking sphinx-build, others have a tox env invoking python -m sphinx. The most correct thing to do is to invoke whatever automation the project has. Otherwise, you're on your own making sure that you invoke things in a way that is supported at all times.

$PYTHONPATH is kind of wrong and by default it should be using build/

No, it shouldn't. It's an internal implementation detail of a certain backend (setuptools). You mustn't rely on it. And you mustn't assume that the project's build backend is setuptools. It could be anything (flit, poetry, a project's own in-tree backend). Each may put temporary build artifacts in different locations. Only rely on what's in the resulting artifact / resulting package install under site-packages.

Am I pointing in at least the right direction or not? If I'm not please .. can someone explain why?

I think the best place to start learning about Python packaging is https://packaging.python.org and you may ask related questions at https://discuss.python.org/c/packaging — this is where a lot of folks who know how this all works are active. There's also https://discord.com/invite/pypa for a more live chat and #pypa on Libera / freenode.
So I suggest moving this elsewhere since it's off-topic in the pytest repo.

@kloczek
Copy link
Contributor Author

kloczek commented Jun 8, 2021

So rephrasing: it does not work because it adds own path on the front
Am I right?

This is half correct. The correct part is that the path gets prepended.
But I must point out that "it" is not pytest, it's python -m that adds CWD in front.

From python manual page:

       -m module-name
              Searches sys.path for the named module and runs the corresponding .py file as a script.

and ..

[tkloczko@barrel SPECS]$ python3
Python 3.8.9 (default, Apr  7 2021, 13:42:48)
[GCC 11.0.1 20210324 (Red Hat 11.0.1-0)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib64/python38.zip', '/usr/lib64/python3.8', '/usr/lib64/python3.8/lib-dynload', '/usr/lib64/python3.8/site-packages', '/usr/lib/python3.8/site-packages']
>>>
[tkloczko@barrel SPECS]$ PYTHONPATH=. python3
Python 3.8.9 (default, Apr  7 2021, 13:42:48)
[GCC 11.0.1 20210324 (Red Hat 11.0.1-0)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/home/tkloczko/rpmbuild/SPECS', '/usr/lib64/python38.zip', '/usr/lib64/python3.8', '/usr/lib64/python3.8/lib-dynload', '/usr/lib64/python3.8/site-packages', '/usr/lib/python3.8/site-packages']
>>>

So sys.path shows exactly what it suppose to show.
If -m by default adds current directory it seems like not documented behaviour.

In man page is as well

       -s     Don't add user site directory to sys.path.

Could -s with PYTHONPATH pointing sitelib and sitearch directories in install prefix could be the solution of not adding current directory? (if pytest will be executed over python3 -s -m pytest)

@webknjaz
Copy link
Member

webknjaz commented Jun 8, 2021

Could -s with PYTHONPATH pointing sitelib and sitearch directories in install prefix could be the solution of not adding current directory? (if pytest will be executed over python3 -s -m pytest)

I believe -s just removes ~/.local/lib/python*/site-packages

@webknjaz
Copy link
Member

webknjaz commented Jun 8, 2021

If -m by default adds current directory it seems like not documented behaviour.

I believe this happens here https://github.com/python/cpython/blob/3fe921cd/Lib/runpy.py#L274

@kloczek
Copy link
Contributor Author

kloczek commented Jun 8, 2021

So there is no any way to run module without that upfront current directory??🤔

@webknjaz
Copy link
Member

webknjaz commented Jun 8, 2021

Yes, you can run the script called pytest that is installed in the bin/ dir (in your virtualenv or system depending on where you have it).

@kloczek
Copy link
Contributor Author

kloczek commented Jun 18, 2021

Is it possible to reopen this ticket?
I sjust realised that ticket because discussion diverted a bit from original issue to how pytest should be used, and I just found another module with exactly the same issue.

So again summary details about scenarion:

  • "setup.py build"
  • "setup.py install --root </install/prefix>"
  • "pytest" with PYTHONPATH pointing to setearch and sitelib inside </install/prefix> and pytest is not able to load DSO modules

Here is example zmq

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-zmq-22.1.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-zmq-22.1.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ PYTHONDONTWRITEBYTECODE=1
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.9, 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)
rootdir: /home/tkloczko/rpmbuild/BUILD/pyzmq-22.1.0
plugins: forked-1.3.0, shutil-1.7.0, virtualenv-1.7.0, expect-1.1.0, httpbin-1.0.0, flake8-1.0.7, timeout-1.4.2, betamax-0.8.1, freezegun-0.4.2, case-1.5.3, isort-1.3.0, aspectlib-1.5.2, asyncio-0.15.1, toolbox-0.5, xprocess-0.17.1, aiohttp-0.3.0, checkdocs-2.7.0, mock-3.6.1, rerunfailures-9.1.1, requests-mock-1.9.3, cov-2.12.1, pyfakefs-4.5.0, cases-3.6.1, flaky-3.7.0, hypothesis-6.14.0, benchmark-3.4.1, xdist-2.3.0, Faker-8.8.1
collected 0 items / 1 error

================================================================================== ERRORS ==================================================================================
______________________________________________________________________ ERROR collecting test session _______________________________________________________________________
/usr/lib64/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
<frozen importlib._bootstrap>:1014: in _gcd_import
    ???
<frozen importlib._bootstrap>:991: in _find_and_load
    ???
<frozen importlib._bootstrap>:961: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:219: in _call_with_frames_removed
    ???
<frozen importlib._bootstrap>:1014: in _gcd_import
    ???
<frozen importlib._bootstrap>:991: in _find_and_load
    ???
<frozen importlib._bootstrap>:961: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:219: in _call_with_frames_removed
    ???
<frozen importlib._bootstrap>:1014: in _gcd_import
    ???
<frozen importlib._bootstrap>:991: in _find_and_load
    ???
<frozen importlib._bootstrap>:975: in _find_and_load_unlocked
    ???
<frozen importlib._bootstrap>:671: in _load_unlocked
    ???
<frozen importlib._bootstrap_external>:783: in exec_module
    ???
<frozen importlib._bootstrap>:219: in _call_with_frames_removed
    ???
zmq/__init__.py:103: in <module>
    from zmq import backend
zmq/backend/__init__.py:32: in <module>
    raise original_error from None
zmq/backend/__init__.py:27: in <module>
    _ns = select_backend(first)
zmq/backend/select.py:32: in select_backend
    mod = import_module(name)
/usr/lib64/python3.8/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
zmq/backend/cython/__init__.py:6: in <module>
    from . import (
E   ImportError: cannot import name 'constants' from partially initialized module 'zmq.backend.cython' (most likely due to a circular import) (/home/tkloczko/rpmbuild/BUILD/pyzmq-22.1.0/zmq/backend/cython/__init__.py)
========================================================================= short test summary info ==========================================================================
ERROR  - ImportError: cannot import name 'constants' from partially initialized module 'zmq.backend.cython' (most likely due to a circular import) (/home/tkloczko/rpmbui...
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================================= 1 error in 0.68s =============================================================================

All installed DSOs are on place

[tkloczko@barrel SPECS]$ ls -la /home/tkloczko/rpmbuild/BUILDROOT/python-zmq-22.1.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages/zmq/backend/cython
total 764
drwxr-xr-x 1 tkloczko tkloczko   1006 Jun 18 08:16 .
drwxr-xr-x 1 tkloczko tkloczko    132 Jun 18 08:16 ..
-rw-r--r-- 1 tkloczko tkloczko    968 May 26 13:05 checkrc.pxd
-rw-r--r-- 1 tkloczko tkloczko   6751 May 26 13:05 constant_enums.pxi
-rwxr-xr-x 1 tkloczko tkloczko  74208 Jun 18 08:16 constants.cpython-38-x86_64-linux-gnu.so
-rw-r--r-- 1 tkloczko tkloczko  12728 May 26 13:05 constants.pxi
-rwxr-xr-x 1 tkloczko tkloczko  71896 Jun 18 08:16 context.cpython-38-x86_64-linux-gnu.so
-rw-r--r-- 1 tkloczko tkloczko   1408 May 26 13:05 context.pxd
-rwxr-xr-x 1 tkloczko tkloczko  52864 Jun 18 08:16 _device.cpython-38-x86_64-linux-gnu.so
-rwxr-xr-x 1 tkloczko tkloczko  39168 Jun 18 08:16 error.cpython-38-x86_64-linux-gnu.so
-rw-r--r-- 1 tkloczko tkloczko    140 May 26 13:05 __init__.pxd
-rw-r--r-- 1 tkloczko tkloczko    742 May 26 13:05 __init__.py
-rw-r--r-- 1 tkloczko tkloczko   4564 May 26 13:05 libzmq.pxd
-rwxr-xr-x 1 tkloczko tkloczko  98832 Jun 18 08:16 message.cpython-38-x86_64-linux-gnu.so
-rw-r--r-- 1 tkloczko tkloczko   2396 May 26 13:05 message.pxd
-rwxr-xr-x 1 tkloczko tkloczko  74120 Jun 18 08:16 _poll.cpython-38-x86_64-linux-gnu.so
-rwxr-xr-x 1 tkloczko tkloczko  52392 Jun 18 08:16 _proxy_steerable.cpython-38-x86_64-linux-gnu.so
drwxr-xr-x 1 tkloczko tkloczko    162 Jun 18 08:16 __pycache__
-rwxr-xr-x 1 tkloczko tkloczko 156264 Jun 18 08:16 socket.cpython-38-x86_64-linux-gnu.so
-rw-r--r-- 1 tkloczko tkloczko   2104 May 26 13:05 socket.pxd
-rwxr-xr-x 1 tkloczko tkloczko  48824 Jun 18 08:16 utils.cpython-38-x86_64-linux-gnu.so
-rwxr-xr-x 1 tkloczko tkloczko  34896 Jun 18 08:16 _version.cpython-38-x86_64-linux-gnu.so

@webknjaz
Copy link
Member

webknjaz commented Jun 18, 2021

No, this is off-topic and should be locked 🔒. I this comment #8731 (comment) I gave you the proper places for such discussions.

P.S. FWIW it looks like Python picks up the local dir instead of the site-packages. This could happen for a million reasons and you need to inject a breakpoint() to investigate your specific env. It could even be that something in tests modifies the search path. Or a third party extension. Or maybe the config isn't properly pointing at the tests dir making pytest scan (and import) things in the source dir. But I'm pretty sure that it has nothing to do with pytest itself.

@RonnyPfannschmidt
Copy link
Member

@kloczek this is again a different wrong test invocation on a package that has a structurally bad setup of the testsuite as inline to the code

a different pytest invocation is needed to run/test it in the installation site
(if you carefully read the setup.py it perfectly mentions testing needs in-place build)

i believe a pytest --pyargs zmq.tests is needed, potentially out of tree

im going to lock this issue now and urge you to reconsider how you interact with upstream issue trackers

@pytest-dev pytest-dev locked as too heated and limited conversation to collaborators Jun 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants