From 5cbf313857d11934bb341f22b7e0be2cc5454cc1 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:07:31 +0100 Subject: [PATCH 01/14] Apply ruff/pyupgrade rule UP004 UP004 Class inherits from `object` --- demo/_curses.py | 2 +- demo/api.py | 2 +- demo/pyobj.py | 2 +- src/c/test_c.py | 6 +++--- src/cffi/api.py | 4 ++-- src/cffi/backend_ctypes.py | 6 +++--- src/cffi/cffi_opcode.py | 2 +- src/cffi/cparser.py | 2 +- src/cffi/model.py | 2 +- src/cffi/vengine_cpy.py | 4 ++-- src/cffi/vengine_gen.py | 2 +- src/cffi/verifier.py | 2 +- testing/cffi0/test_cdata.py | 4 ++-- testing/cffi0/test_function.py | 4 ++-- testing/cffi0/test_ownlib.py | 2 +- testing/cffi0/test_parsing.py | 10 +++++----- testing/cffi0/test_verify.py | 6 +++--- testing/cffi0/test_zdistutils.py | 2 +- testing/cffi0/test_zintegration.py | 2 +- testing/cffi1/test_verify1.py | 6 +++--- testing/cffi1/test_zdist.py | 2 +- testing/support.py | 6 +++--- 22 files changed, 40 insertions(+), 40 deletions(-) diff --git a/demo/_curses.py b/demo/_curses.py index d5e993f4b..60a21e8fa 100644 --- a/demo/_curses.py +++ b/demo/_curses.py @@ -219,7 +219,7 @@ class error(Exception): pass -class Window(object): +class Window: def __init__(self, window): self._win = window diff --git a/demo/api.py b/demo/api.py index a6c072415..b40af9bfe 100644 --- a/demo/api.py +++ b/demo/api.py @@ -35,7 +35,7 @@ def verify(self, source='', **kwargs): return lib -class _PyExport(object): +class _PyExport: def __init__(self, tp, func): self.tp = tp self.func = func diff --git a/demo/pyobj.py b/demo/pyobj.py index 922f6da6c..5f2cdf4bb 100644 --- a/demo/pyobj.py +++ b/demo/pyobj.py @@ -24,7 +24,7 @@ def discard(p): freelist = p return x -class Ref(object): +class Ref: """For use in 'with Ref(x) as ob': open an object descriptor and returns it in 'ob', and close it automatically when the 'with' statement finishes.""" diff --git a/src/c/test_c.py b/src/c/test_c.py index b21144353..d4c666a1a 100644 --- a/src/c/test_c.py +++ b/src/c/test_c.py @@ -75,7 +75,7 @@ def _capture_unraisable_hook(ur_args): mandatory_u_prefix = 'u' bytechr = chr bitem2bchr = lambda x: x - class U(object): + class U: def __add__(self, other): return eval('u'+repr(other).replace(r'\\u', r'\u') .replace(r'\\U', r'\U')) @@ -3322,7 +3322,7 @@ def test_new_handle_cycle(): import gc import _weakref BVoidP = new_pointer_type(new_void_type()) - class A(object): + class A: pass o = A() o.cycle = newp_handle(BVoidP, o) @@ -3849,7 +3849,7 @@ def check(methods, expected, expected_for_memoryview=None): return if expected_for_memoryview is not None: expected = expected_for_memoryview - class X(object): + class X: pass _testbuff(X, methods) bufobj = X() diff --git a/src/cffi/api.py b/src/cffi/api.py index 5a474f3da..830aef84a 100644 --- a/src/cffi/api.py +++ b/src/cffi/api.py @@ -20,7 +20,7 @@ -class FFI(object): +class FFI: r''' The main top-level class that you instantiate once, or once per module. @@ -909,7 +909,7 @@ def make_accessor(name): raise AttributeError(name) accessors[name](name) # - class FFILibrary(object): + class FFILibrary: def __getattr__(self, name): make_accessor(name) return getattr(self, name) diff --git a/src/cffi/backend_ctypes.py b/src/cffi/backend_ctypes.py index e7956a79c..01374149c 100644 --- a/src/cffi/backend_ctypes.py +++ b/src/cffi/backend_ctypes.py @@ -12,7 +12,7 @@ class CTypesType(type): pass -class CTypesData(object): +class CTypesData: __metaclass__ = CTypesType __slots__ = ['__weakref__'] __name__ = '' @@ -270,7 +270,7 @@ def __repr__(self, c_name=None): return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) -class CTypesBackend(object): +class CTypesBackend: PRIMITIVE_TYPES = { 'char': ctypes.c_char, @@ -1094,7 +1094,7 @@ def rawaddressof(self, BTypePtr, cdata, offset=None): return BTypePtr._from_ctypes(ptr) -class CTypesLibrary(object): +class CTypesLibrary: def __init__(self, backend, cdll): self.backend = backend diff --git a/src/cffi/cffi_opcode.py b/src/cffi/cffi_opcode.py index 6421df621..c964ba13e 100644 --- a/src/cffi/cffi_opcode.py +++ b/src/cffi/cffi_opcode.py @@ -1,6 +1,6 @@ from .error import VerificationError -class CffiOp(object): +class CffiOp: def __init__(self, op, arg): self.op = op self.arg = arg diff --git a/src/cffi/cparser.py b/src/cffi/cparser.py index dd590d874..2d78fc4be 100644 --- a/src/cffi/cparser.py +++ b/src/cffi/cparser.py @@ -292,7 +292,7 @@ def _common_type_names(csource): return words_used -class Parser(object): +class Parser: def __init__(self): self._declarations = {} diff --git a/src/cffi/model.py b/src/cffi/model.py index e5f4cae3e..3b229b942 100644 --- a/src/cffi/model.py +++ b/src/cffi/model.py @@ -22,7 +22,7 @@ def qualify(quals, replace_with): return replace_with -class BaseTypeByIdentity(object): +class BaseTypeByIdentity: is_array_type = False is_raw_function = False diff --git a/src/cffi/vengine_cpy.py b/src/cffi/vengine_cpy.py index 540e87edf..4d2ea43e5 100644 --- a/src/cffi/vengine_cpy.py +++ b/src/cffi/vengine_cpy.py @@ -7,7 +7,7 @@ from . import _imp_emulation as imp -class VCPythonEngine(object): +class VCPythonEngine: _class_key = 'x' _gen_python_module = True @@ -166,7 +166,7 @@ def load_library(self, flags=None): # it will invoke the chained list of functions that will really # build (notably) the constant objects, as if they are # pointers, and store them as attributes on the 'library' object. - class FFILibrary(object): + class FFILibrary: _cffi_python_module = module _cffi_ffi = self.ffi _cffi_dir = [] diff --git a/src/cffi/vengine_gen.py b/src/cffi/vengine_gen.py index bffc82122..0209c794a 100644 --- a/src/cffi/vengine_gen.py +++ b/src/cffi/vengine_gen.py @@ -8,7 +8,7 @@ from .error import VerificationError -class VGenericEngine(object): +class VGenericEngine: _class_key = 'g' _gen_python_module = False diff --git a/src/cffi/verifier.py b/src/cffi/verifier.py index e392a2b7f..97634066b 100644 --- a/src/cffi/verifier.py +++ b/src/cffi/verifier.py @@ -27,7 +27,7 @@ def write(self, s): super(NativeIO, self).write(s) -class Verifier(object): +class Verifier: def __init__(self, ffi, preamble, tmpdir=None, modulename=None, ext_package=None, tag='', force_generic_engine=False, diff --git a/testing/cffi0/test_cdata.py b/testing/cffi0/test_cdata.py index b044c0af2..9601b812d 100644 --- a/testing/cffi0/test_cdata.py +++ b/testing/cffi0/test_cdata.py @@ -1,6 +1,6 @@ from cffi import FFI -class FakeBackend(object): +class FakeBackend: def nonstandard_integer_types(self): return {} @@ -28,7 +28,7 @@ def _get_types(self): buffer = "buffer type" -class FakeType(object): +class FakeType: def __init__(self, cdecl): self.cdecl = cdecl diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index afa6453d3..b14a62b54 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -28,7 +28,7 @@ elif is_musl: lib_m = 'c' -class TestFunction(object): +class TestFunction: Backend = CTypesBackend def test_sin(self): @@ -430,7 +430,7 @@ def make_callback(data): # Ref cycle: callback -> lambda (closure) -> container -> callback return callback - class Data(object): + class Data: pass ffi = FFI(backend=self.Backend()) data = Data() diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index 180a8864b..af52f766b 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -124,7 +124,7 @@ """ @pytest.mark.thread_unsafe(reason="Parallel tests would share a build directory") -class TestOwnLib(object): +class TestOwnLib: Backend = CTypesBackend def setup_class(cls): diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py index f10b989a1..8d4dd016f 100644 --- a/testing/cffi0/test_parsing.py +++ b/testing/cffi0/test_parsing.py @@ -5,7 +5,7 @@ from testing.support import is_musl -class FakeBackend(object): +class FakeBackend: def nonstandard_integer_types(self): return {} @@ -53,24 +53,24 @@ def _get_types(self): buffer = "buffer type" -class FakeType(object): +class FakeType: def __init__(self, cdecl): self.cdecl = cdecl def __str__(self): return self.cdecl -class FakeStruct(object): +class FakeStruct: def __init__(self, name): self.name = name def __str__(self): return ', '.join([str(y) + str(x) for x, y, z in self.fields]) -class FakeLibrary(object): +class FakeLibrary: def load_function(self, BType, name): return FakeFunction(BType, name) -class FakeFunction(object): +class FakeFunction: def __init__(self, BType, name): self.BType = str(BType) diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py index dfe3f9331..79fe473db 100644 --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -1502,7 +1502,7 @@ def test_bool(): assert int(ffi.cast("_Bool", 10**200)) == 1 assert int(ffi.cast("_Bool", 10**40000)) == 1 # - class Foo(object): + class Foo: def __int__(self): self.seen = 1 return result @@ -2520,7 +2520,7 @@ def test_ffi_gc_size_arg_2(): lib = ffi.verify(r""" #include """) - class X(object): + class X: pass for i in range(2000): p = lib.malloc(50*1024*1024) # 50 MB @@ -2540,7 +2540,7 @@ def test_ffi_new_with_cycles(): ffi = FFI() ffi.cdef("") lib = ffi.verify("") - class X(object): + class X: pass for i in range(2000): p = ffi.new("char[]", 50*1024*1024) # 50 MB diff --git a/testing/cffi0/test_zdistutils.py b/testing/cffi0/test_zdistutils.py index 965087f4d..c4b2db14b 100644 --- a/testing/cffi0/test_zdistutils.py +++ b/testing/cffi0/test_zdistutils.py @@ -11,7 +11,7 @@ ] -class DistUtilsTest(object): +class DistUtilsTest: def setup_class(self): self.lib_m = "m" if sys.platform == 'win32': diff --git a/testing/cffi0/test_zintegration.py b/testing/cffi0/test_zintegration.py index e0152fb21..69c3d01f3 100644 --- a/testing/cffi0/test_zintegration.py +++ b/testing/cffi0/test_zintegration.py @@ -119,7 +119,7 @@ def run_setup_and_program(dirname, python_snippet): assert not (SNIPPET_DIR / dirname / 'yacctab.py').exists() @pytest.mark.thread_unsafe(reason="very slow in parallel") -class TestZIntegration(object): +class TestZIntegration: def teardown_class(self): if udir.is_dir(): shutil.rmtree(udir) diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py index 9c6d29bea..e8ffabd39 100644 --- a/testing/cffi1/test_verify1.py +++ b/testing/cffi1/test_verify1.py @@ -1472,7 +1472,7 @@ def test_bool(): assert int(ffi.cast("_Bool", 10**200)) == 1 assert int(ffi.cast("_Bool", 10**40000)) == 1 # - class Foo(object): + class Foo: def __int__(self): self.seen = 1 return result @@ -2355,7 +2355,7 @@ def test_ffi_gc_size_arg_2(): lib = ffi.verify(r""" #include """) - class X(object): + class X: pass for i in range(2000): p = lib.malloc(50*1024*1024) # 50 MB @@ -2375,7 +2375,7 @@ def test_ffi_new_with_cycles(): ffi = FFI() ffi.cdef("") lib = ffi.verify("") - class X(object): + class X: pass for i in range(2000): p = ffi.new("char[]", 50*1024*1024) # 50 MB diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py index 1b08980f6..fb02befbc 100644 --- a/testing/cffi1/test_zdist.py +++ b/testing/cffi1/test_zdist.py @@ -20,7 +20,7 @@ def from_outside(f): return f -class TestDist(object): +class TestDist: def setup_method(self, meth): self.executable = os.path.abspath(sys.executable) diff --git a/testing/support.py b/testing/support.py index 063e52cff..382102ab3 100644 --- a/testing/support.py +++ b/testing/support.py @@ -4,7 +4,7 @@ if sys.version_info < (3,): __all__ = ['u', 'arraytostring', 'load_dynamic'] - class U(object): + class U: def __add__(self, other): return eval('u'+repr(other).replace(r'\\u', r'\u') .replace(r'\\U', r'\U')) @@ -25,7 +25,7 @@ def arraytostring(a): return a.tobytes() -class StdErrCapture(object): +class StdErrCapture: """Capture writes to sys.stderr (not to the underlying file descriptor).""" def __enter__(self): try: @@ -44,7 +44,7 @@ def __exit__(self, *args): sys.unraisablehook = self.old_unraisablebook -class FdWriteCapture(object): +class FdWriteCapture: """xxx limited to capture at most 512 bytes of output, according to the Posix manual.""" From 6946e475dbb7b9115e1928ab867ff9a1bae3095f Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:08:48 +0100 Subject: [PATCH 02/14] Apply ruff/pyupgrade rule UP008 UP008 Use `super()` instead of `super(__class__, self)` --- src/cffi/backend_ctypes.py | 8 ++++---- src/cffi/recompiler.py | 2 +- src/cffi/verifier.py | 2 +- testing/cffi0/test_verify.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cffi/backend_ctypes.py b/src/cffi/backend_ctypes.py index 01374149c..0801fc8ea 100644 --- a/src/cffi/backend_ctypes.py +++ b/src/cffi/backend_ctypes.py @@ -564,7 +564,7 @@ def _arg_to_ctypes(cls, *value): if value and isinstance(value[0], bytes): return ctypes.c_char_p(value[0]) else: - return super(CTypesPtr, cls)._arg_to_ctypes(*value) + return super()._arg_to_ctypes(*value) if kind == 'charp' or kind == 'bytep': def _to_string(self, maxlen): @@ -581,7 +581,7 @@ def _get_own_repr(self): if getattr(self, '_own', False): return 'owning %d bytes' % ( ctypes.sizeof(self._as_ctype_ptr.contents),) - return super(CTypesPtr, self)._get_own_repr() + return super()._get_own_repr() # if (BItem is self.ffi._get_cached_btype(model.void_type) or BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): @@ -677,7 +677,7 @@ def _to_string(self, maxlen): def _get_own_repr(self): if getattr(self, '_own', False): return 'owning %d bytes' % (ctypes.sizeof(self._blob),) - return super(CTypesArray, self)._get_own_repr() + return super()._get_own_repr() def _convert_to_address(self, BClass): if BClass in (CTypesPtr, None) or BClass._automatic_casts: @@ -917,7 +917,7 @@ def __repr__(self): def _get_own_repr(self): if getattr(self, '_own_callback', None) is not None: return 'calling %r' % (self._own_callback,) - return super(CTypesFunctionPtr, self)._get_own_repr() + return super()._get_own_repr() def __call__(self, *args): if has_varargs: diff --git a/src/cffi/recompiler.py b/src/cffi/recompiler.py index 8b263b75d..feee98c23 100644 --- a/src/cffi/recompiler.py +++ b/src/cffi/recompiler.py @@ -1401,7 +1401,7 @@ class NativeIO(io.BytesIO): def write(self, s): if isinstance(s, unicode): s = s.encode('ascii') - super(NativeIO, self).write(s) + super().write(s) def _is_file_like(maybefile): # compare to xml.etree.ElementTree._get_writer diff --git a/src/cffi/verifier.py b/src/cffi/verifier.py index 97634066b..5ad0105ed 100644 --- a/src/cffi/verifier.py +++ b/src/cffi/verifier.py @@ -24,7 +24,7 @@ class NativeIO(io.BytesIO): def write(self, s): if isinstance(s, unicode): s = s.encode('ascii') - super(NativeIO, self).write(s) + super().write(s) class Verifier: diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py index 79fe473db..f7a9c881c 100644 --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -22,7 +22,7 @@ else: class FFI(FFI): def verify(self, *args, **kwds): - return super(FFI, self).verify( + return super().verify( *args, extra_compile_args=extra_compile_args, **kwds) def setup_module(): From 2eed722deb39201461498e6f20d4e3108988d9cc Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:11:48 +0100 Subject: [PATCH 03/14] Apply ruff/pyupgrade rule UP009 UP009 UTF-8 encoding declaration is unnecessary --- doc/source/conf.py | 2 -- src/cffi/recompiler.py | 4 ---- 2 files changed, 6 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 23a21019c..8a1d9eeed 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # CFFI documentation build configuration file, created by # sphinx-quickstart on Thu Jun 14 16:37:47 2012. # diff --git a/src/cffi/recompiler.py b/src/cffi/recompiler.py index feee98c23..009a2ee4c 100644 --- a/src/cffi/recompiler.py +++ b/src/cffi/recompiler.py @@ -1299,10 +1299,6 @@ def _print_string_literal_in_array(self, s): s = s.encode('utf-8') # -> bytes else: s.decode('utf-8') # got bytes, check for valid utf-8 - try: - s.decode('ascii') - except UnicodeDecodeError: - s = b'# -*- encoding: utf8 -*-\n' + s for line in s.splitlines(True): comment = line if type('//') is bytes: # python2 From c6ee64386ad885be1a26864b4036f708d165e707 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:18:53 +0100 Subject: [PATCH 04/14] Apply ruff/pyupgrade rule UP010 UP010 Unnecessary `__future__` import for target Python version Manually removed comments about Unicode literals. --- demo/btrfs-snap.py | 2 -- demo/extern_python_varargs.py | 1 - demo/fastcsv.py | 1 - demo/gmp.py | 1 - demo/manual2.py | 1 - demo/pwuid.py | 1 - demo/pyobj.py | 2 -- demo/readdir.py | 1 - demo/readdir2.py | 1 - demo/readdir_ctypes.py | 1 - demo/winclipboard.py | 1 - testing/cffi0/test_unicode_literals.py | 34 +++++++++----------------- testing/cffi1/test_unicode_literals.py | 24 ++++++------------ 13 files changed, 19 insertions(+), 52 deletions(-) diff --git a/demo/btrfs-snap.py b/demo/btrfs-snap.py index 8061f83ad..616289e53 100644 --- a/demo/btrfs-snap.py +++ b/demo/btrfs-snap.py @@ -3,8 +3,6 @@ creates a exactly named snapshots and bails out if they exist """ -from __future__ import print_function - import argparse import fcntl import os diff --git a/demo/extern_python_varargs.py b/demo/extern_python_varargs.py index f832d6a39..79fee0e3e 100644 --- a/demo/extern_python_varargs.py +++ b/demo/extern_python_varargs.py @@ -1,4 +1,3 @@ -from __future__ import print_function import cffi ffi = cffi.FFI() diff --git a/demo/fastcsv.py b/demo/fastcsv.py index 943dd676d..8e28a57e3 100644 --- a/demo/fastcsv.py +++ b/demo/fastcsv.py @@ -1,4 +1,3 @@ -from __future__ import print_function import csv import cffi diff --git a/demo/gmp.py b/demo/gmp.py index 4f0857f35..90e5e1cfb 100644 --- a/demo/gmp.py +++ b/demo/gmp.py @@ -1,4 +1,3 @@ -from __future__ import print_function import sys # # This is only a demo based on the GMP library. diff --git a/demo/manual2.py b/demo/manual2.py index 598fb7eed..571523720 100644 --- a/demo/manual2.py +++ b/demo/manual2.py @@ -1,4 +1,3 @@ -from __future__ import print_function import _cffi_backend ffi = _cffi_backend.FFI(b"manual2", diff --git a/demo/pwuid.py b/demo/pwuid.py index 7651e6e0a..2ecb4b848 100644 --- a/demo/pwuid.py +++ b/demo/pwuid.py @@ -1,4 +1,3 @@ -from __future__ import print_function import sys, os # run pwuid_build first, then make sure the shared object is on sys.path diff --git a/demo/pyobj.py b/demo/pyobj.py index 5f2cdf4bb..1e6b832a8 100644 --- a/demo/pyobj.py +++ b/demo/pyobj.py @@ -1,5 +1,3 @@ -from __future__ import print_function - referents = [] # list "object descriptor -> python object" freelist = None diff --git a/demo/readdir.py b/demo/readdir.py index cac9200af..fcb2b7d9c 100644 --- a/demo/readdir.py +++ b/demo/readdir.py @@ -1,4 +1,3 @@ -from __future__ import print_function # A Linux-only demo # import sys diff --git a/demo/readdir2.py b/demo/readdir2.py index 601cd2ce2..7aef32897 100644 --- a/demo/readdir2.py +++ b/demo/readdir2.py @@ -1,4 +1,3 @@ -from __future__ import print_function # A Linux-only demo, using set_source() instead of hard-coding the exact layouts # import sys diff --git a/demo/readdir_ctypes.py b/demo/readdir_ctypes.py index 8f7f14dbb..9ceb52980 100644 --- a/demo/readdir_ctypes.py +++ b/demo/readdir_ctypes.py @@ -1,4 +1,3 @@ -from __future__ import print_function # A Linux-only demo # # For comparison purposes, this is a ctypes version of readdir.py. diff --git a/demo/winclipboard.py b/demo/winclipboard.py index e7335e45e..c9e64d718 100644 --- a/demo/winclipboard.py +++ b/demo/winclipboard.py @@ -1,4 +1,3 @@ -from __future__ import print_function __author__ = "Israel Fruchter " import sys, os diff --git a/testing/cffi0/test_unicode_literals.py b/testing/cffi0/test_unicode_literals.py index 373e4fc23..1b42c9789 100644 --- a/testing/cffi0/test_unicode_literals.py +++ b/testing/cffi0/test_unicode_literals.py @@ -1,12 +1,3 @@ -# -# ---------------------------------------------- -# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE -# ---------------------------------------------- -# -from __future__ import unicode_literals -# -# -# import sys, math from cffi import FFI from testing.support import is_musl @@ -25,61 +16,60 @@ def test_cast(): ffi = FFI() - assert int(ffi.cast("int", 3.14)) == 3 # unicode literal + assert int(ffi.cast("int", 3.14)) == 3 def test_new(): ffi = FFI() - assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal + assert ffi.new("int[]", [3, 4, 5])[2] == 5 def test_typeof(): ffi = FFI() - tp = ffi.typeof("int[51]") # unicode literal + tp = ffi.typeof("int[51]") assert tp.length == 51 def test_sizeof(): ffi = FFI() - assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal + assert ffi.sizeof("int[51]") == 51 * 4 def test_alignof(): ffi = FFI() - assert ffi.alignof("int[51]") == 4 # unicode literal + assert ffi.alignof("int[51]") == 4 def test_getctype(): ffi = FFI() - assert ffi.getctype("int**") == "int * *" # unicode literal + assert ffi.getctype("int**") == "int * *" assert type(ffi.getctype("int**")) is str def test_cdef(): ffi = FFI() - ffi.cdef("typedef int foo_t[50];") # unicode literal + ffi.cdef("typedef int foo_t[50];") def test_offsetof(): ffi = FFI() ffi.cdef("typedef struct { int x, y; } foo_t;") - assert ffi.offsetof("foo_t", "y") == 4 # unicode literal + assert ffi.offsetof("foo_t", "y") == 4 def test_enum(): ffi = FFI() - ffi.cdef("enum foo_e { AA, BB, CC };") # unicode literal + ffi.cdef("enum foo_e { AA, BB, CC };") x = ffi.cast("enum foo_e", 1) assert int(ffi.cast("int", x)) == 1 def test_dlopen(): ffi = FFI() ffi.cdef("double sin(double x);") - m = ffi.dlopen(lib_m) # unicode literal + m = ffi.dlopen(lib_m) x = m.sin(1.23) assert x == math.sin(1.23) @pytest.mark.thread_unsafe(reason="FFI verifier is not thread-safe") def test_verify(): ffi = FFI() - ffi.cdef("double test_verify_1(double x);") # unicode literal + ffi.cdef("double test_verify_1(double x);") lib = ffi.verify("double test_verify_1(double x) { return x * 42.0; }") assert lib.test_verify_1(-1.5) == -63.0 def test_callback(): ffi = FFI() - cb = ffi.callback("int(int)", # unicode literal - lambda x: x + 42) + cb = ffi.callback("int(int)", lambda x: x + 42) assert cb(5) == 47 diff --git a/testing/cffi1/test_unicode_literals.py b/testing/cffi1/test_unicode_literals.py index e9825db84..405bcde07 100644 --- a/testing/cffi1/test_unicode_literals.py +++ b/testing/cffi1/test_unicode_literals.py @@ -1,43 +1,33 @@ -# -# ---------------------------------------------- -# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE -# ---------------------------------------------- -# -from __future__ import unicode_literals -# -# -# from _cffi_backend import FFI def test_cast(): ffi = FFI() - assert int(ffi.cast("int", 3.14)) == 3 # unicode literal + assert int(ffi.cast("int", 3.14)) == 3 def test_new(): ffi = FFI() - assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal + assert ffi.new("int[]", [3, 4, 5])[2] == 5 def test_typeof(): ffi = FFI() - tp = ffi.typeof("int[51]") # unicode literal + tp = ffi.typeof("int[51]") assert tp.length == 51 def test_sizeof(): ffi = FFI() - assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal + assert ffi.sizeof("int[51]") == 51 * 4 def test_alignof(): ffi = FFI() - assert ffi.alignof("int[51]") == 4 # unicode literal + assert ffi.alignof("int[51]") == 4 def test_getctype(): ffi = FFI() - assert ffi.getctype("int**") == "int * *" # unicode literal + assert ffi.getctype("int**") == "int * *" assert type(ffi.getctype("int**")) is str def test_callback(): ffi = FFI() - cb = ffi.callback("int(int)", # unicode literal - lambda x: x + 42) + cb = ffi.callback("int(int)", lambda x: x + 42) assert cb(5) == 47 From aad7376737bd56a593c0b978c79b5e1fc610505f Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 14:13:29 +0100 Subject: [PATCH 05/14] Apply ruff/pyupgrade rule UP020 In Python 3, `io.open` is an alias for `open`. This is a manual change, not detected by ruff. --- src/c/test_c.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/c/test_c.py b/src/c/test_c.py index d4c666a1a..46ca6b921 100644 --- a/src/c/test_c.py +++ b/src/c/test_c.py @@ -3017,8 +3017,8 @@ def test_string_assignment_to_byte_array(): # XXX hack if sys.version_info >= (3,): try: - import posix, io - posix.fdopen = io.open + import posix + posix.fdopen = open except ImportError: pass # win32 From d38eca1eb2b435ff646349e981c851c2b73bd71e Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:20:08 +0100 Subject: [PATCH 06/14] Apply ruff/pyupgrade rule UP024 UP024 Replace aliased errors with `OSError` --- demo/btrfs-snap.py | 2 +- src/cffi/pkgconfig.py | 2 +- src/cffi/recompiler.py | 4 ++-- testing/cffi1/test_commontypes.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/btrfs-snap.py b/demo/btrfs-snap.py index 616289e53..b1e1e518e 100644 --- a/demo/btrfs-snap.py +++ b/demo/btrfs-snap.py @@ -45,7 +45,7 @@ args_buffer = ffi.buffer(args) try: fcntl.ioctl(target, lib.BTRFS_IOC_SNAP_CREATE_V2, args_buffer) -except IOError as e: +except OSError as e: print(e) sys.exit(1) diff --git a/src/cffi/pkgconfig.py b/src/cffi/pkgconfig.py index 5c93f15a6..e1034ad68 100644 --- a/src/cffi/pkgconfig.py +++ b/src/cffi/pkgconfig.py @@ -31,7 +31,7 @@ def call(libname, flag, encoding=sys.getfilesystemencoding()): a.append(libname) try: pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except EnvironmentError as e: + except OSError as e: raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) bout, berr = pc.communicate() diff --git a/src/cffi/recompiler.py b/src/cffi/recompiler.py index 009a2ee4c..495b897a2 100644 --- a/src/cffi/recompiler.py +++ b/src/cffi/recompiler.py @@ -1419,11 +1419,11 @@ def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): try: with open(target_file, 'r') as f1: if f1.read(len(output) + 1) != output: - raise IOError + raise OSError if verbose: print("(already up-to-date)") return False # already up-to-date - except IOError: + except OSError: tmp_file = '%s.~%d' % (target_file, os.getpid()) with open(tmp_file, 'w') as f1: f1.write(output) diff --git a/testing/cffi1/test_commontypes.py b/testing/cffi1/test_commontypes.py index 9e7d79e9a..8f5ad4ca7 100644 --- a/testing/cffi1/test_commontypes.py +++ b/testing/cffi1/test_commontypes.py @@ -7,7 +7,7 @@ def getlines(): try: f = open(os.path.join(os.path.dirname(cffi.__file__), '..', 'c', 'commontypes.c')) - except IOError: + except OSError: pytest.skip("cannot find ../c/commontypes.c") lines = [line for line in f.readlines() if line.strip().startswith('EQ(')] f.close() From 9b18ef6500b809d4c33f04e0a4a66c48fec5e41b Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:20:50 +0100 Subject: [PATCH 07/14] Apply ruff:pyupgrade rule UP025 UP025 Remove unicode literals from strings --- doc/source/conf.py | 8 ++++---- src/c/test_c.py | 4 ++-- testing/embedding/withunicode.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 8a1d9eeed..075336b76 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -35,8 +35,8 @@ master_doc = 'index' # General information about the project. -project = u'CFFI' -copyright = u'2012-2025, Armin Rigo, Maciej Fijalkowski' +project = 'CFFI' +copyright = '2012-2025, Armin Rigo, Maciej Fijalkowski' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -170,8 +170,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'CFFI.tex', u'CFFI Documentation', - u'Armin Rigo, Maciej Fijalkowski', 'manual'), + ('index', 'CFFI.tex', 'CFFI Documentation', + 'Armin Rigo, Maciej Fijalkowski', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/src/c/test_c.py b/src/c/test_c.py index 46ca6b921..b4c860eaa 100644 --- a/src/c/test_c.py +++ b/src/c/test_c.py @@ -4180,8 +4180,8 @@ def test_unpack(): for typename in ["wchar_t", "char16_t", "char32_t"]: BWChar = new_primitive_type(typename) BArray = new_array_type(new_pointer_type(BWChar), 10) # wchar_t[10] - p = newp(BArray, u"abc\x00def") - assert unpack(p, 10) == u"abc\x00def\x00\x00\x00" + p = newp(BArray, "abc\x00def") + assert unpack(p, 10) == "abc\x00def\x00\x00\x00" for typename, samples in [ ("uint8_t", [0, 2**8-1]), diff --git a/testing/embedding/withunicode.py b/testing/embedding/withunicode.py index 839c6cdff..be86a21d5 100644 --- a/testing/embedding/withunicode.py +++ b/testing/embedding/withunicode.py @@ -8,7 +8,7 @@ ffi = cffi.FFI() -ffi.embedding_api(u""" +ffi.embedding_api(""" int add1(int, int); """) From 19305a8769e56fd02aad81fc8afb72694b9a62f3 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:21:26 +0100 Subject: [PATCH 08/14] Apply ruff/pyupgrade rule UP028 UP028 Replace `yield` over `for` loop with `yield from` --- src/cffi/model.py | 3 +-- testing/cffi1/test_recompiler.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cffi/model.py b/src/cffi/model.py index 3b229b942..a2fb7b257 100644 --- a/src/cffi/model.py +++ b/src/cffi/model.py @@ -371,8 +371,7 @@ def enumfields(self, expand_anonymous_struct_union=True): if (name == '' and isinstance(type, StructOrUnion) and expand_anonymous_struct_union): # nested anonymous struct/union - for result in type.enumfields(): - yield result + yield from type.enumfields() else: yield (name, type, bitsize, quals) diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index a789483c2..6e61a2ff8 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -1241,8 +1241,7 @@ def test_macro_var_callback(): # values = ffi.new("int[50]") def it(): - for i in range(50): - yield i + yield from range(50) it = it() # @ffi.callback("int *(*)(void)") From 0d2675357170b7458b82fb6f09abcea22327f97a Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:21:55 +0100 Subject: [PATCH 09/14] Apply ruff/pyupgrade rule UP035 UP035 Import from `collections.abc` instead: `Callable` Co-authored-by: Matti Picus --- src/cffi/api.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/cffi/api.py b/src/cffi/api.py index 830aef84a..4ec607112 100644 --- a/src/cffi/api.py +++ b/src/cffi/api.py @@ -3,13 +3,6 @@ from .error import CDefError from . import model -try: - callable -except NameError: - # Python 3.1 - from collections import Callable - callable = lambda x: isinstance(x, Callable) - try: basestring except NameError: From 9634426eb047693843f41372ede6741821f834a8 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:27:32 +0100 Subject: [PATCH 10/14] Apply ruff/refurb rule FURB129 FURB129 Instead of calling `readlines()`, iterate over file object directly --- testing/cffi1/test_commontypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/cffi1/test_commontypes.py b/testing/cffi1/test_commontypes.py index 8f5ad4ca7..d28810e53 100644 --- a/testing/cffi1/test_commontypes.py +++ b/testing/cffi1/test_commontypes.py @@ -9,7 +9,7 @@ def getlines(): '..', 'c', 'commontypes.c')) except OSError: pytest.skip("cannot find ../c/commontypes.c") - lines = [line for line in f.readlines() if line.strip().startswith('EQ(')] + lines = [line for line in f if line.strip().startswith('EQ(')] f.close() return lines From 74275af81e97db7af44dae08dc924d1b3bff1a78 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:28:32 +0100 Subject: [PATCH 11/14] Apply ruff/refurb rule FURB188 FURB188 Prefer `str.removeprefix()` over conditionally replacing with slice. --- testing/cffi1/test_recompiler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index 6e61a2ff8..d69fdbe03 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -893,8 +893,7 @@ def test_unpack_args(): e7 = pytest.raises(TypeError, lib.foo2, 45, 46, 47) def st1(s): s = str(s) - if s.startswith("_CFFI_test_unpack_args.Lib."): - s = s[len("_CFFI_test_unpack_args.Lib."):] + s = s.removeprefix("_CFFI_test_unpack_args.Lib.") return s assert st1(e1.value) == "foo0() takes no arguments (1 given)" assert st1(e2.value) == "foo0() takes no arguments (2 given)" From 92413143c11510f5adbef6fac684eaf5100cc5c0 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:40:09 +0100 Subject: [PATCH 12/14] Apply ruff/pycodestyle rule E703 E703 Statement ends with an unnecessary semicolon --- testing/cffi1/test_recompiler.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py index d69fdbe03..d37335c18 100644 --- a/testing/cffi1/test_recompiler.py +++ b/testing/cffi1/test_recompiler.py @@ -2046,7 +2046,7 @@ def test_function_returns_partial_struct(): def test_function_returns_float_complex(): ffi = FFI() - ffi.cdef("float _Complex f1(float a, float b);"); + ffi.cdef("float _Complex f1(float a, float b);") if sys.platform == 'win32': lib = verify(ffi, "test_function_returns_float_complex", """ #include @@ -2064,7 +2064,7 @@ def test_function_returns_float_complex(): def test_function_returns_double_complex(): ffi = FFI() - ffi.cdef("double _Complex f1(double a, double b);"); + ffi.cdef("double _Complex f1(double a, double b);") if sys.platform == 'win32': lib = verify(ffi, "test_function_returns_double_complex", """ #include @@ -2084,7 +2084,7 @@ def test_cdef_using_windows_complex(): if sys.platform != 'win32': pytest.skip("only for MSVC") ffi = FFI() - ffi.cdef("_Fcomplex f1(float a, float b); _Dcomplex f2(double a, double b);"); + ffi.cdef("_Fcomplex f1(float a, float b); _Dcomplex f2(double a, double b);") lib = verify(ffi, "test_cdef_using_windows_complex", """ #include static _Fcomplex f1(float a, float b) { return _FCbuild(a, 2.0f*b); } @@ -2101,7 +2101,7 @@ def test_cdef_using_windows_complex(): def test_function_argument_float_complex(): ffi = FFI() - ffi.cdef("float f1(float _Complex x);"); + ffi.cdef("float f1(float _Complex x);") if sys.platform == 'win32': lib = verify(ffi, "test_function_argument_float_complex", """ #include @@ -2118,7 +2118,7 @@ def test_function_argument_float_complex(): def test_function_argument_double_complex(): ffi = FFI() - ffi.cdef("double f1(double _Complex);"); + ffi.cdef("double f1(double _Complex);") if sys.platform == 'win32': lib = verify(ffi, "test_function_argument_double_complex", """ #include From b303986fadaea7d2fe1a4fa8495a789337b24e08 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:40:48 +0100 Subject: [PATCH 13/14] Apply ruff/pycodestyle rule E711 E711 Comparison to `None` should be `cond is not None` --- src/c/test_c.py | 8 ++++---- testing/cffi0/backend_tests.py | 20 ++++++++++---------- testing/cffi1/test_new_ffi_1.py | 20 ++++++++++---------- testing/cffi1/test_zdist.py | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/c/test_c.py b/src/c/test_c.py index b4c860eaa..8d2228c17 100644 --- a/src/c/test_c.py +++ b/src/c/test_c.py @@ -460,7 +460,7 @@ def test_reading_pointer_to_pointer(): assert p[0] is not None assert p[0] == cast(BVoidP, 0) assert p[0] == cast(BCharP, 0) - assert p[0] != None + assert p[0] is not None assert repr(p[0]) == "" p[0] = q assert p[0] != cast(BVoidP, 0) @@ -495,12 +495,12 @@ def test_no_len_on_nonarray(): def test_cmp_none(): p = new_primitive_type("int") x = cast(p, 42) - assert (x == None) is False - assert (x != None) is True + assert (x is None) is False + assert (x is not None) is True assert (x == ["hello"]) is False assert (x != ["hello"]) is True y = cast(p, 0) - assert (y == None) is False + assert (y is None) is False def test_invalid_indexing(): p = new_primitive_type("int") diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py index 63900435a..99b303169 100644 --- a/testing/cffi0/backend_tests.py +++ b/testing/cffi0/backend_tests.py @@ -207,7 +207,7 @@ def test_pointer_direct(self): assert p is not None assert bool(p) is False assert p == ffi.cast("int*", 0) - assert p != None + assert p is not None assert repr(p) == "" a = ffi.new("int[]", [123, 456]) p = ffi.cast("int*", a) @@ -397,7 +397,7 @@ def test_none_as_null_doesnt_work(self): ffi = FFI(backend=self.Backend()) p = ffi.new("int*[1]") assert p[0] is not None - assert p[0] != None + assert p[0] is not None assert p[0] == ffi.NULL assert repr(p[0]) == "" # @@ -1154,14 +1154,14 @@ def test_pointer_comparison(self): assert (p > q) is False assert (p >= q) is False # - assert (None == s) is False - assert (None != s) is True - assert (s == None) is False - assert (s != None) is True - assert (None == q) is False - assert (None != q) is True - assert (q == None) is False - assert (q != None) is True + assert (None is s) is False + assert (None is not s) is True + assert (s is None) is False + assert (s is not None) is True + assert (None is q) is False + assert (None is not q) is True + assert (q is None) is False + assert (q is not None) is True def test_integer_comparison(self): ffi = FFI(backend=self.Backend()) diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py index b8db8af9a..676d235de 100644 --- a/testing/cffi1/test_new_ffi_1.py +++ b/testing/cffi1/test_new_ffi_1.py @@ -265,7 +265,7 @@ def test_pointer_direct(self): assert p is not None assert bool(p) is False assert p == ffi.cast("int*", 0) - assert p != None + assert p is not None assert repr(p) == "" a = ffi.new("int[]", [123, 456]) p = ffi.cast("int*", a) @@ -446,7 +446,7 @@ def test_wchar_t(self): def test_none_as_null_doesnt_work(self): p = ffi.new("int*[1]") assert p[0] is not None - assert p[0] != None + assert p[0] is not None assert p[0] == ffi.NULL assert repr(p[0]) == "" # @@ -1130,14 +1130,14 @@ def test_pointer_comparison(self): assert (p > q) is False assert (p >= q) is False # - assert (None == s) is False - assert (None != s) is True - assert (s == None) is False - assert (s != None) is True - assert (None == q) is False - assert (None != q) is True - assert (q == None) is False - assert (q != None) is True + assert (None is s) is False + assert (None is not s) is True + assert (s is None) is False + assert (s is not None) is True + assert (None is q) is False + assert (None is not q) is True + assert (q is None) is False + assert (q is not None) is True def test_integer_comparison(self): x = ffi.cast("int", 123) diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py index fb02befbc..d8200e7e9 100644 --- a/testing/cffi1/test_zdist.py +++ b/testing/cffi1/test_zdist.py @@ -43,7 +43,7 @@ def run(self, args, cwd=None): # NOTE: pointing $HOME to a nonexistent directory can break certain things # that look there for configuration (like ccache). tmp_home = mkdtemp() - assert tmp_home != None, "cannot create temporary homedir" + assert tmp_home is not None, "cannot create temporary homedir" env['HOME'] = tmp_home pathlist = sys.path[:] if cwd is None: From fc735bf0bc52e52af772c26d0f211642260f93a3 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 22 Nov 2025 15:42:06 +0100 Subject: [PATCH 14/14] Apply ruff/pycodestyle rule E713 E713 Test for membership should be `not in` --- src/cffi/api.py | 2 +- src/cffi/model.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cffi/api.py b/src/cffi/api.py index 4ec607112..ec8ee7dfd 100644 --- a/src/cffi/api.py +++ b/src/cffi/api.py @@ -407,7 +407,7 @@ def getctype(self, cdecl, replace_with=''): if (replace_with.startswith('*') and '&[' in self._backend.getcname(cdecl, '&')): replace_with = '(%s)' % replace_with - elif replace_with and not replace_with[0] in '[(': + elif replace_with and replace_with[0] not in '[(': replace_with = ' ' + replace_with return self._backend.getcname(cdecl, replace_with) diff --git a/src/cffi/model.py b/src/cffi/model.py index a2fb7b257..8bade1d6d 100644 --- a/src/cffi/model.py +++ b/src/cffi/model.py @@ -34,7 +34,7 @@ def get_c_name(self, replace_with='', context='a C file', quals=0): if replace_with: if replace_with.startswith('*') and '&[' in result: replace_with = '(%s)' % replace_with - elif not replace_with[0] in '[(': + elif replace_with[0] not in '[(': replace_with = ' ' + replace_with replace_with = qualify(quals, replace_with) result = result.replace('&', replace_with)