Skip to content

Commit c9c5ab5

Browse files
committed
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
1 parent 73465a1 commit c9c5ab5

File tree

1 file changed

+184
-181
lines changed

1 file changed

+184
-181
lines changed

testing/cffi0/test_zintegration.py

Lines changed: 184 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,202 @@
1-
import py, os, sys, shutil
1+
import os
2+
import pathlib
3+
import pytest
4+
import site
25
import subprocess
6+
import sys
37
import textwrap
4-
from testing.udir import udir
5-
import pytest
8+
9+
from importlib.util import find_spec
610

711
if sys.platform == 'win32':
812
pytestmark = pytest.mark.skip('snippets do not run on win32')
13+
914
if sys.version_info < (2, 7):
1015
pytestmark = pytest.mark.skip(
1116
'fails e.g. on a Debian/Ubuntu which patches virtualenv'
1217
' in a non-2.6-friendly way')
1318

14-
def create_venv(name):
15-
tmpdir = udir.join(name)
16-
try:
17-
# FUTURE: we should probably update this to use venv for at least more modern Pythons, and
18-
# install setuptools/pip/etc explicitly for the tests that require them (as venv has stopped including
19-
# setuptools and wheel by default for newer versions).
20-
subprocess.check_call(['virtualenv',
21-
#'--never-download', <= could be added, but causes failures
22-
# in random cases on random machines
23-
'-p', os.path.abspath(sys.executable),
24-
str(tmpdir)])
25-
26-
# Python 3.12 venv/virtualenv no longer include setuptools and wheel by default, which
27-
# breaks a number of these tests; ensure it's always present for 3.12+
28-
if sys.version_info >= (3, 12):
29-
subprocess.check_call([
30-
os.path.join(tmpdir, 'bin/python'),
31-
'-m',
32-
'pip',
33-
'install',
34-
'setuptools',
35-
'wheel',
36-
'--upgrade'
37-
])
38-
39-
except OSError as e:
40-
pytest.skip("Cannot execute virtualenv: %s" % (e,))
41-
42-
site_packages = None
43-
for dirpath, dirnames, filenames in os.walk(str(tmpdir)):
44-
if os.path.basename(dirpath) == 'site-packages':
45-
site_packages = dirpath
46-
break
47-
paths = ""
48-
if site_packages:
49-
try:
50-
from cffi import _pycparser
51-
modules = ('cffi', '_cffi_backend')
52-
except ImportError:
53-
modules = ('cffi', '_cffi_backend', 'pycparser')
54-
try:
55-
import ply
56-
except ImportError:
57-
pass
58-
else:
59-
modules += ('ply',) # needed for older versions of pycparser
60-
paths = []
61-
for module in modules:
62-
target = __import__(module, None, None, [])
63-
if not hasattr(target, '__file__'): # for _cffi_backend on pypy
64-
continue
65-
src = os.path.abspath(target.__file__)
66-
for end in ['__init__.pyc', '__init__.pyo', '__init__.py']:
67-
if src.lower().endswith(end):
68-
src = src[:-len(end)-1]
69-
break
70-
paths.append(os.path.dirname(src))
71-
paths = os.pathsep.join(paths)
72-
return tmpdir, paths
73-
74-
SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets')
75-
76-
def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet):
77-
venv_dir, paths = venv_dir_and_paths
78-
def remove(dir):
79-
dir = str(SNIPPET_DIR.join(dirname, dir))
80-
shutil.rmtree(dir, ignore_errors=True)
81-
remove('build')
82-
remove('__pycache__')
83-
for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))):
84-
remove(os.path.join(basedir, '__pycache__'))
85-
olddir = os.getcwd()
86-
python_f = udir.join('x.py')
87-
python_f.write(textwrap.dedent(python_snippet))
88-
try:
89-
os.chdir(str(SNIPPET_DIR.join(dirname)))
90-
if os.name == 'nt':
91-
bindir = 'Scripts'
19+
@pytest.fixture(scope="session")
20+
def snippet_dir():
21+
return pathlib.Path(__file__).parent / 'snippets'
22+
23+
@pytest.fixture
24+
def create_venv(tmp_path):
25+
venv_path = tmp_path / ".venv"
26+
27+
def _create_venv(name):
28+
if find_spec("venv") is not None:
29+
venv_module = "venv"
30+
args = []
9231
else:
93-
bindir = 'bin'
94-
vp = str(venv_dir.join(bindir).join('python'))
95-
env = os.environ.copy()
96-
env['PYTHONPATH'] = paths
97-
subprocess.check_call((vp, 'setup.py', 'clean'), env=env)
98-
# there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and
99-
# we're in the same directory with the build (it tries to look up dependencies for itself on PyPI);
100-
# subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now.
101-
subprocess.check_call((vp, '-m', 'pip', 'install', '.'), env=env)
102-
subprocess.check_call((vp, str(python_f)), env=env)
103-
finally:
104-
os.chdir(olddir)
32+
venv_module = "virtualenv"
33+
args = ["--python", sys.executable]
10534

106-
def run_setup_and_program(dirname, python_snippet):
107-
venv_dir = create_venv(dirname + '-cpy')
108-
really_run_setup_and_program(dirname, venv_dir, python_snippet)
109-
#
110-
sys._force_generic_engine_ = True
111-
try:
112-
venv_dir = create_venv(dirname + '-gen')
113-
really_run_setup_and_program(dirname, venv_dir, python_snippet)
114-
finally:
115-
del sys._force_generic_engine_
116-
# the two files lextab.py and yacctab.py are created by not-correctly-
117-
# installed versions of pycparser.
118-
assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'lextab.py')))
119-
assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'yacctab.py')))
120-
121-
class TestZIntegration(object):
122-
def teardown_class(self):
123-
if udir.isdir():
124-
udir.remove(ignore_errors=True)
125-
udir.ensure(dir=1)
126-
127-
def test_infrastructure(self):
128-
run_setup_and_program('infrastructure', '''
129-
import snip_infrastructure
130-
assert snip_infrastructure.func() == 42
131-
''')
132-
133-
def test_distutils_module(self):
134-
run_setup_and_program("distutils_module", '''
135-
import snip_basic_verify
136-
p = snip_basic_verify.C.getpwuid(0)
137-
assert snip_basic_verify.ffi.string(p.pw_name) == b"root"
138-
''')
139-
140-
def test_distutils_package_1(self):
141-
run_setup_and_program("distutils_package_1", '''
142-
import snip_basic_verify1
143-
p = snip_basic_verify1.C.getpwuid(0)
144-
assert snip_basic_verify1.ffi.string(p.pw_name) == b"root"
145-
''')
146-
147-
def test_distutils_package_2(self):
148-
run_setup_and_program("distutils_package_2", '''
149-
import snip_basic_verify2
150-
p = snip_basic_verify2.C.getpwuid(0)
151-
assert snip_basic_verify2.ffi.string(p.pw_name) == b"root"
152-
''')
153-
154-
def test_setuptools_module(self):
155-
run_setup_and_program("setuptools_module", '''
156-
import snip_setuptools_verify
157-
p = snip_setuptools_verify.C.getpwuid(0)
158-
assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root"
159-
''')
160-
161-
def test_setuptools_package_1(self):
162-
run_setup_and_program("setuptools_package_1", '''
163-
import snip_setuptools_verify1
164-
p = snip_setuptools_verify1.C.getpwuid(0)
165-
assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root"
166-
''')
167-
168-
def test_setuptools_package_2(self):
169-
run_setup_and_program("setuptools_package_2", '''
170-
import snip_setuptools_verify2
171-
p = snip_setuptools_verify2.C.getpwuid(0)
172-
assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root"
173-
''')
174-
175-
def test_set_py_limited_api(self):
176-
from cffi.setuptools_ext import _set_py_limited_api
177-
try:
178-
import setuptools
179-
except ImportError as e:
180-
pytest.skip(str(e))
181-
orig_version = setuptools.__version__
182-
expecting_limited_api = not hasattr(sys, 'gettotalrefcount')
18335
try:
184-
setuptools.__version__ = '26.0.0'
185-
from setuptools import Extension
36+
subprocess.check_call([sys.executable, "-m", venv_module, *args, str(venv_path)])
18637

187-
kwds = _set_py_limited_api(Extension, {})
188-
assert kwds.get('py_limited_api', False) == expecting_limited_api
38+
# Python 3.12 venv/virtualenv no longer include setuptools and wheel by default, which
39+
# breaks a number of these tests; ensure it's always present for 3.12+
40+
if sys.version_info >= (3, 12):
41+
subprocess.check_call([
42+
venv_path / "bin" / "python", "-m", "pip", "install", "--upgrade",
43+
"setuptools",
44+
"wheel",
45+
])
18946

190-
setuptools.__version__ = '25.0'
191-
kwds = _set_py_limited_api(Extension, {})
192-
assert kwds.get('py_limited_api', False) == False
47+
except OSError as e:
48+
pytest.skip("Cannot execute %s: %s" % (venv_module, e))
19349

194-
setuptools.__version__ = 'development'
195-
kwds = _set_py_limited_api(Extension, {})
196-
assert kwds.get('py_limited_api', False) == expecting_limited_api
50+
site_packages = site.getsitepackages()
51+
paths = []
52+
if site_packages:
53+
if find_spec("cffi._pycparser") is not None:
54+
modules = ('cffi', '_cffi_backend')
55+
else:
56+
modules = ('cffi', '_cffi_backend', 'pycparser')
57+
if find_spec("ply") is not None:
58+
modules += ('ply',) # needed for older versions of pycparser
59+
60+
paths = []
61+
for module in modules:
62+
target = __import__(module, None, None, [])
63+
if not hasattr(target, '__file__'): # for _cffi_backend on pypy
64+
continue
65+
66+
src = os.path.abspath(target.__file__)
67+
for end in ['__init__.pyc', '__init__.pyo', '__init__.py']:
68+
if src.lower().endswith(end):
69+
src = src[:-len(end)-1]
70+
break
71+
72+
paths.append(os.path.dirname(src))
73+
74+
paths = os.pathsep.join(paths)
75+
76+
return venv_path, paths
77+
78+
return _create_venv
79+
80+
@pytest.fixture
81+
def setup_program(tmp_path_factory, snippet_dir):
82+
def _setup_program(dirname, venv_dir_and_paths, python_snippet):
83+
venv_dir, paths = venv_dir_and_paths
84+
olddir = os.getcwd()
85+
workdir = tmp_path_factory.mktemp("ffi-", numbered=True)
86+
python_file = workdir.joinpath('x.py')
87+
python_file.write_text(textwrap.dedent(python_snippet))
88+
try:
89+
os.chdir(str(snippet_dir.joinpath(dirname)))
90+
if os.name == 'nt':
91+
bindir = 'Scripts'
92+
else:
93+
bindir = 'bin'
94+
95+
venv_python = str(venv_dir.joinpath(bindir).joinpath('python'))
96+
env = os.environ.copy()
97+
env['PYTHONPATH'] = paths
98+
subprocess.check_call((venv_python, 'setup.py', 'clean'), env=env)
99+
# there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and
100+
# we're in the same directory with the build (it tries to look up dependencies for itself on PyPI);
101+
# subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now.
102+
subprocess.check_call((venv_python, '-m', 'pip', 'install', '.'), env=env)
103+
subprocess.check_call((venv_python, str(python_file)), env=env)
104+
finally:
105+
os.chdir(olddir)
106+
107+
return _setup_program
197108

109+
@pytest.fixture
110+
def run_setup_and_program(tmp_path, create_venv, snippet_dir, setup_program):
111+
def _run_setup_and_program(dirname, python_snippet):
112+
venv_dir_and_paths = create_venv(dirname + '-cpy')
113+
setup_program(dirname, venv_dir_and_paths, python_snippet)
114+
115+
sys._force_generic_engine_ = True
116+
try:
117+
venv_dir = create_venv(dirname + '-gen')
118+
setup_program(dirname, venv_dir, python_snippet)
198119
finally:
199-
setuptools.__version__ = orig_version
120+
del sys._force_generic_engine_
121+
122+
# the two files lextab.py and yacctab.py are created by not-correctly-
123+
# installed versions of pycparser.
124+
assert not os.path.exists(str(snippet_dir.joinpath(dirname, 'lextab.py')))
125+
assert not os.path.exists(str(snippet_dir.joinpath(dirname, 'yacctab.py')))
126+
127+
return _run_setup_and_program
128+
129+
130+
def test_infrastructure(run_setup_and_program):
131+
run_setup_and_program('infrastructure', '''
132+
import snip_infrastructure
133+
assert snip_infrastructure.func() == 42
134+
''')
135+
136+
def test_distutils_module(run_setup_and_program):
137+
run_setup_and_program("distutils_module", '''
138+
import snip_basic_verify
139+
p = snip_basic_verify.C.getpwuid(0)
140+
assert snip_basic_verify.ffi.string(p.pw_name) == b"root"
141+
''')
142+
143+
def test_distutils_package_1(run_setup_and_program):
144+
run_setup_and_program("distutils_package_1", '''
145+
import snip_basic_verify1
146+
p = snip_basic_verify1.C.getpwuid(0)
147+
assert snip_basic_verify1.ffi.string(p.pw_name) == b"root"
148+
''')
149+
150+
def test_distutils_package_2(run_setup_and_program):
151+
run_setup_and_program("distutils_package_2", '''
152+
import snip_basic_verify2
153+
p = snip_basic_verify2.C.getpwuid(0)
154+
assert snip_basic_verify2.ffi.string(p.pw_name) == b"root"
155+
''')
156+
157+
def test_setuptools_module(run_setup_and_program):
158+
run_setup_and_program("setuptools_module", '''
159+
import snip_setuptools_verify
160+
p = snip_setuptools_verify.C.getpwuid(0)
161+
assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root"
162+
''')
163+
164+
def test_setuptools_package_1(run_setup_and_program):
165+
run_setup_and_program("setuptools_package_1", '''
166+
import snip_setuptools_verify1
167+
p = snip_setuptools_verify1.C.getpwuid(0)
168+
assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root"
169+
''')
170+
171+
def test_setuptools_package_2(run_setup_and_program):
172+
run_setup_and_program("setuptools_package_2", '''
173+
import snip_setuptools_verify2
174+
p = snip_setuptools_verify2.C.getpwuid(0)
175+
assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root"
176+
''')
177+
178+
def test_set_py_limited_api():
179+
from cffi.setuptools_ext import _set_py_limited_api
180+
try:
181+
import setuptools
182+
except ImportError as e:
183+
pytest.skip(str(e))
184+
orig_version = setuptools.__version__
185+
expecting_limited_api = not hasattr(sys, 'gettotalrefcount')
186+
try:
187+
setuptools.__version__ = '26.0.0'
188+
from setuptools import Extension
189+
190+
kwds = _set_py_limited_api(Extension, {})
191+
assert kwds.get('py_limited_api', False) == expecting_limited_api
192+
193+
setuptools.__version__ = '25.0'
194+
kwds = _set_py_limited_api(Extension, {})
195+
assert kwds.get('py_limited_api', False) == False
196+
197+
setuptools.__version__ = 'development'
198+
kwds = _set_py_limited_api(Extension, {})
199+
assert kwds.get('py_limited_api', False) == expecting_limited_api
200+
201+
finally:
202+
setuptools.__version__ = orig_version

0 commit comments

Comments
 (0)