pybind: replace deprecated IF statements with Tempita templates

Replace deprecated Cython IF statements with Tempita templates to
eliminate warnings and prepare for Cython 3.0+ compatibility.

Cython 3.0+ produces deprecation warnings for compile-time IF statements:

  warning: rados.pxd:7:0: The 'IF' statement is deprecated and will be
  removed in a future Cython version. Consider using runtime conditions
  or C macros instead. See https://github.com/cython/cython/issues/4310

Changes for all modules (rados, rbd, rgw, cephfs):
- Replace IF BUILD_DOC with Tempita {{if BUILD_DOC}}...{{endif}}
- Add Tempita preprocessing to setup.py

Changes for rados and rgw (platform-specific errno):
- Replace IF UNAME_SYSNAME with Tempita
- Use _nodata_errno for FreeBSD ENOATTR vs Linux ENODATA

Changes for rados.pxd:
- Use C preprocessor conditional (#ifndef BUILD_DOC)
- Add C typedef definitions in #else branch for BUILD_DOC mode
- Use quoted include path for -iquote compatibility

Signed-off-by: Kefu Chai <k.chai@proxmox.com>
This commit is contained in:
Kefu Chai 2025-11-28 20:41:04 +08:00
parent ec0e508444
commit 719b749846
9 changed files with 214 additions and 113 deletions

View File

@ -10,14 +10,15 @@ from libc.stdint cimport *
from libc.stdlib cimport malloc, realloc, free
from types cimport *
IF BUILD_DOC:
include "mock_cephfs.pxi"
cdef class Rados:
cdef:
rados_t cluster
ELSE:
from c_cephfs cimport *
from rados cimport Rados
{{if BUILD_DOC}}
include "mock_cephfs.pxi"
cdef class Rados:
cdef:
rados_t cluster
{{else}}
from c_cephfs cimport *
from rados cimport Rados
{{endif}}
from collections import namedtuple, deque
from datetime import datetime

View File

@ -159,6 +159,7 @@ cmdclass = {}
try:
from Cython.Build import cythonize
from Cython.Distutils import build_ext
from Cython import Tempita
cmdclass = {'build_ext': build_ext}
except ImportError:
@ -173,7 +174,27 @@ except ImportError:
source = "cephfs.c"
else:
source = "cephfs.pyx"
# Process Tempita template
source_pyx = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"cephfs.pyx"
)
# Read the template from source
with open(source_pyx) as f:
template_content = f.read()
# Process the template with cython_constants
processed = Tempita.sub(template_content, **cython_constants)
# Write processed output to current working directory
# (which is the build directory when invoked by CMake)
output_pyx = "cephfs_processed.pyx"
with open(output_pyx, 'w') as f:
f.write(processed)
source = output_pyx
# Disable cythonification if we're not really building anything
if (len(sys.argv) >= 2 and

View File

@ -4,16 +4,19 @@
#
# Copyright 2016 Mehdi Abaakouk <sileht@redhat.com>
IF BUILD_DOC:
cdef:
ctypedef void* rados_t
ctypedef void* rados_config_t
ctypedef void* rados_ioctx_t
ELSE:
cdef extern from "rados/librados.h" nogil:
ctypedef void* rados_t
ctypedef void* rados_config_t
ctypedef void* rados_ioctx_t
cdef extern from *:
"""
#ifdef BUILD_DOC
typedef void* rados_t;
typedef void* rados_config_t;
typedef void* rados_ioctx_t;
#else
#include "rados/librados.h"
#endif
"""
ctypedef void* rados_t
ctypedef void* rados_config_t
ctypedef void* rados_ioctx_t
cdef class Rados(object):

View File

@ -18,10 +18,12 @@ from cpython.pycapsule cimport *
from libc cimport errno
from libc.stdint cimport *
from libc.stdlib cimport malloc, realloc, free
IF BUILD_DOC:
include "mock_rados.pxi"
ELSE:
from c_rados cimport *
{{if BUILD_DOC}}
include "mock_rados.pxi"
{{else}}
from c_rados cimport *
{{endif}}
import threading
import time
@ -232,42 +234,45 @@ class ConnectionShutdown(OSError):
"RADOS connection was shutdown (%s)" % message, errno)
IF UNAME_SYSNAME == "FreeBSD":
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.EBUSY : ObjectBusy,
errno.ENOATTR : NoData,
errno.EINTR : InterruptedOrTimeoutError,
errno.ETIMEDOUT : TimedOut,
errno.EACCES : PermissionDeniedError,
errno.EINPROGRESS : InProgress,
errno.EISCONN : IsConnected,
errno.EINVAL : InvalidArgumentError,
errno.ENOTCONN : NotConnected,
errno.ESHUTDOWN : ConnectionShutdown,
}
ELSE:
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.EBUSY : ObjectBusy,
errno.ENODATA : NoData,
errno.EINTR : InterruptedOrTimeoutError,
errno.ETIMEDOUT : TimedOut,
errno.EACCES : PermissionDeniedError,
errno.EINPROGRESS : InProgress,
errno.EISCONN : IsConnected,
errno.EINVAL : InvalidArgumentError,
errno.ENOTCONN : NotConnected,
errno.ESHUTDOWN : ConnectionShutdown,
}
# Build errno mapping based on platform
# FreeBSD uses ENOATTR while Linux uses ENODATA
{{if UNAME_SYSNAME == "FreeBSD"}}
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.EBUSY : ObjectBusy,
errno.ENOATTR : NoData,
errno.EINTR : InterruptedOrTimeoutError,
errno.ETIMEDOUT : TimedOut,
errno.EACCES : PermissionDeniedError,
errno.EINPROGRESS : InProgress,
errno.EISCONN : IsConnected,
errno.EINVAL : InvalidArgumentError,
errno.ENOTCONN : NotConnected,
errno.ESHUTDOWN : ConnectionShutdown,
}
{{else}}
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.EBUSY : ObjectBusy,
errno.ENODATA : NoData,
errno.EINTR : InterruptedOrTimeoutError,
errno.ETIMEDOUT : TimedOut,
errno.EACCES : PermissionDeniedError,
errno.EINPROGRESS : InProgress,
errno.EISCONN : IsConnected,
errno.EINVAL : InvalidArgumentError,
errno.ENOTCONN : NotConnected,
errno.ESHUTDOWN : ConnectionShutdown,
}
{{endif}}
cdef make_ex(ret: int, msg: str):

View File

@ -11,6 +11,7 @@ from distutils.ccompiler import new_compiler
from itertools import filterfalse, takewhile
import os
import platform
import shutil
import sys
import tempfile
@ -138,11 +139,11 @@ def check_sanity():
if 'BUILD_DOC' in os.environ or 'READTHEDOCS' in os.environ:
ext_args = {}
cython_constants = dict(BUILD_DOC=True)
ext_args = dict(extra_compile_args=['-DBUILD_DOC'])
cython_constants = dict(BUILD_DOC=True, UNAME_SYSNAME=platform.system())
elif check_sanity():
ext_args = get_python_flags(['rados'])
cython_constants = dict(BUILD_DOC=False)
cython_constants = dict(BUILD_DOC=False, UNAME_SYSNAME=platform.system())
else:
sys.exit(1)
@ -150,6 +151,7 @@ cmdclass = {}
try:
from Cython.Build import cythonize
from Cython.Distutils import build_ext
from Cython import Tempita
cmdclass = {'build_ext': build_ext}
except ImportError:
@ -164,7 +166,27 @@ except ImportError:
source = "rados.c"
else:
source = "rados.pyx"
# Process Tempita template
source_pyx = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"rados.pyx"
)
# Read the template from source
with open(source_pyx) as f:
template_content = f.read()
# Process the template with cython_constants
processed = Tempita.sub(template_content, **cython_constants)
# Write processed output to current working directory
# (which is the build directory when invoked by CMake)
output_pyx = "rados_processed.pyx"
with open(output_pyx, 'w') as f:
f.write(processed)
source = output_pyx
# Disable cythonification if we're not really building anything
if (len(sys.argv) >= 2 and

View File

@ -34,11 +34,12 @@ import errno
from itertools import chain
import time
IF BUILD_DOC:
include "mock_rbd.pxi"
ELSE:
from c_rbd cimport *
cimport rados
{{if BUILD_DOC}}
include "mock_rbd.pxi"
{{else}}
from c_rbd cimport *
cimport rados
{{endif}}
cdef extern from "Python.h":
@ -373,18 +374,19 @@ cdef make_ex(ret, msg, exception_map=errno_to_exception):
return OSError(msg, errno=ret)
IF BUILD_DOC:
cdef rados_t convert_rados(rados) nogil:
return <rados_t>0
{{if BUILD_DOC}}
cdef rados_t convert_rados(rados) nogil:
return <rados_t>0
cdef rados_ioctx_t convert_ioctx(ioctx) nogil:
return <rados_ioctx_t>0
ELSE:
cdef rados_t convert_rados(rados.Rados rados) except? NULL:
return <rados_t>rados.cluster
cdef rados_ioctx_t convert_ioctx(ioctx) nogil:
return <rados_ioctx_t>0
{{else}}
cdef rados_t convert_rados(rados.Rados rados) except? NULL:
return <rados_t>rados.cluster
cdef rados_ioctx_t convert_ioctx(rados.Ioctx ioctx) except? NULL:
return <rados_ioctx_t>ioctx.io
cdef rados_ioctx_t convert_ioctx(rados.Ioctx ioctx) except? NULL:
return <rados_ioctx_t>ioctx.io
{{endif}}
cdef int progress_callback(uint64_t offset, uint64_t total, void* ptr) with gil:
return (<object>ptr)(offset, total)

View File

@ -160,6 +160,7 @@ try:
from Cython.Build import cythonize
from Cython.Distutils import build_ext
from Cython import __version__ as cython_version
from Cython import Tempita
cmdclass = {'build_ext': build_ext}
@ -183,7 +184,25 @@ except ImportError:
source = "rbd.c"
else:
source = "rbd.pyx"
# Process Tempita template
source_pyx = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"rbd.pyx"
)
# Read the template from source
with open(source_pyx) as f:
template_content = f.read()
# Process the template with cython_constants
processed = Tempita.sub(template_content, **cython_constants)
# Write processed output to current working directory
# (which is the build directory when invoked by CMake)
source = "rbd_processed.pyx"
with open(source, 'w') as f:
f.write(processed)
# Disable cythonification if we're not really building anything
if (len(sys.argv) >= 2 and

View File

@ -9,11 +9,12 @@ from libc.stdlib cimport malloc, realloc, free
from cstat cimport stat
cimport libcpp
IF BUILD_DOC:
include "mock_rgw.pxi"
ELSE:
from c_rgw cimport *
cimport rados
{{if BUILD_DOC}}
include "mock_rgw.pxi"
{{else}}
from c_rgw cimport *
cimport rados
{{endif}}
from collections import namedtuple
from datetime import datetime
@ -90,32 +91,36 @@ class WouldBlock(Error):
class OutOfRange(Error):
pass
IF UNAME_SYSNAME == "FreeBSD":
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.ENOATTR : NoData,
errno.EINVAL : InvalidValue,
errno.EOPNOTSUPP : OperationNotSupported,
errno.ERANGE : OutOfRange,
errno.EWOULDBLOCK: WouldBlock,
}
ELSE:
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.ENODATA : NoData,
errno.EINVAL : InvalidValue,
errno.EOPNOTSUPP : OperationNotSupported,
errno.ERANGE : OutOfRange,
errno.EWOULDBLOCK: WouldBlock,
}
# Build errno mapping based on platform
# FreeBSD uses ENOATTR while Linux uses ENODATA
{{if UNAME_SYSNAME == "FreeBSD"}}
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.ENOATTR : NoData,
errno.EINVAL : InvalidValue,
errno.EOPNOTSUPP : OperationNotSupported,
errno.ERANGE : OutOfRange,
errno.EWOULDBLOCK: WouldBlock,
}
{{else}}
cdef errno_to_exception = {
errno.EPERM : PermissionError,
errno.ENOENT : ObjectNotFound,
errno.EIO : IOError,
errno.ENOSPC : NoSpace,
errno.EEXIST : ObjectExists,
errno.ENODATA : NoData,
errno.EINVAL : InvalidValue,
errno.EOPNOTSUPP : OperationNotSupported,
errno.ERANGE : OutOfRange,
errno.EWOULDBLOCK: WouldBlock,
}
{{endif}}
cdef class FileHandle(object):

View File

@ -141,13 +141,15 @@ def check_sanity():
shutil.rmtree(tmp_dir)
import platform
if 'BUILD_DOC' in os.environ or 'READTHEDOCS' in os.environ:
ext_args = {}
cython_constants = dict(BUILD_DOC=True)
cython_constants = dict(BUILD_DOC=True, UNAME_SYSNAME=platform.system())
cythonize_args = dict(compile_time_env=cython_constants)
elif check_sanity():
ext_args = get_python_flags(['rados', 'rgw'])
cython_constants = dict(BUILD_DOC=False)
cython_constants = dict(BUILD_DOC=False, UNAME_SYSNAME=platform.system())
include_path = [os.path.join(os.path.dirname(__file__), "..", "rados")]
cythonize_args = dict(compile_time_env=cython_constants,
include_path=include_path)
@ -158,6 +160,7 @@ cmdclass = {}
try:
from Cython.Build import cythonize
from Cython.Distutils import build_ext
from Cython import Tempita
cmdclass = {'build_ext': build_ext}
except ImportError:
@ -172,7 +175,27 @@ except ImportError:
source = "rgw.c"
else:
source = "rgw.pyx"
# Process Tempita template
source_pyx = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"rgw.pyx"
)
# Read the template from source
with open(source_pyx) as f:
template_content = f.read()
# Process the template with cython_constants
processed = Tempita.sub(template_content, **cython_constants)
# Write processed output to current working directory
# (which is the build directory when invoked by CMake)
output_pyx = "rgw_processed.pyx"
with open(output_pyx, 'w') as f:
f.write(processed)
source = output_pyx
# Disable cythonification if we're not really building anything
if (len(sys.argv) >= 2 and