Skip to content

Commit 6094564

Browse files
committed
Add _c_extension.pyi to template. Expand on Cython documentation.
1 parent fde7f13 commit 6094564

5 files changed

Lines changed: 54 additions & 15 deletions

File tree

README.rst

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Cython
8787
======
8888
This template has an option to add boilerplate for Cython_.
8989
Cython is a programming language that simplifies the creation of C extensions for Python.
90-
The Cython documentation is quite good; the aim of this section is to explain what this
90+
The `Cython documentation is quite good <https://cython.readthedocs.io/en/latest/src/userguide/language_basics.html>`_; the aim of this section is to explain what this
9191
template sets up, and what actions will still need to be performed by you.
9292
This explanation assumes you are familiar with C.
9393
Replace any reference here to ``pythontemplate`` with your project name.
@@ -97,31 +97,46 @@ Replace any reference here to ``pythontemplate`` with your project name.
9797

9898
2. Update ``pythontemplate/cpythontemplate.pxd`` with header information from the files in (1).
9999
Example of common definitions (functions, structs, and enums) are provided.
100-
Think of ``*.pxd`` as a header file that allows Cython ``.pyx`` code to access pure C files.
101-
This file will be compiled into a package that can be imported in a ``.pyx`` file via ``cimport``.
102-
If you don't plan on using any explicit C files, you may delete this file.
100+
Think of ``*.pxd`` as a header file that allows Cython ``.pyx`` code to access pure C ``.c`` files.
101+
This file will be compiled into a package of the same name that can be imported in a ``.pyx`` file via ``cimport``.
102+
If you don't plan on using any explicit C files, you may delete this file and the ``_c_src`` directory.
103103

104104
3. Add Cython code to ``pythontemplate/_c_extension.pyx``. Some class starter code is provided.
105105
This is where a good pythonic interface (functions and classes) should be written.
106106

107-
4. Optionally tweak ``build.py`` (runs at setup/installation) with compiler options.
107+
4. If adding type hints, update ``pythontemplate/_c_extension.pyi`` to reflect your ``.pyx`` implementation.
108+
109+
5. Optionally tweak ``build.py`` (runs at setup/installation) with compiler options.
108110
The default ``build.py`` offers a good, working starting point for most projects and performs the following:
109111

110112
a. Recursively searches for all C files in ``pythontemplate/_c_src/``.
111113
To change this action, modify the variable ``c_files``.
112114

113115
b. Compiles the code defined in ``_c_extension.pyx`` into a shared object file.
114116

115-
c. Adds ``pythontemplate`` and ``pythontemplate/_c_src`` to the Include Path (variable ``include_dirs``).
117+
c. Adds ``pythontemplate`` and ``pythontemplate/_c_src`` to the Include Path (python variable ``include_dirs``).
116118

117119
d. If your codebase contains a slower, python implementation of your Cython code,
118120
we can allow building to fail by uncommenting the ``allowed_to_fail`` logic at the top.
121+
The logic checks for the environment variable ``CIBUILDWHEEL`` because we don't want to allow
122+
build failures in our CI when creating pre-built wheels that we upload to PyPI.
119123

120-
5. The Github Action workflow defined in ``.github/workflows/build_wheels.yaml`` will create pre-built
124+
6. The Github Action workflow defined in ``.github/workflows/build_wheels.yaml`` will create pre-built
121125
binaries for all major Python versions, operating systems, and computer architectures.
122126
It will also create a Source Distribution (sdist).
123-
Finally, on git semver tags (``vX.X.X``), it will upload all the resulting wheels to PyPI.
127+
All of these distributions will be uploaded to the github action job page.
128+
On git semver tags (``vX.X.X``), they will be uploaded to PyPI.
129+
130+
When developing, you must re-run ``poetry-install`` to re-compile changes made in C/Cython code.
131+
The resulting, built Cython code will be importable from ``pythontemplate._c_extension``, so it may be
132+
good to add something like the following to your ``pythontemplate/__init__.py``:
133+
134+
.. code-block:: python
124135
136+
__all__ = [
137+
"Foo",
138+
]
139+
from pythontemplate._c_extension import Foo
125140
126141
Reference
127142
=========

bootstrap

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def validate_input(prompt, validate=None, default=""):
8383
break
8484
except BadResponseError as e:
8585
print(f'"{response}" {e}')
86+
print('To disable these validation checks, run "./bootstrap --no-verify"')
8687
return response
8788

8889

@@ -224,6 +225,7 @@ def main():
224225
if not is_typed:
225226
# Remove the py.typed file
226227
Path("pythontemplate/py.typed").unlink()
228+
Path("pythontemplate/_c_extension.pyi").unlink()
227229

228230
if not include_cli:
229231
# Delete CLI-replated files & config
@@ -256,9 +258,11 @@ def main():
256258
".github/workflows/build_wheels.yaml",
257259
"build.py",
258260
"pythontemplate/_c_src",
261+
"pythontemplate/_c_extension.pyx",
262+
"pythontemplate/cpythontemplate.pxd",
263+
# Do not include "pythontemplate/_c_extension.pyi" here,
264+
# it is removed in the ``is_typed`` section above.
259265
]
260-
paths_to_delete.extend(Path("pythontemplate").rglob("*.pyx"))
261-
paths_to_delete.extend(Path("pythontemplate").rglob("*.pxd"))
262266

263267
for path_to_delete in paths_to_delete:
264268
path_to_delete = Path(path_to_delete)

build.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
import shutil
33
from pathlib import Path
44

5-
# Uncommend if your library can still function if extensions fail to compile.
6-
allowed_to_fail = False
5+
# Uncomment if library can still function if extensions fail to compile (e.g. slower, python fallback).
6+
# Don't allow failure if cibuildwheel is running.
77
# allowed_to_fail = os.environ.get("CIBUILDWHEEL", "0") != "1"
8+
allowed_to_fail = False
89

910

1011
def build_cython_extensions():

pythontemplate/_c_extension.pyi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class Foo:
2+
def __init__(self): ...
3+
def __call__(self): ...
4+
5+
def divide(x: float, y: float) -> float: ...

pythontemplate/_c_extension.pyx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1-
cimport cpythontemplate
2-
# Invoke PyErr_CheckSignals occasionally if your C code runs long.
1+
cimport cpythontemplate # See cpythontemplate.pxd
2+
# Invoke PyErr_CheckSignals() occasionally if your C code runs long.
33
# This allows your code to be interrupted via ctrl+c.
44
from cpython.exc cimport PyErr_CheckSignals
55
from cpython.mem cimport PyMem_Malloc, PyMem_Free
66
from libc.stddef cimport size_t
77

88

99
cdef class Foo:
10+
"""Pythonic interface to the C "foo" struct."""
1011
cdef cpythontemplate.foo_t * _object
1112

1213
def __cinit__(self):
13-
# Allocate objects here
14+
# Automatically called before __init__.
15+
# All arguments passed to __init__ are also passed to __cinit__.
16+
# * As a convenience, if __cinit__() takes no arguments (other than self), it will
17+
# ignore arguments passed to the constructor without complaining about signature mismatch.
18+
# Allocate memory for C objects here.
1419
self._object = <cpythontemplate.foo_t *>PyMem_Malloc(sizeof(cpythontemplate.foo_t))
1520
if self._object is NULL:
1621
raise MemoryError
1722

1823
def __dealloc__(self):
24+
# Should "undo" __cinit__
1925
PyMem_Free(self._object)
2026

2127
def __init__(self):
@@ -24,3 +30,11 @@ cdef class Foo:
2430
def __call__(self):
2531
# invoke increment
2632
cpythontemplate.foo_increment(self._object)
33+
34+
# Functions declared with cpdef are visible to both cython and python.
35+
# https://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#python-functions-vs-c-functions
36+
# https://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#error-return-values
37+
cpdef float divide(float x, float y) except? 1.23:
38+
if y == 0.0:
39+
raise ZeroDivisionError
40+
return x / y

0 commit comments

Comments
 (0)