From 27b427e89438b398649acff651e0120add69db3b Mon Sep 17 00:00:00 2001 From: Abderrahim Kitouni Date: Wed, 15 Dec 2021 15:38:54 +0100 Subject: [PATCH 1/3] setup.py: support pure-python cython extensions --- setup.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/setup.py b/setup.py index 3f48e0d9a..013842e82 100755 --- a/setup.py +++ b/setup.py @@ -253,12 +253,15 @@ def cythonize(extensions, **kwargs): missing_c_sources = [] for extension in extensions: + c_sources = [] for source in extension.sources: - if source.endswith(".pyx"): - c_file = source.replace(".pyx", ".c") + if source.endswith(".pyx") or source.endswith(".py"): + c_file = source.replace(".pyx", ".c").replace(".py", ".c") + c_sources.append(c_file) if not os.path.exists(c_file): missing_c_sources.append((extension, c_file)) + extension.sources = c_sources if missing_c_sources: for extension, source in missing_c_sources: @@ -273,29 +276,29 @@ def cythonize(extensions, **kwargs): def register_cython_module(module_name, dependencies=None): def files_from_module(modname): basename = "src/{}".format(modname.replace(".", "/")) + if os.path.exists("{}.py".format(basename)): + return ("{}.py".format(basename),) return "{}.pyx".format(basename), "{}.pxd".format(basename) if dependencies is None: dependencies = [] - implementation_file, definition_file = files_from_module(module_name) + cython_files = files_from_module(module_name) - assert os.path.exists(implementation_file) + assert os.path.exists(cython_files[0]) depends = [] - if os.path.exists(definition_file): - depends.append(definition_file) + if len(cython_files) == 2 and os.path.exists(cython_files[1]): + depends.append(cython_files[1]) for module in dependencies: - imp_file, def_file = files_from_module(module) - assert os.path.exists(imp_file), "Dependency file not found: {}".format(imp_file) - assert os.path.exists(def_file), "Dependency declaration file not found: {}".format(def_file) + files = files_from_module(module) + assert all(os.path.exists(f) for f in files), "Missing file for module: {}".format(module) - depends.append(imp_file) - depends.append(def_file) + depends.extend(files) BUILD_EXTENSIONS.append( - Extension(name=module_name, sources=[implementation_file], depends=depends, define_macros=extension_macros,) + Extension(name=module_name, sources=[cython_files[0]], depends=depends, define_macros=extension_macros,) ) From fe685c51b8349af3c2c6ec90ed9f8dc900e686f1 Mon Sep 17 00:00:00 2001 From: Abderrahim Kitouni Date: Wed, 15 Dec 2021 15:49:28 +0100 Subject: [PATCH 2/3] _types.py: compile as a pure-python module Fix lint and formatting errors in the process --- src/buildstream/{_types.pyx => _types.py} | 15 ++++++++------- src/buildstream/_types.pyi | 1 - 2 files changed, 8 insertions(+), 8 deletions(-) rename src/buildstream/{_types.pyx => _types.py} (91%) delete mode 100644 src/buildstream/_types.pyi diff --git a/src/buildstream/_types.pyx b/src/buildstream/_types.py similarity index 91% rename from src/buildstream/_types.pyx rename to src/buildstream/_types.py index ac65c505c..62a3ee011 100644 --- a/src/buildstream/_types.pyx +++ b/src/buildstream/_types.py @@ -58,8 +58,9 @@ def set_values(mcs, kls, data): value_to_entry = {} assert len(set(data.values())) == len(data.values()), "Values for {} are not unique".format(kls) - assert len(set(type(value) for value in data.values())) <= 1, \ - "Values of {} are of heterogeneous types".format(kls) + assert len(set(type(value) for value in data.values())) <= 1, "Values of {} are of heterogeneous types".format( + kls + ) for key, value in data.items(): new_value = object.__new__(kls) @@ -72,11 +73,11 @@ def set_values(mcs, kls, data): type.__setattr__(kls, "_value_to_entry", value_to_entry) - def __repr__(self): - return "".format(self.__name__) + def __repr__(cls): + return "".format(cls.__name__) - def __setattr__(self, key, value): + def __setattr__(cls, key, value): raise AttributeError("Adding new values dynamically is not supported") - def __iter__(self): - return iter(self._value_to_entry.values()) + def __iter__(cls): + return iter(cls._value_to_entry.values()) diff --git a/src/buildstream/_types.pyi b/src/buildstream/_types.pyi deleted file mode 100644 index d1986fc4a..000000000 --- a/src/buildstream/_types.pyi +++ /dev/null @@ -1 +0,0 @@ -class MetaFastEnum(type): ... From 36e7d5d5b2899d43a7a4610913a00fe1d1419fdc Mon Sep 17 00:00:00 2001 From: Abderrahim Kitouni Date: Wed, 15 Dec 2021 19:53:12 +0100 Subject: [PATCH 3/3] _utils.py: port to pure-python syntax Fix lint and formatting errors in the process --- pyproject.toml | 2 +- setup.cfg | 2 +- src/buildstream/{_utils.pyx => _utils.py} | 23 ++++++++++++----------- src/buildstream/_utils.pyi | 3 --- 4 files changed, 14 insertions(+), 16 deletions(-) rename src/buildstream/{_utils.pyx => _utils.py} (81%) delete mode 100644 src/buildstream/_utils.pyi diff --git a/pyproject.toml b/pyproject.toml index fefbbecd4..8afddd6f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = [ "setuptools>=36.6.0", # In order to build wheels, and as required by PEP 517 "wheel", - "Cython" + "Cython>=3a" ] build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 283d78099..b6817e7e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,7 +28,7 @@ warn_no_return = True # Ignore missing stubs for third-party packages. # In future, these should be re-enabled if/when stubs for them become available. -[mypy-copyreg,grpc,pluginbase,psutil,pyroaring,ruamel,multiprocessing.forkserver] +[mypy-copyreg,grpc,pluginbase,psutil,pyroaring,ruamel,multiprocessing.forkserver,cython.*] ignore_missing_imports=True # Ignore issues with generated files and vendored code diff --git a/src/buildstream/_utils.pyx b/src/buildstream/_utils.py similarity index 81% rename from src/buildstream/_utils.pyx rename to src/buildstream/_utils.py index 2386ef430..4b99ef25c 100644 --- a/src/buildstream/_utils.pyx +++ b/src/buildstream/_utils.py @@ -22,12 +22,13 @@ This module contains utilities that have been optimized in Cython """ -from cpython.pystate cimport PyThreadState_SetAsyncExc -from cpython.ref cimport PyObject +import cython # pylint: disable=import-error +from cython.cimports.cpython.pystate import PyThreadState_SetAsyncExc # pylint: disable=import-error +from cython.cimports.cpython.ref import PyObject # pylint: disable=import-error from ._signals import TerminateException -def url_directory_name(str url): +def url_directory_name(url: str): """Normalizes a url into a directory name Args: @@ -36,8 +37,7 @@ def url_directory_name(str url): Returns: A string which can be used as a directory name """ - return ''.join([_transl(x) for x in url]) - + return "".join([_transl(x) for x in url]) # terminate_thread() @@ -47,8 +47,8 @@ def url_directory_name(str url): # Args: # thread_id (int): the thread id in which to throw the exception # -def terminate_thread(long thread_id): - res = PyThreadState_SetAsyncExc(thread_id, TerminateException) +def terminate_thread(thread_id: cython.long): + res = PyThreadState_SetAsyncExc(thread_id, cython.cast(cython.pointer(PyObject), TerminateException)) assert res == 1 @@ -60,9 +60,9 @@ def terminate_thread(long thread_id): # Returns: # (bool): True if all characters are valid, False otherwise. # -def valid_chars_name(str name): - cdef int char_value - cdef int forbidden_char +def valid_chars_name(name: str): + char_value: cython.int + forbidden_char: cython.int for char_value in name: # 0-31 are control chars, 127 is DEL, and >127 means non-ASCII @@ -96,7 +96,8 @@ def valid_chars_name(str name): # # This transforms the value to "_" if is it not a ascii letter, a digit or "%" or "_" # -cdef Py_UNICODE _transl(Py_UNICODE x): +@cython.cfunc +def _transl(x: cython.Py_UNICODE) -> cython.Py_UNICODE: if ("a" <= x <= "z") or ("A" <= x <= "Z") or ("0" <= x <= "9") or x == "%": return x return "_" diff --git a/src/buildstream/_utils.pyi b/src/buildstream/_utils.pyi deleted file mode 100644 index 1938eec08..000000000 --- a/src/buildstream/_utils.pyi +++ /dev/null @@ -1,3 +0,0 @@ -def url_directory_name(url: str) -> str: ... -def valid_chars_name(name: str) -> bool: ... -def terminate_thread(thread_id: int) -> None: ...