From e3ac113ae2e2de8f9b836626ce3737a54e1ef2ba Mon Sep 17 00:00:00 2001 From: Stefano Rivera Date: Sun, 30 Nov 2025 20:07:49 -0400 Subject: [PATCH] Migrate away from the "py" module Replace py.local in udir with pathlib.Path. Change some code to be more idiomatic for pathlib on modern Python. Fixes: #216 --- testing/cffi0/test_function.py | 6 ++-- testing/cffi0/test_ownlib.py | 10 +++--- testing/cffi0/test_zdistutils.py | 37 ++++++++++---------- testing/cffi0/test_zintegration.py | 37 ++++++++++---------- testing/cffi1/test_dlopen.py | 54 ++++++++++++++--------------- testing/cffi1/test_function_args.py | 37 ++++++++++---------- testing/cffi1/test_new_ffi_1.py | 6 ++-- testing/cffi1/test_re_python.py | 18 +++++----- testing/cffi1/test_recompiler.py | 25 +++++++------ testing/cffi1/test_verify1.py | 4 +-- testing/cffi1/test_zdist.py | 19 +++++----- testing/embedding/test_basic.py | 8 +++-- testing/udir.py | 41 ++++++++++------------ 13 files changed, 150 insertions(+), 152 deletions(-) diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index d23d55fb6..afa6453d3 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -337,18 +337,18 @@ def test_function_typedef(self): def test_fputs_custom_FILE(self): if self.Backend is CTypesBackend: pytest.skip("FILE not supported with the ctypes backend") - filename = str(udir.join('fputs_custom_FILE')) + temp_file = udir / 'fputs_custom_FILE' ffi = FFI(backend=self.Backend()) ffi.cdef("int fputs(const char *, FILE *);") needs_dlopen_none() C = ffi.dlopen(None) - with open(filename, 'wb') as f: + with temp_file.open('wb') as f: f.write(b'[') C.fputs(b"hello from custom file", f) f.write(b'][') C.fputs(b"some more output", f) f.write(b']') - with open(filename, 'rb') as f: + with temp_file.open('rb') as f: res = f.read() assert res == b'[hello from custom file][some more output]' diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index a744a72c2..180a8864b 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -130,13 +130,13 @@ class TestOwnLib(object): def setup_class(cls): cls.module = None from testing.udir import udir - udir.join('testownlib.c').write(SOURCE) + (udir / 'testownlib.c').write_text(SOURCE) if sys.platform == 'win32': # did we already build it? if cls.Backend is CTypesBackend: - dll_path = str(udir) + '\\testownlib1.dll' # only ascii for the ctypes backend + dll_path = f"{udir}\\testownlib1.dll" # only ascii for the ctypes backend else: - dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll') # non-ascii char + dll_path = f"{udir}\\testownlib\u03be.dll" # non-ascii char if os.path.exists(dll_path): cls.module = dll_path return @@ -159,7 +159,7 @@ def setup_class(cls): cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \ ' /LD /Fetestownlib.dll' subprocess.check_call(cmd, cwd = str(udir), shell=True) - os.rename(str(udir) + '\\testownlib.dll', dll_path) + os.rename(f"{udir}\\testownlib.dll", dll_path) cls.module = dll_path else: encoded = None @@ -177,7 +177,7 @@ def setup_class(cls): subprocess.check_call( "cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded,), cwd=str(udir), shell=True) - cls.module = os.path.join(str(udir), unicode_name + (u+'.so')) + cls.module = f"{udir / unicode_name}.so" print(repr(cls.module)) def test_getting_errno(self): diff --git a/testing/cffi0/test_zdistutils.py b/testing/cffi0/test_zdistutils.py index 0f88a8ae0..965087f4d 100644 --- a/testing/cffi0/test_zdistutils.py +++ b/testing/cffi0/test_zdistutils.py @@ -21,9 +21,9 @@ def setup_class(self): self.lib_m = 'msvcrt' def teardown_class(self): - if udir.isdir(): - udir.remove(ignore_errors=True) - udir.ensure(dir=1) + if udir.is_dir(): + shutil.rmtree(udir) + udir.mkdir() def test_locate_engine_class(self): cls = _locate_engine_class(FFI(), self.generic) @@ -55,12 +55,11 @@ def test_write_source_explicit_filename(self): csrc = '/*hi there %s!*/\n#include \n' % self v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) - v.sourcefilename = filename = str(udir.join('write_source.c')) + source_file = udir / 'write_source.c' + v.sourcefilename = str(source_file) v.write_source() - assert filename == v.sourcefilename - with open(filename, 'r') as f: - data = f.read() - assert csrc in data + assert str(source_file) == v.sourcefilename + assert csrc in source_file.read_text() def test_write_source_to_file_obj(self): ffi = FFI() @@ -95,9 +94,10 @@ def test_compile_module_explicit_filename(self): v = Verifier(ffi, csrc, force_generic_engine=self.generic, libraries=[self.lib_m]) basename = self.__class__.__name__[:10] + '_test_compile_module' - v.modulefilename = filename = str(udir.join(basename + '.so')) + module_file = udir / (basename + '.so') + v.modulefilename = str(module_file) v.compile_module() - assert filename == v.modulefilename + assert str(module_file) == v.modulefilename assert v.get_module_name() == basename if v.generates_python_module(): mod = load_dynamic(v.get_module_name(), v.modulefilename) @@ -136,7 +136,7 @@ def test_verifier_args(self): ffi = FFI() ffi.cdef("double sin(double x);") csrc = '/*hi there %s!4*/#include "test_verifier_args.h"\n' % self - udir.join('test_verifier_args.h').write('#include \n') + (udir / 'test_verifier_args.h').write_text('#include \n') v = Verifier(ffi, csrc, include_dirs=[str(udir)], force_generic_engine=self.generic, libraries=[self.lib_m]) @@ -189,13 +189,13 @@ def test_extension_forces_write_source(self): def test_extension_object_extra_sources(self): ffi = FFI() ffi.cdef("double test1eoes(double x);") - extra_source = str(udir.join('extension_extra_sources.c')) - with open(extra_source, 'w') as f: - f.write('double test1eoes(double x) { return x * 6.0; }\n') + extra_source = udir / 'extension_extra_sources.c' + extra_source.write_text( + 'double test1eoes(double x) { return x * 6.0; }\n') csrc = '/*9%s*/' % self + ''' double test1eoes(double x); /* or #include "extra_sources.h" */ ''' - lib = ffi.verify(csrc, sources=[extra_source], + lib = ffi.verify(csrc, sources=[str(extra_source)], force_generic_engine=self.generic) assert lib.test1eoes(7.0) == 42.0 v = ffi.verifier @@ -203,7 +203,7 @@ def test_extension_object_extra_sources(self): assert 'distutils.extension.Extension' in str(ext.__class__) or \ 'setuptools.extension.Extension' in str(ext.__class__) assert ext.sources == [maybe_relative_path(v.sourcefilename), - extra_source] + str(extra_source)] assert ext.name == v.get_module_name() def test_install_and_reload_module(self, targetpackage='', ext_package=''): @@ -212,7 +212,8 @@ def test_install_and_reload_module(self, targetpackage='', ext_package=''): pytest.skip("test requires os.fork()") if targetpackage: - udir.ensure(targetpackage, dir=1).ensure('__init__.py') + (udir / targetpackage).mkdir(exist_ok=True) + (udir / targetpackage / "__init__.py").touch(exist_ok=True) sys.path.insert(0, str(udir)) def make_ffi(**verifier_args): @@ -232,7 +233,7 @@ def make_ffi(**verifier_args): assert lib.test1iarm(1.5) == 63.0 # "install" the module by moving it into udir (/targetpackage) if targetpackage: - target = udir.join(targetpackage) + target = udir / targetpackage else: target = udir shutil.move(ffi.verifier.modulefilename, str(target)) diff --git a/testing/cffi0/test_zintegration.py b/testing/cffi0/test_zintegration.py index 3daf4a5c9..e0152fb21 100644 --- a/testing/cffi0/test_zintegration.py +++ b/testing/cffi0/test_zintegration.py @@ -1,7 +1,8 @@ -import py, os, sys, shutil +import os, sys, shutil import subprocess import sysconfig import textwrap +from pathlib import Path from testing.udir import udir import pytest @@ -13,7 +14,7 @@ ' in a non-2.6-friendly way') def create_venv(name): - tmpdir = udir.join(name) + tmpdir = udir / 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 @@ -27,7 +28,7 @@ def create_venv(name): # Newer venv/virtualenv no longer include setuptools by default, which # breaks a number of these tests; ensure they're always present subprocess.check_call([ - os.path.join(tmpdir, 'bin/python'), + str(tmpdir / 'bin/python'), '-m', 'pip', 'install', @@ -39,8 +40,8 @@ def create_venv(name): 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': + for dirpath, dirnames, filenames in os.walk(tmpdir): + if Path(dirpath).name == 'site-packages': site_packages = dirpath break paths = "" @@ -70,27 +71,27 @@ def create_venv(name): paths = os.pathsep.join(paths) return tmpdir, paths -SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets') +SNIPPET_DIR = Path(__file__).absolute().parent / '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)) + dir = str(SNIPPET_DIR / 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__')) + for basedir in (SNIPPET_DIR / dirname).iterdir(): + remove(basedir / '__pycache__') olddir = os.getcwd() - python_f = udir.join('x.py') - python_f.write(textwrap.dedent(python_snippet)) + python_f = udir / 'x.py' + python_f.write_text(textwrap.dedent(python_snippet)) try: - os.chdir(str(SNIPPET_DIR.join(dirname))) + os.chdir(str(SNIPPET_DIR / dirname)) if os.name == 'nt': bindir = 'Scripts' else: bindir = 'bin' - vp = str(venv_dir.join(bindir).join('python')) + vp = str(venv_dir / bindir / 'python') env = os.environ.copy() env['PYTHONPATH'] = paths subprocess.check_call((vp, 'setup.py', 'clean'), env=env) @@ -114,15 +115,15 @@ def run_setup_and_program(dirname, python_snippet): 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'))) + assert not (SNIPPET_DIR / dirname / 'lextab.py').exists() + assert not (SNIPPET_DIR / dirname / 'yacctab.py').exists() @pytest.mark.thread_unsafe(reason="very slow in parallel") class TestZIntegration(object): def teardown_class(self): - if udir.isdir(): - udir.remove(ignore_errors=True) - udir.ensure(dir=1) + if udir.is_dir(): + shutil.rmtree(udir) + udir.mkdir() def test_infrastructure(self): run_setup_and_program('infrastructure', ''' diff --git a/testing/cffi1/test_dlopen.py b/testing/cffi1/test_dlopen.py index ae9b5731f..1753bc99e 100644 --- a/testing/cffi1/test_dlopen.py +++ b/testing/cffi1/test_dlopen.py @@ -10,9 +10,9 @@ def test_simple(): ffi = FFI() ffi.cdef("int close(int); static const int BB = 42; extern int somevar;") - target = udir.join('test_simple.py') + target = udir / 'test_simple.py' make_py_source(ffi, 'test_simple', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_simple', @@ -25,9 +25,9 @@ def test_simple(): def test_global_constant(): ffi = FFI() ffi.cdef("static const long BB; static const float BF = 12;") - target = udir.join('test_valid_global_constant.py') + target = udir / 'test_valid_global_constant.py' make_py_source(ffi, 'test_valid_global_constant', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_valid_global_constant', @@ -46,7 +46,7 @@ def test_invalid_global_constant_3(): def test_invalid_dotdotdot_in_macro(): ffi = FFI() ffi.cdef("#define FOO ...") - target = udir.join('test_invalid_dotdotdot_in_macro.py') + target = udir / 'test_invalid_dotdotdot_in_macro.py' e = pytest.raises(VerificationError, make_py_source, ffi, 'test_invalid_dotdotdot_in_macro', str(target)) assert str(e.value) == ("macro FOO: cannot use the syntax '...' in " @@ -55,9 +55,9 @@ def test_invalid_dotdotdot_in_macro(): def test_typename(): ffi = FFI() ffi.cdef("typedef int foobar_t;") - target = udir.join('test_typename.py') + target = udir / 'test_typename.py' make_py_source(ffi, 'test_typename', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_typename', @@ -70,9 +70,9 @@ def test_typename(): def test_enum(): ffi = FFI() ffi.cdef("enum myenum_e { AA, BB, CC=-42 };") - target = udir.join('test_enum.py') + target = udir / 'test_enum.py' make_py_source(ffi, 'test_enum', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_enum', @@ -86,9 +86,9 @@ def test_enum(): def test_struct(): ffi = FFI() ffi.cdef("struct foo_s { int a; signed char b[]; }; struct bar_s;") - target = udir.join('test_struct.py') + target = udir / 'test_struct.py' make_py_source(ffi, 'test_struct', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_struct', @@ -102,9 +102,9 @@ def test_include(): ffi = FFI() ffi.cdef("#define ABC 123") ffi.set_source('test_include', None) - target = udir.join('test_include.py') + target = udir / 'test_include.py' make_py_source(ffi, 'test_include', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_include', @@ -116,9 +116,9 @@ def test_include(): # ffi2 = FFI() ffi2.include(ffi) - target2 = udir.join('test2_include.py') + target2 = udir / 'test2_include.py' make_py_source(ffi2, 'test2_include', str(target2)) - assert target2.read() == r"""# auto-generated file + assert target2.read_text() == r"""# auto-generated file import _cffi_backend from test_include import ffi as _ffi0 @@ -132,9 +132,9 @@ def test_include(): def test_negative_constant(): ffi = FFI() ffi.cdef("static const int BB = -42;") - target = udir.join('test_negative_constant.py') + target = udir / 'test_negative_constant.py' make_py_source(ffi, 'test_negative_constant', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_negative_constant', @@ -151,9 +151,9 @@ def test_struct_included(): # ffi = FFI() ffi.include(baseffi) - target = udir.join('test_struct_included.py') + target = udir / 'test_struct_included.py' make_py_source(ffi, 'test_struct_included', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend from test_struct_included_base import ffi as _ffi0 @@ -171,16 +171,16 @@ def test_no_cross_include(): # ffi = FFI() ffi.include(baseffi) - target = udir.join('test_no_cross_include.py') + target = udir / 'test_no_cross_include.py' pytest.raises(VerificationError, make_py_source, ffi, 'test_no_cross_include', str(target)) def test_array(): ffi = FFI() ffi.cdef("typedef int32_t my_array_t[42];") - target = udir.join('test_array.py') + target = udir / 'test_array.py' make_py_source(ffi, 'test_array', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_array', @@ -193,16 +193,16 @@ def test_array(): def test_array_overflow(): ffi = FFI() ffi.cdef("typedef int32_t my_array_t[3000000000];") - target = udir.join('test_array_overflow.py') + target = udir / 'test_array_overflow.py' pytest.raises(OverflowError, make_py_source, ffi, 'test_array_overflow', str(target)) def test_global_var(): ffi = FFI() ffi.cdef("extern int myglob;") - target = udir.join('test_global_var.py') + target = udir / 'test_global_var.py' make_py_source(ffi, 'test_global_var', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_global_var', @@ -215,9 +215,9 @@ def test_global_var(): def test_bitfield(): ffi = FFI() ffi.cdef("struct foo_s { int y:10; short x:5; };") - target = udir.join('test_bitfield.py') + target = udir / 'test_bitfield.py' make_py_source(ffi, 'test_bitfield', str(target)) - assert target.read() == r"""# auto-generated file + assert target.read_text() == r"""# auto-generated file import _cffi_backend ffi = _cffi_backend.FFI('test_bitfield', diff --git a/testing/cffi1/test_function_args.py b/testing/cffi1/test_function_args.py index 4e7e8c3b7..38816e0f2 100644 --- a/testing/cffi1/test_function_args.py +++ b/testing/cffi1/test_function_args.py @@ -117,16 +117,15 @@ def build_type(tp): if False: from testing.udir import udir import subprocess - f = open(str(udir.join('run1.py')), 'w') - f.write('import sys; sys.path = %r\n' % (sys.path,)) - f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % - TEST_RUN_COUNTER) - for i in range(len(args)): - f.write('a%d = ffi.new("%s *")\n' % (i, args[i])) - aliststr = ', '.join(['a%d[0]' % i for i in range(len(args))]) - f.write('lib.testfargs(%s)\n' % aliststr) - f.write('ffi.addressof(lib, "testfargs")(%s)\n' % aliststr) - f.close() + with (udir / 'run1.py').open('w') as f: + f.write('import sys; sys.path = %r\n' % (sys.path,)) + f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % + TEST_RUN_COUNTER) + for i in range(len(args)): + f.write('a%d = ffi.new("%s *")\n' % (i, args[i])) + aliststr = ', '.join(['a%d[0]' % i for i in range(len(args))]) + f.write('lib.testfargs(%s)\n' % aliststr) + f.write('ffi.addressof(lib, "testfargs")(%s)\n' % aliststr) print("checking for segfault for direct call...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc @@ -185,15 +184,15 @@ def expand(value): if False: from testing.udir import udir import subprocess - f = open(str(udir.join('run1.py')), 'w') - f.write('import sys; sys.path = %r\n' % (sys.path,)) - f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % - TEST_RUN_COUNTER) - f.write('def callback(*args): return ffi.new("%s *")[0]\n' % result) - f.write('fptr = ffi.callback("%s(%s)", callback)\n' % (result, - ','.join(args))) - f.write('print(lib.testfcallback(fptr))\n') - f.close() + with (udir / 'run1.py').open('w') as f: + f.write('import sys; sys.path = %r\n' % (sys.path,)) + f.write('from _CFFI_test_function_args_%d import ffi, lib\n' % + TEST_RUN_COUNTER) + f.write('def callback(*args): return ffi.new("%s *")[0]\n' + % result) + f.write('fptr = ffi.callback("%s(%s)", callback)\n' + % (result, ','.join(args))) + f.write('print(lib.testfcallback(fptr))\n') print("checking for segfault for callback...") rc = subprocess.call([sys.executable, 'run1.py'], cwd=str(udir)) assert rc == 0, rc diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py index 0eb0e71c7..b8db8af9a 100644 --- a/testing/cffi1/test_new_ffi_1.py +++ b/testing/cffi1/test_new_ffi_1.py @@ -1775,9 +1775,9 @@ def test_all_primitives(self): def test_emit_c_code(self): ffi = cffi.FFI() ffi.set_source("foobar", "??") - c_file = str(udir.join('test_emit_c_code')) - ffi.emit_c_code(c_file) - assert os.path.isfile(c_file) + c_file = udir / 'test_emit_c_code' + ffi.emit_c_code(str(c_file)) + assert c_file.is_file() @pytest.mark.thread_unsafe(reason="workers would share a compilation directory") def test_emit_c_code_to_file_obj(self): diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py index 11247d490..fbca325e2 100644 --- a/testing/cffi1/test_re_python.py +++ b/testing/cffi1/test_re_python.py @@ -25,10 +25,10 @@ def setup_module(mod): void init_test_re_python(void) { } /* windows hack */ void PyInit__test_re_python(void) { } /* windows hack */ """ - tmpdir = udir.join('test_re_python') - tmpdir.ensure(dir=1) - c_file = tmpdir.join('_test_re_python.c') - c_file.write(SRC) + tmpdir = udir / 'test_re_python' + tmpdir.mkdir(exist_ok=True) + c_file = tmpdir/ '_test_re_python.c' + c_file.write_text(SRC) ext = ffiplatform.get_extension( str(c_file), '_test_re_python', @@ -79,7 +79,7 @@ def setup_module(mod): typedef struct selfref { struct selfref *next; } *selfref_ptr_t; """) ffi.set_source('re_python_pysrc', None) - ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py'))) + ffi.emit_python_code(str(tmpdir / 're_python_pysrc.py')) mod.original_ffi = ffi # sys.path.insert(0, str(tmpdir)) @@ -176,7 +176,7 @@ def test_include_1(): assert 'macro FOOBAR' in original_ffi._parser._declarations assert 'macro FOOBAZ' in original_ffi._parser._declarations sub_ffi.set_source('re_python_pysrc', None) - sub_ffi.emit_python_code(str(tmpdir.join('_re_include_1.py'))) + sub_ffi.emit_python_code(str(tmpdir / '_re_include_1.py')) # if sys.version_info[:2] >= (3, 3): import importlib @@ -251,7 +251,7 @@ def test_partial_enum(): ffi.cdef("enum foo { A, B, ... };") ffi.set_source('test_partial_enum', None) pytest.raises(VerificationMissing, ffi.emit_python_code, - str(tmpdir.join('test_partial_enum.py'))) + str(tmpdir / 'test_partial_enum.py')) def test_anonymous_union_inside_struct(): # based on issue #357 @@ -310,7 +310,7 @@ def test_rec_structs_1(): ffi = FFI() ffi.cdef("struct B { struct C* c; }; struct C { struct B b; };") ffi.set_source('test_rec_structs_1', None) - ffi.emit_python_code(str(tmpdir.join('_rec_structs_1.py'))) + ffi.emit_python_code(str(tmpdir / '_rec_structs_1.py')) # if sys.version_info[:2] >= (3, 3): import importlib @@ -327,7 +327,7 @@ def test_rec_structs_2(): ffi = FFI() ffi.cdef("struct B { struct C* c; }; struct C { struct B b; };") ffi.set_source('test_rec_structs_2', None) - ffi.emit_python_code(str(tmpdir.join('_rec_structs_2.py'))) + ffi.emit_python_code(tmpdir / '_rec_structs_2.py') # if sys.version_info[:2] >= (3, 3): import importlib diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index 5d286f999..a789483c2 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -550,13 +550,13 @@ def test_module_name_in_package(): tmpdir=str(udir)) old_sys_path = sys.path[:] try: - package_dir = udir.join('test_module_name_in_package') - for name in os.listdir(str(udir)): - assert not name.startswith('test_module_name_in_package.') - assert os.path.isdir(str(package_dir)) - assert len(os.listdir(str(package_dir))) > 0 - assert os.path.exists(str(package_dir.join('mymod.c'))) - package_dir.join('__init__.py').write('') + package_dir = udir / 'test_module_name_in_package' + for item in udir.iterdir(): + assert not item.name.startswith('test_module_name_in_package.') + assert package_dir.is_dir() + assert next(package_dir.iterdir()) + assert (package_dir / 'mymod.c').is_file() + (package_dir / '__init__.py').touch() # getattr(importlib, 'invalidate_caches', object)() # @@ -1002,9 +1002,9 @@ def test_variable_of_unknown_size(): assert ffi.string(ffi.cast("char *", p), 8) == b"hello" def test_constant_of_value_unknown_to_the_compiler(): - extra_c_source = udir.join( - 'extra_test_constant_of_value_unknown_to_the_compiler.c') - extra_c_source.write('const int external_foo = 42;\n') + extra_c_source = ( + udir / 'extra_test_constant_of_value_unknown_to_the_compiler.c') + extra_c_source.write_text('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_constant_of_value_unknown_to_the_compiler', """ @@ -1013,9 +1013,8 @@ def test_constant_of_value_unknown_to_the_compiler(): assert lib.external_foo == 42 def test_dotdot_in_source_file_names(): - extra_c_source = udir.join( - 'extra_test_dotdot_in_source_file_names.c') - extra_c_source.write('const int external_foo = 42;\n') + extra_c_source = udir / 'extra_test_dotdot_in_source_file_names.c' + extra_c_source.write_text('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_dotdot_in_source_file_names', """ diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py index 6255beb03..9c6d29bea 100644 --- a/testing/cffi1/test_verify1.py +++ b/testing/cffi1/test_verify1.py @@ -2258,8 +2258,8 @@ def test_windows_dllimport_data(): if sys.platform != 'win32': pytest.skip("Windows only") from testing.udir import udir - tmpfile = udir.join('dllimport_data.c') - tmpfile.write('int my_value = 42;\n') + tmpfile = udir / 'dllimport_data.c' + tmpfile.write_text('int my_value = 42;\n') ffi = FFI() ffi.cdef("int my_value;") lib = ffi.verify("extern __declspec(dllimport) int my_value;", diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py index 4dee1aa7c..1b08980f6 100644 --- a/testing/cffi1/test_zdist.py +++ b/testing/cffi1/test_zdist.py @@ -26,8 +26,8 @@ def setup_method(self, meth): self.executable = os.path.abspath(sys.executable) self.rootdir = os.path.abspath(os.path.dirname(os.path.dirname( cffi.__file__))) - self.udir = udir.join(meth.__name__) - os.mkdir(str(self.udir)) + self.udir = udir / meth.__name__ + self.udir.mkdir() if meth.chdir_to_tmp: self.saved_cwd = os.getcwd() os.chdir(str(self.udir)) @@ -125,7 +125,7 @@ def test_abi_emit_python_code_2(self): def test_abi_emit_python_code_3(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", None) - ffi.emit_python_code(str(self.udir.join('xyt.py'))) + ffi.emit_python_code(str(self.udir / 'xyt.py')) self.check_produced_files({'xyt.py': None}) @chdir_to_tmp @@ -149,7 +149,7 @@ def test_abi_compile_2(self): def test_abi_compile_3(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", None) - tmpdir = str(self.udir.join('build3')) + tmpdir = str(self.udir / 'build3') x = ffi.compile(tmpdir) self.check_produced_files({'build3': { 'mod_name_in_package': {'mymod.py': None}}}) @@ -172,7 +172,7 @@ def test_api_emit_c_code_2(self): def test_api_emit_c_code_3(self): ffi = cffi.FFI() ffi.set_source("package_name_1.mymod", "/*code would be here*/") - ffi.emit_c_code(str(self.udir.join('xyu.c'))) + ffi.emit_c_code(str(self.udir / 'xyu.c')) self.check_produced_files({'xyu.c': None}) @chdir_to_tmp @@ -213,7 +213,7 @@ def test_api_compile_2(self): def test_api_compile_3(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - x = ffi.compile(str(self.udir.join('foo'))) + x = ffi.compile(str(self.udir / 'foo')) if sys.platform != 'win32': sofile = self.check_produced_files({ 'foo': {'mod_name_in_package': {'mymod.SO': None, @@ -253,8 +253,7 @@ def test_api_compile_explicit_target_3(self): 'mod_name_in_package': {'foo.bar.baz': None, 'mymod.c': None, 'mymod.o': None}}) - sofile = os.path.join(str(self.udir), - 'mod_name_in_package', 'foo.bar.baz') + sofile = self.udir / 'mod_name_in_package' / 'foo.bar.baz' assert os.path.isabs(x) and os.path.samefile(x, sofile) else: self.check_produced_files({ @@ -277,12 +276,12 @@ def test_api_distutils_extension_1(self): def test_api_distutils_extension_2(self): ffi = cffi.FFI() ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") - ext = ffi.distutils_extension(str(self.udir.join('foo'))) + ext = ffi.distutils_extension(str(self.udir / 'foo')) self.check_produced_files({'foo': { 'mod_name_in_package': {'mymod.c': None}}}) if hasattr(os.path, 'samefile'): assert os.path.samefile(ext.sources[0], - str(self.udir.join('foo/mod_name_in_package/mymod.c'))) + str(self.udir / 'foo' / 'mod_name_in_package' / 'mymod.c')) def _make_distutils_api(self): diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py index 9f55b7993..2f7648c6a 100644 --- a/testing/embedding/test_basic.py +++ b/testing/embedding/test_basic.py @@ -44,13 +44,15 @@ class EmbeddingTests: _compiled_modules = {} def setup_method(self, meth): - check_lib_python_found(str(udir.ensure('embedding', dir=1))) - self._path = udir.join('embedding', meth.__name__) + (embedding_path := udir / 'embedding').mkdir(exist_ok=True) + check_lib_python_found(str(embedding_path)) + self._path = embedding_path / meth.__name__ if sys.platform == "win32" or sys.platform == "darwin": self._compiled_modules.clear() # workaround def get_path(self): - return str(self._path.ensure(dir=1)) + self._path.mkdir(exist_ok=True) + return str(self._path) def _run_base(self, args, **kwds): print('RUNNING:', args, kwds) diff --git a/testing/udir.py b/testing/udir.py index 59db1c4e9..d5f2b88be 100644 --- a/testing/udir.py +++ b/testing/udir.py @@ -1,5 +1,5 @@ -import py -import sys, os, atexit +import sys, tempfile, os, atexit +from pathlib import Path # This is copied from PyPy's vendored py lib. The latest py lib release @@ -15,11 +15,11 @@ def make_numbered_dir(prefix='session-', rootdir=None, keep=3, will be removed. """ if rootdir is None: - rootdir = py.path.local.get_temproot() + rootdir = Path(tempfile.gettempdir()) def parse_num(path): """ parse the number out of a path (if it matches the prefix) """ - bn = path.basename + bn = path.name if bn.startswith(prefix): try: return int(bn[len(prefix):]) @@ -31,15 +31,15 @@ def parse_num(path): lastmax = None while True: maxnum = -1 - for path in rootdir.listdir(): + for path in rootdir.iterdir(): num = parse_num(path) if num is not None: maxnum = max(maxnum, num) # make the new directory try: - udir = rootdir.mkdir(prefix + str(maxnum+1)) - except py.error.EEXIST: + (udir := rootdir / (prefix + str(maxnum+1))).mkdir() + except FileExistsError: # race condition: another thread/process created the dir # in the meantime. Try counting again if lastmax == maxnum: @@ -51,12 +51,9 @@ def parse_num(path): # put a .lock file in the new directory that will be removed at # process exit if lock_timeout: - lockfile = udir.join('.lock') + lockfile = udir / '.lock' mypid = os.getpid() - if hasattr(lockfile, 'mksymlinkto'): - lockfile.mksymlinkto(str(mypid)) - else: - lockfile.write(str(mypid)) + lockfile.symlink_to(str(mypid)) def try_remove_lockfile(): # in a fork() situation, only the last process should # remove the .lock, otherwise the other processes run the @@ -66,14 +63,14 @@ def try_remove_lockfile(): if os.getpid() != mypid: return try: - lockfile.remove() - except py.error.Error: + lockfile.unlink() + except FileNotFoundError: pass atexit.register(try_remove_lockfile) # prune old directories if keep: - for path in rootdir.listdir(): + for path in rootdir.iterdir(): num = parse_num(path) if num is not None and num <= (maxnum - keep): if min_timeout: @@ -84,19 +81,19 @@ def try_remove_lockfile(): # last process notices the first dir doesn't have # (yet) a .lock in it and kills it. try: - t1 = path.lstat().mtime - t2 = lockfile.lstat().mtime + t1 = path.lstat().st_mtime + t2 = lockfile.lstat().st_mtime if abs(t2-t1) < min_timeout: continue # skip directories too recent - except py.error.Error: + except FileNotFoundError: continue # failure to get a time, better skip - lf = path.join('.lock') + lf = path / '.lock' try: - t1 = lf.lstat().mtime - t2 = lockfile.lstat().mtime + t1 = lf.lstat().st_mtime + t2 = lockfile.lstat().st_mtime if not lock_timeout or abs(t2-t1) < lock_timeout: continue # skip directories still locked - except py.error.Error: + except FileNotFoundError: pass # assume that it means that there is no 'lf' try: path.remove(rec=1)