Skip to content

Commit dc844dd

Browse files
committed
TMP
1 parent 1525795 commit dc844dd

File tree

2 files changed

+180
-6
lines changed

2 files changed

+180
-6
lines changed

Doc/extending/first-extension-module.rst

Lines changed: 178 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ Including the Header
149149

150150

151151
Now, put the first line in the file: include :file:`Python.h` to pull in
152-
all definitions of the Python C API:
152+
all declarations of the Python C API:
153153

154154
.. literalinclude:: ../includes/capi-extension/spammodule-01.c
155155
:start-at: <Python.h>
@@ -158,8 +158,8 @@ all definitions of the Python C API:
158158
This header contains all of the Python C API.
159159

160160
Next, bring in the declaration of the C library function we want to call.
161-
Documentation of the :c:func:`system` function tells us that we need
162-
``stdlib.h``:
161+
Documentation of the :c:func:`system` function tells us that teh necessary
162+
header is:
163163

164164
.. literalinclude:: ../includes/capi-extension/spammodule-01.c
165165
:start-at: <stdlib.h>
@@ -177,6 +177,181 @@ which affect the standard headers on some systems.
177177
and ``stdlib`` is one of them.
178178
However, it is good practice to explicitly include what you need.
179179

180+
With the includes in place, compile and import the extension again.
181+
You should get the same exception as with the empty file.
182+
183+
.. note::
184+
185+
Third-party build tools should handle pointing the compiler to
186+
the CPython headers and libraries, and setting appropriate options.
187+
188+
If you are running the compiler directly, you will need to do this yourself.
189+
If your installation of Python comes with a corresponding ``python-config``
190+
command, you can run something like:
191+
192+
.. code-block:: shell
193+
194+
gcc --shared $(python-config --cflags --ldflags) spammodule.c -o spam.so
195+
196+
197+
Module export hook
198+
==================
199+
200+
The exception you should be getting tells you that Python is looking for an
201+
module :ref:`export hook <extension-export-hook>` function.
202+
Let's define one.
203+
204+
First, let's add a prototype:
205+
206+
.. literalinclude:: ../includes/capi-extension/spammodule-01.c
207+
:start-after: /// Export hook prototype
208+
:end-before: ///
209+
210+
The :c:macro:`PyMODEXPORT_FUNC` macro declares the function's
211+
return type, and adds any special linkage declarations required by the platform
212+
to make the compiled extension export the function.
213+
For C++, it declares the function as ``extern "C"``.
214+
215+
.. tip::
216+
The prototype is not strictly necessary, but some modern C compilers emit
217+
warnings when a public, exported function has no prototype declaration.
218+
It's better to add a prototype than disable the warning, so that in other
219+
code you're notified in common cases of forgetting a header or misspelling
220+
a name.
221+
222+
After the prototype, add the function itself.
223+
For now, make it return ``NULL``:
224+
225+
.. code-block:: c
226+
227+
PyMODEXPORT_FUNC
228+
PyModExport_spam(void)
229+
{
230+
return NULL;
231+
}
232+
233+
Compile and load the module again.
234+
You should get a different error this time:
235+
236+
.. code-block:: pycon
237+
238+
>>> import spam
239+
Traceback (most recent call last):
240+
File "<string>", line 1, in <module>
241+
import spam
242+
SystemError: module export hook for module 'spam' failed without setting an exception
243+
244+
Many functions in the Python C API, including export hooks, are expected to
245+
do two things to signal that they have failed: return ``NULL``, and
246+
set an exception.
247+
Here, Python assumes that you only did half of this.
248+
249+
.. note::
250+
251+
This is one of a few situation where CPython checks this situation and
252+
emits a "nice" error message.
253+
Elsewhere, returning ``NULL`` without setting an exception can
254+
trigger undefined behavior.
255+
256+
257+
The slot table
258+
==============
259+
260+
Rather than ``NULL``, the export hook should return the information needed to
261+
create a module, as a ``static`` array of :c:type:`PyModuleDef_Slot` entries.
262+
Define this array just before your export hook:
263+
264+
.. code-block:: c
265+
266+
static PyModuleDef_Slot spam_slots[] = {
267+
{Py_mod_name, "spam"},
268+
{Py_mod_doc, PyDoc_STR("A wonderful module with an example function")},
269+
{0, NULL}
270+
};
271+
272+
The array contains:
273+
274+
* the module name (as a NUL-terminated UTF-8 encoded C string),
275+
* the docstring (similarly encoded), and
276+
* a zero-filled *sentinel* marking the end of the array.
277+
278+
.. tip::
279+
280+
The :c:func:`PyDoc_STR` macro can, in a special build mode, omit the
281+
docstring to save a bit of memory.
282+
283+
Return this array from your export hook, instead of ``NULL``:
284+
285+
.. code-block:: c
286+
:emphasize-lines: 4
287+
288+
PyMODEXPORT_FUNC
289+
PyModExport_spam(void)
290+
{
291+
return spam_slots;
292+
}
293+
294+
Now, recompile and try it out:
295+
296+
.. code-block:: pycon
297+
298+
>>> import spam
299+
>>> print(spam)
300+
<module 'spam' from '/home/encukou/dev/cpython/spam.so'>
301+
302+
You now have a extension module!
303+
Try ``help(spam)`` to see the docstring.
304+
305+
The next step will be adding a function to it.
306+
307+
308+
Adding a function
309+
=================
310+
311+
To expose a C function to Python, you need three things:
312+
313+
* The *implementation*: a C function that does what you need, and
314+
* a *name* to use in Python code.
315+
316+
You can also add a *dosctring*.
317+
OK, *amongst* the things you need to expose a C function are
318+
319+
320+
321+
322+
The :c:type:`PyModuleDef_Slot` array must be passed to the interpreter in the
323+
module's :ref:`export hook <extension-export-hook>` function.
324+
The hook must be named :c:func:`!PyModExport_name`, where *name* is the name
325+
of the module, and it should be the only non-\ ``static`` item defined in the
326+
module file.
327+
328+
Several modern C compilers emit warnings when you define a public function
329+
without a prototype declaration.
330+
Normally, prototypes go in a header file so they can be used from other
331+
C files.
332+
A function without a prototype is allowed in C, but is normally a strong
333+
hint that something is wrong -- for example, the name is misspelled.
334+
335+
In our case, Python will load our function dynamically, so a prototype
336+
isn't needed.
337+
We'll only add one to avoid well-meaning compiler warnings:
338+
339+
.. literalinclude:: ../includes/capi-extension/spammodule-01.c
340+
:start-after: /// Export hook prototype
341+
:end-before: ///
342+
343+
The :c:macro:`PyMODEXPORT_FUNC` macro declares the function's
344+
``PyModuleDef_Slot *`` return type, and adds any special linkage
345+
declarations required by the platform.
346+
For C++, it declares the function as ``extern "C"``.
347+
348+
Just after the prototype, we'll add the function itself.
349+
Its only job is to return the slot array, which in turn contains
350+
all the information needed to create our module object:
351+
352+
.. literalinclude:: ../includes/capi-extension/spammodule-01.c
353+
:start-after: /// Module export hook
354+
:end-before: ///
180355

181356

182357
The ``spam`` module

Doc/includes/capi-extension/spammodule-01.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ PyABIInfo_VAR(abi_info);
3737

3838
/// Module slot table
3939

40-
static PyModuleDef_Slot spam_module[] = {
41-
{Py_mod_abi, &abi_info},
40+
static PyModuleDef_Slot spam_slots[] = {
4241
{Py_mod_name, "spam"},
4342
{Py_mod_doc, PyDoc_STR("A wonderful module with an example function")},
4443
{Py_mod_methods, spam_methods},
@@ -55,7 +54,7 @@ PyMODEXPORT_FUNC PyModExport_spam(void);
5554
PyMODEXPORT_FUNC
5655
PyModExport_spam(void)
5756
{
58-
return spam_module;
57+
return spam_slots;
5958
}
6059

6160

0 commit comments

Comments
 (0)