-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make test_zintegration work concurrently
- Move to using tmp_path fixture instead of udir - Use fixtures to setup separate virtual environments instead of function calls
- Loading branch information
Showing
1 changed file
with
184 additions
and
181 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,199 +1,202 @@ | ||
import py, os, sys, shutil | ||
import os | ||
import pathlib | ||
import pytest | ||
import site | ||
import subprocess | ||
import sys | ||
import textwrap | ||
from testing.udir import udir | ||
import pytest | ||
|
||
from importlib.util import find_spec | ||
|
||
if sys.platform == 'win32': | ||
pytestmark = pytest.mark.skip('snippets do not run on win32') | ||
|
||
if sys.version_info < (2, 7): | ||
pytestmark = pytest.mark.skip( | ||
'fails e.g. on a Debian/Ubuntu which patches virtualenv' | ||
' in a non-2.6-friendly way') | ||
|
||
def create_venv(name): | ||
tmpdir = udir.join(name) | ||
try: | ||
# FUTURE: we should probably update this to use venv for at least more modern Pythons, and | ||
# install setuptools/pip/etc explicitly for the tests that require them (as venv has stopped including | ||
# setuptools and wheel by default for newer versions). | ||
subprocess.check_call(['virtualenv', | ||
#'--never-download', <= could be added, but causes failures | ||
# in random cases on random machines | ||
'-p', os.path.abspath(sys.executable), | ||
str(tmpdir)]) | ||
|
||
# Python 3.12 venv/virtualenv no longer include setuptools and wheel by default, which | ||
# breaks a number of these tests; ensure it's always present for 3.12+ | ||
if sys.version_info >= (3, 12): | ||
subprocess.check_call([ | ||
os.path.join(tmpdir, 'bin/python'), | ||
'-m', | ||
'pip', | ||
'install', | ||
'setuptools', | ||
'wheel', | ||
'--upgrade' | ||
]) | ||
|
||
except OSError as e: | ||
pytest.skip("Cannot execute virtualenv: %s" % (e,)) | ||
|
||
site_packages = None | ||
for dirpath, dirnames, filenames in os.walk(str(tmpdir)): | ||
if os.path.basename(dirpath) == 'site-packages': | ||
site_packages = dirpath | ||
break | ||
paths = "" | ||
if site_packages: | ||
try: | ||
from cffi import _pycparser | ||
modules = ('cffi', '_cffi_backend') | ||
except ImportError: | ||
modules = ('cffi', '_cffi_backend', 'pycparser') | ||
try: | ||
import ply | ||
except ImportError: | ||
pass | ||
else: | ||
modules += ('ply',) # needed for older versions of pycparser | ||
paths = [] | ||
for module in modules: | ||
target = __import__(module, None, None, []) | ||
if not hasattr(target, '__file__'): # for _cffi_backend on pypy | ||
continue | ||
src = os.path.abspath(target.__file__) | ||
for end in ['__init__.pyc', '__init__.pyo', '__init__.py']: | ||
if src.lower().endswith(end): | ||
src = src[:-len(end)-1] | ||
break | ||
paths.append(os.path.dirname(src)) | ||
paths = os.pathsep.join(paths) | ||
return tmpdir, paths | ||
|
||
SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets') | ||
|
||
def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet): | ||
venv_dir, paths = venv_dir_and_paths | ||
def remove(dir): | ||
dir = str(SNIPPET_DIR.join(dirname, dir)) | ||
shutil.rmtree(dir, ignore_errors=True) | ||
remove('build') | ||
remove('__pycache__') | ||
for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))): | ||
remove(os.path.join(basedir, '__pycache__')) | ||
olddir = os.getcwd() | ||
python_f = udir.join('x.py') | ||
python_f.write(textwrap.dedent(python_snippet)) | ||
try: | ||
os.chdir(str(SNIPPET_DIR.join(dirname))) | ||
if os.name == 'nt': | ||
bindir = 'Scripts' | ||
@pytest.fixture(scope="session") | ||
def snippet_dir(): | ||
return pathlib.Path(__file__).parent / 'snippets' | ||
|
||
@pytest.fixture | ||
def create_venv(tmp_path): | ||
venv_path = tmp_path / ".venv" | ||
|
||
def _create_venv(name): | ||
if find_spec("venv") is not None: | ||
venv_module = "venv" | ||
args = [] | ||
else: | ||
bindir = 'bin' | ||
vp = str(venv_dir.join(bindir).join('python')) | ||
env = os.environ.copy() | ||
env['PYTHONPATH'] = paths | ||
subprocess.check_call((vp, 'setup.py', 'clean'), env=env) | ||
# there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and | ||
# we're in the same directory with the build (it tries to look up dependencies for itself on PyPI); | ||
# subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now. | ||
subprocess.check_call((vp, '-m', 'pip', 'install', '.'), env=env) | ||
subprocess.check_call((vp, str(python_f)), env=env) | ||
finally: | ||
os.chdir(olddir) | ||
venv_module = "virtualenv" | ||
args = ["--python", sys.executable] | ||
|
||
def run_setup_and_program(dirname, python_snippet): | ||
venv_dir = create_venv(dirname + '-cpy') | ||
really_run_setup_and_program(dirname, venv_dir, python_snippet) | ||
# | ||
sys._force_generic_engine_ = True | ||
try: | ||
venv_dir = create_venv(dirname + '-gen') | ||
really_run_setup_and_program(dirname, venv_dir, python_snippet) | ||
finally: | ||
del sys._force_generic_engine_ | ||
# the two files lextab.py and yacctab.py are created by not-correctly- | ||
# installed versions of pycparser. | ||
assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'lextab.py'))) | ||
assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'yacctab.py'))) | ||
|
||
class TestZIntegration(object): | ||
def teardown_class(self): | ||
if udir.isdir(): | ||
udir.remove(ignore_errors=True) | ||
udir.ensure(dir=1) | ||
|
||
def test_infrastructure(self): | ||
run_setup_and_program('infrastructure', ''' | ||
import snip_infrastructure | ||
assert snip_infrastructure.func() == 42 | ||
''') | ||
|
||
def test_distutils_module(self): | ||
run_setup_and_program("distutils_module", ''' | ||
import snip_basic_verify | ||
p = snip_basic_verify.C.getpwuid(0) | ||
assert snip_basic_verify.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_distutils_package_1(self): | ||
run_setup_and_program("distutils_package_1", ''' | ||
import snip_basic_verify1 | ||
p = snip_basic_verify1.C.getpwuid(0) | ||
assert snip_basic_verify1.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_distutils_package_2(self): | ||
run_setup_and_program("distutils_package_2", ''' | ||
import snip_basic_verify2 | ||
p = snip_basic_verify2.C.getpwuid(0) | ||
assert snip_basic_verify2.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_setuptools_module(self): | ||
run_setup_and_program("setuptools_module", ''' | ||
import snip_setuptools_verify | ||
p = snip_setuptools_verify.C.getpwuid(0) | ||
assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_setuptools_package_1(self): | ||
run_setup_and_program("setuptools_package_1", ''' | ||
import snip_setuptools_verify1 | ||
p = snip_setuptools_verify1.C.getpwuid(0) | ||
assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_setuptools_package_2(self): | ||
run_setup_and_program("setuptools_package_2", ''' | ||
import snip_setuptools_verify2 | ||
p = snip_setuptools_verify2.C.getpwuid(0) | ||
assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_set_py_limited_api(self): | ||
from cffi.setuptools_ext import _set_py_limited_api | ||
try: | ||
import setuptools | ||
except ImportError as e: | ||
pytest.skip(str(e)) | ||
orig_version = setuptools.__version__ | ||
expecting_limited_api = not hasattr(sys, 'gettotalrefcount') | ||
try: | ||
setuptools.__version__ = '26.0.0' | ||
from setuptools import Extension | ||
subprocess.check_call([sys.executable, "-m", venv_module, *args, str(venv_path)]) | ||
|
||
kwds = _set_py_limited_api(Extension, {}) | ||
assert kwds.get('py_limited_api', False) == expecting_limited_api | ||
# Python 3.12 venv/virtualenv no longer include setuptools and wheel by default, which | ||
# breaks a number of these tests; ensure it's always present for 3.12+ | ||
if sys.version_info >= (3, 12): | ||
subprocess.check_call([ | ||
venv_path / "bin" / "python", "-m", "pip", "install", "--upgrade", | ||
"setuptools", | ||
"wheel", | ||
]) | ||
|
||
setuptools.__version__ = '25.0' | ||
kwds = _set_py_limited_api(Extension, {}) | ||
assert kwds.get('py_limited_api', False) == False | ||
except OSError as e: | ||
pytest.skip("Cannot execute %s: %s" % (venv_module, e)) | ||
|
||
setuptools.__version__ = 'development' | ||
kwds = _set_py_limited_api(Extension, {}) | ||
assert kwds.get('py_limited_api', False) == expecting_limited_api | ||
site_packages = site.getsitepackages() | ||
paths = [] | ||
if site_packages: | ||
if find_spec("cffi._pycparser") is not None: | ||
modules = ('cffi', '_cffi_backend') | ||
else: | ||
modules = ('cffi', '_cffi_backend', 'pycparser') | ||
if find_spec("ply") is not None: | ||
modules += ('ply',) # needed for older versions of pycparser | ||
|
||
paths = [] | ||
for module in modules: | ||
target = __import__(module, None, None, []) | ||
if not hasattr(target, '__file__'): # for _cffi_backend on pypy | ||
continue | ||
|
||
src = os.path.abspath(target.__file__) | ||
for end in ['__init__.pyc', '__init__.pyo', '__init__.py']: | ||
if src.lower().endswith(end): | ||
src = src[:-len(end)-1] | ||
break | ||
|
||
paths.append(os.path.dirname(src)) | ||
|
||
paths = os.pathsep.join(paths) | ||
|
||
return venv_path, paths | ||
|
||
return _create_venv | ||
|
||
@pytest.fixture | ||
def setup_program(tmp_path_factory, snippet_dir): | ||
def _setup_program(dirname, venv_dir_and_paths, python_snippet): | ||
venv_dir, paths = venv_dir_and_paths | ||
olddir = os.getcwd() | ||
workdir = tmp_path_factory.mktemp("ffi-", numbered=True) | ||
python_file = workdir.joinpath('x.py') | ||
python_file.write_text(textwrap.dedent(python_snippet)) | ||
try: | ||
os.chdir(str(snippet_dir.joinpath(dirname))) | ||
if os.name == 'nt': | ||
bindir = 'Scripts' | ||
else: | ||
bindir = 'bin' | ||
|
||
venv_python = str(venv_dir.joinpath(bindir).joinpath('python')) | ||
env = os.environ.copy() | ||
env['PYTHONPATH'] = paths | ||
subprocess.check_call((venv_python, 'setup.py', 'clean'), env=env) | ||
# there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and | ||
# we're in the same directory with the build (it tries to look up dependencies for itself on PyPI); | ||
# subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now. | ||
subprocess.check_call((venv_python, '-m', 'pip', 'install', '.'), env=env) | ||
subprocess.check_call((venv_python, str(python_file)), env=env) | ||
finally: | ||
os.chdir(olddir) | ||
|
||
return _setup_program | ||
|
||
@pytest.fixture | ||
def run_setup_and_program(tmp_path, create_venv, snippet_dir, setup_program): | ||
def _run_setup_and_program(dirname, python_snippet): | ||
venv_dir_and_paths = create_venv(dirname + '-cpy') | ||
setup_program(dirname, venv_dir_and_paths, python_snippet) | ||
|
||
sys._force_generic_engine_ = True | ||
try: | ||
venv_dir = create_venv(dirname + '-gen') | ||
setup_program(dirname, venv_dir, python_snippet) | ||
finally: | ||
setuptools.__version__ = orig_version | ||
del sys._force_generic_engine_ | ||
|
||
# the two files lextab.py and yacctab.py are created by not-correctly- | ||
# installed versions of pycparser. | ||
assert not os.path.exists(str(snippet_dir.joinpath(dirname, 'lextab.py'))) | ||
assert not os.path.exists(str(snippet_dir.joinpath(dirname, 'yacctab.py'))) | ||
|
||
return _run_setup_and_program | ||
|
||
|
||
def test_infrastructure(run_setup_and_program): | ||
run_setup_and_program('infrastructure', ''' | ||
import snip_infrastructure | ||
assert snip_infrastructure.func() == 42 | ||
''') | ||
|
||
def test_distutils_module(run_setup_and_program): | ||
run_setup_and_program("distutils_module", ''' | ||
import snip_basic_verify | ||
p = snip_basic_verify.C.getpwuid(0) | ||
assert snip_basic_verify.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_distutils_package_1(run_setup_and_program): | ||
run_setup_and_program("distutils_package_1", ''' | ||
import snip_basic_verify1 | ||
p = snip_basic_verify1.C.getpwuid(0) | ||
assert snip_basic_verify1.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_distutils_package_2(run_setup_and_program): | ||
run_setup_and_program("distutils_package_2", ''' | ||
import snip_basic_verify2 | ||
p = snip_basic_verify2.C.getpwuid(0) | ||
assert snip_basic_verify2.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_setuptools_module(run_setup_and_program): | ||
run_setup_and_program("setuptools_module", ''' | ||
import snip_setuptools_verify | ||
p = snip_setuptools_verify.C.getpwuid(0) | ||
assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_setuptools_package_1(run_setup_and_program): | ||
run_setup_and_program("setuptools_package_1", ''' | ||
import snip_setuptools_verify1 | ||
p = snip_setuptools_verify1.C.getpwuid(0) | ||
assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_setuptools_package_2(run_setup_and_program): | ||
run_setup_and_program("setuptools_package_2", ''' | ||
import snip_setuptools_verify2 | ||
p = snip_setuptools_verify2.C.getpwuid(0) | ||
assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" | ||
''') | ||
|
||
def test_set_py_limited_api(): | ||
from cffi.setuptools_ext import _set_py_limited_api | ||
try: | ||
import setuptools | ||
except ImportError as e: | ||
pytest.skip(str(e)) | ||
orig_version = setuptools.__version__ | ||
expecting_limited_api = not hasattr(sys, 'gettotalrefcount') | ||
try: | ||
setuptools.__version__ = '26.0.0' | ||
from setuptools import Extension | ||
|
||
kwds = _set_py_limited_api(Extension, {}) | ||
assert kwds.get('py_limited_api', False) == expecting_limited_api | ||
|
||
setuptools.__version__ = '25.0' | ||
kwds = _set_py_limited_api(Extension, {}) | ||
assert kwds.get('py_limited_api', False) == False | ||
|
||
setuptools.__version__ = 'development' | ||
kwds = _set_py_limited_api(Extension, {}) | ||
assert kwds.get('py_limited_api', False) == expecting_limited_api | ||
|
||
finally: | ||
setuptools.__version__ = orig_version |