Skip to content

Commit cfc03a2

Browse files
committed
Added Sphinx build HTML
Contents now links to Sphinx build HTML as .md was a bit flakey.
1 parent d55b493 commit cfc03a2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+6515
-8
lines changed

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
doc/sphinx/build/*
21
src/build/*
32
*.so
43
PythonExtensionPatterns/*

README.md

+1-5
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,4 @@ Examples of reliable coding of Python 'C' extensions by Paul Ross.
44

55
## Contents:
66

7-
* Introduction to [reference counts](doc/sphinx/source/refcount.rst) in Python C extensions.
8-
* Managing [exceptions](doc/sphinx/source/exceptions.rst).
9-
* A [canonical function pattern](doc/sphinx/source/canonical_function.rst) for C extensions.
10-
* Handling [function arguments](doc/sphinx/source/parsing_arguments.rst).
11-
* Creating, setting and getting [module globals](doc/sphinx/source/module_globals.rst).
7+
The [Sphinx Documentation](doc/sphinx/build/html/index.html) for this project.
Binary file not shown.
3.75 KB
Binary file not shown.
14.8 KB
Binary file not shown.
13.3 KB
Binary file not shown.
5.05 KB
Binary file not shown.
4.57 KB
Binary file not shown.
20.7 KB
Binary file not shown.
Binary file not shown.
74.8 KB
Binary file not shown.

doc/sphinx/build/html/.buildinfo

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Sphinx build info version 1
2+
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3+
config: 16bf173fae07a841c4cb05b21572d779
4+
tags: 645f666f9bcd5a90fca523b33c5a78b7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
.. toctree::
2+
:maxdepth: 3
3+
4+
5+
===========================================
6+
A Pythonic Coding Pattern for C Functions
7+
===========================================
8+
9+
To avoid all the errors we have seen it is useful to have a C coding pattern for handling ``PyObjects`` that does the following:
10+
11+
* No early returns and a single place for clean up code.
12+
* Borrowed references incref'd and decref'd correctly.
13+
* No stale Exception from previous execution path.
14+
* Exceptions set and tested.
15+
* NULL is returned when an exception is set.
16+
* Non-NULL is returned when no exception is set.
17+
18+
The basic pattern in C is similar to Python's try/except/finally pattern:
19+
20+
.. code-block:: c
21+
22+
try:
23+
/* Do fabulous stuff here. */
24+
except:
25+
/* Handle every abnormal condition and clean up. */
26+
finally:
27+
/* Clean up under normal conditions and return an appropriate value. */
28+
29+
30+
Firstly we set any local ``PyObject`` (s) and the return value to ``NULL``:
31+
32+
.. code-block:: c
33+
34+
static PyObject *function(PyObject *arg_1) {
35+
PyObject *obj_a = NULL;
36+
PyObject *ret = NULL;
37+
38+
Then we have a little bit of Pythonic C - this can be omitted:
39+
40+
.. code-block:: c
41+
42+
goto try; /* Pythonic 'C' ;-) */
43+
try:
44+
45+
Check that there are no lingering Exceptions:
46+
47+
.. code-block:: c
48+
49+
assert(! PyErr_Occurred());
50+
51+
An alternative check for no lingering Exceptions:
52+
53+
.. code-block:: c
54+
55+
if(PyErr_Occurred()) {
56+
goto except;
57+
}
58+
59+
Now we assume that any argument is a "Borrowed" reference so we increment it (we need a matching ``Py_DECREF`` before function exit, see below). The first pattern assumes a non-NULL argument.
60+
61+
.. code-block:: c
62+
63+
assert(arg_1);
64+
Py_INCREF(arg_1);
65+
66+
If you are willing to accept NULL arguments then this pattern would be more suitable:
67+
68+
.. code-block:: c
69+
70+
if (arg_1) {
71+
Py_INCREF(arg_1);
72+
}
73+
74+
Of course the same test must be used when calling ``Py_DECFREF``, or just use ``Py_XDECREF``.
75+
76+
Now we create any local objects, if they are "Borrowed" references we need to incref them. With any abnormal behaviour we do a local jump straight to the cleanup code.
77+
78+
.. code-block:: c
79+
80+
/* Local object creation. */
81+
/* obj_a = ...; */
82+
if (! obj_a) {
83+
PyErr_SetString(PyExc_ValueError, "Ooops.");
84+
goto except;
85+
}
86+
/* If obj_a is a borrowed reference rather than a new reference. */
87+
Py_INCREF(obj_a);
88+
89+
Create the return value and deal with abnormal behaviour in the same way:
90+
91+
.. code-block:: c
92+
93+
/* More of your code to do stuff with arg_1 and obj_a. */
94+
/* Return object creation, ret should be a new reference otherwise you are in trouble. */
95+
/* ret = ...; */
96+
if (! ret) {
97+
PyErr_SetString(PyExc_ValueError, "Ooops again.");
98+
goto except;
99+
}
100+
101+
You might want to check the contents of the return value here. On error jump to ``except:`` otherwise jump to ``finally:``.
102+
103+
.. code-block:: c
104+
105+
/* Any return value checking here. */
106+
107+
/* If success then check exception is clear,
108+
* then goto finally; with non-NULL return value. */
109+
assert(! PyErr_Occurred());
110+
assert(ret);
111+
goto finally;
112+
113+
This is the except block where we cleanup any local objects and set the return value to NULL.
114+
115+
.. code-block:: c
116+
117+
except:
118+
/* Failure so Py_XDECREF the return value here. */
119+
Py_XDECREF(ret);
120+
/* Check a Python error is set somewhere above. */
121+
assert(PyErr_Occurred());
122+
/* Signal failure. */
123+
ret = NULL;
124+
125+
Notice the ``except:`` block falls through to the ``finally:`` block.
126+
127+
.. code-block:: c
128+
129+
finally:
130+
/* All _local_ PyObjects declared at the entry point are Py_XDECREF'd here.
131+
* For new references this will free them. For borrowed references this
132+
* will return them to their previous refcount.
133+
*/
134+
Py_XDECREF(obj_a);
135+
/* Decrement the ref count of externally supplied the arguments here.
136+
* If you allow arg_1 == NULL then Py_XDECREF(arg_1). */
137+
Py_DECREF(arg_1);
138+
/* And return...*/
139+
return ret;
140+
}
141+
142+
143+
Here is the complete code with minimal comments:
144+
145+
.. code-block:: c
146+
147+
static PyObject *function(PyObject *arg_1) {
148+
PyObject *obj_a = NULL;
149+
PyObject *ret = NULL;
150+
151+
goto try;
152+
try:
153+
assert(! PyErr_Occurred());
154+
assert(arg_1);
155+
Py_INCREF(arg_1);
156+
157+
/* obj_a = ...; */
158+
if (! obj_a) {
159+
PyErr_SetString(PyExc_ValueError, "Ooops.");
160+
goto except;
161+
}
162+
/* Only do this if obj_a is a borrowed reference. */
163+
Py_INCREF(obj_a);
164+
165+
/* More of your code to do stuff with obj_a. */
166+
167+
/* Return object creation, ret must be a new reference. */
168+
/* ret = ...; */
169+
if (! ret) {
170+
PyErr_SetString(PyExc_ValueError, "Ooops again.");
171+
goto except;
172+
}
173+
assert(! PyErr_Occurred());
174+
assert(ret);
175+
goto finally;
176+
except:
177+
Py_XDECREF(ret);
178+
assert(PyErr_Occurred());
179+
ret = NULL;
180+
finally:
181+
/* Only do this if obj_a is a borrowed reference. */
182+
Py_XDECREF(obj_a);
183+
Py_DECREF(arg_1);
184+
return ret;
185+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
.. highlight:: python
2+
:linenothreshold: 10
3+
4+
.. toctree::
5+
:maxdepth: 3
6+
7+
=================================
8+
Exception Raising
9+
=================================
10+
11+
A brief interlude on how to communicate error conditions from C code to Python.
12+
13+
These CPython calls are the most useful:
14+
15+
* ``PyErr_SetString(...)`` - To set an exception type with a fixed string.
16+
* ``PyErr_Format(...)`` - To set an exception type with a formatted string.
17+
* ``PyErr_Occurred()`` - To check if an exception has already been set in the flow of control.
18+
* ``PyErr_Clear()`` - Clearing any set exceptions, have good reason to do this!
19+
20+
Indicating an error condition is a two stage process; your code must register an exception and then indicate failure by returning ``NULL``. Here is a C function doing just that:
21+
22+
.. code-block:: c
23+
24+
static PyObject *_raise_error(PyObject *module) {
25+
26+
PyErr_SetString(PyExc_ValueError, "Ooops.");
27+
return NULL;
28+
}
29+
30+
You might want some dynamic information in the exception object, in that case ``PyErr_Format`` will do:
31+
32+
.. code-block:: c
33+
34+
static PyObject *_raise_error_formatted(PyObject *module) {
35+
36+
PyErr_Format(PyExc_ValueError,
37+
"Can not read %d bytes when offset %d in byte length %d.", \
38+
12, 25, 32
39+
);
40+
return NULL;
41+
}
42+
43+
If one of the two actions is missing then the exception will not be raised correctly. For example returning ``NULL`` without setting an exception type:
44+
45+
.. code-block:: c
46+
47+
/* Illustrate returning NULL but not setting an exception. */
48+
static PyObject *_raise_error_bad(PyObject *module) {
49+
return NULL;
50+
}
51+
52+
Executing this from Python will produce a clear error message (the C function ``_raise_error_bad()`` is mapped to the Python function ``cExcep.raiseErrBad()`` ::
53+
54+
>>> cExcep.raiseErrBad()
55+
Traceback (most recent call last):
56+
File "<stdin>", line 1, in <module>
57+
SystemError: error return without exception set
58+
59+
If the opposite error is made, that is setting an exception but not signalling then the function will succeed but leave a later runtime error:
60+
61+
.. code-block:: c
62+
63+
static PyObject *_raise_error_mixup(PyObject *module) {
64+
PyErr_SetString(PyExc_ValueError, "ERROR: _raise_error_mixup()");
65+
Py_RETURN_NONE;
66+
}
67+
68+
The confusion can arise is that if a subsequent function then tests to see if an exception is set, if so signal it. It will appear that the error is coming from the second function when actually it is from the first:
69+
70+
.. code-block:: c
71+
72+
static PyObject *_raise_error_mixup_test(PyObject *module) {
73+
if (PyErr_Occurred()) {
74+
return NULL;
75+
}
76+
Py_RETURN_NONE;
77+
}
78+
79+
The other thing to note is that if there are multiple calls to ``PyErr_SetString`` only the last one counts:
80+
81+
.. code-block:: c
82+
83+
static PyObject *_raise_error_overwrite(PyObject *module) {
84+
PyErr_SetString(PyExc_RuntimeError, "FORGOTTEN.");
85+
PyErr_SetString(PyExc_ValueError, "ERROR: _raise_error_overwrite()");
86+
assert(PyErr_Occurred());
87+
return NULL;
88+
}
89+
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.. Python Extension Patterns documentation master file, created by
2+
sphinx-quickstart on Sun May 11 19:42:01 2014.
3+
You can adapt this file completely to your liking, but it should at least
4+
contain the root `toctree` directive.
5+
6+
Coding Patterns for Python Extensions
7+
=====================================================
8+
9+
This describes reliable patterns of coding Python Extensions in C. It covers the essentials of reference counts, exceptions and creating functions that are safe and efficient.
10+
11+
.. toctree::
12+
:maxdepth: 3
13+
14+
refcount
15+
exceptions
16+
canonical_function
17+
parsing_arguments
18+
module_globals
19+
20+
21+
Indices and tables
22+
==================
23+
24+
* :ref:`search`
25+

0 commit comments

Comments
 (0)