Skip to content

Pure python cython #1547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 15 additions & 12 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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,)
)


Expand Down
15 changes: 8 additions & 7 deletions src/buildstream/_types.pyx → src/buildstream/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -72,11 +73,11 @@ def set_values(mcs, kls, data):

type.__setattr__(kls, "_value_to_entry", value_to_entry)

def __repr__(self):
return "<fastenum '{}'>".format(self.__name__)
def __repr__(cls):
Copy link
Contributor

@nanonyme nanonyme Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks weird. This looks like it assumes it's a class method but there's no classmethod decorator. Is this change because it's a metaclass?

return "<fastenum '{}'>".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())
1 change: 0 additions & 1 deletion src/buildstream/_types.pyi

This file was deleted.

23 changes: 12 additions & 11 deletions src/buildstream/_utils.pyx → src/buildstream/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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()
Expand All @@ -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, <PyObject*> TerminateException)
def terminate_thread(thread_id: cython.long):
res = PyThreadState_SetAsyncExc(thread_id, cython.cast(cython.pointer(PyObject), TerminateException))
assert res == 1


Expand All @@ -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
Expand Down Expand Up @@ -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 "_"
Expand Down
3 changes: 0 additions & 3 deletions src/buildstream/_utils.pyi

This file was deleted.