summaryrefslogtreecommitdiff
path: root/docs/src/userguide/language_basics.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/src/userguide/language_basics.rst')
-rw-r--r--docs/src/userguide/language_basics.rst240
1 files changed, 156 insertions, 84 deletions
diff --git a/docs/src/userguide/language_basics.rst b/docs/src/userguide/language_basics.rst
index 593542eae..11561e1ee 100644
--- a/docs/src/userguide/language_basics.rst
+++ b/docs/src/userguide/language_basics.rst
@@ -48,7 +48,7 @@ the use of ‘early binding’ programming techniques.
C variable and type definitions
===============================
-C variables can be declared by
+C variables can be declared by
* using the Cython specific :keyword:`cdef` statement,
* using PEP-484/526 type annotations with C data types or
@@ -128,51 +128,6 @@ the declaration in most cases:
cdef float *g = [1, 2, 3, 4]
cdef float *h = &f
-In addition to the basic types, C :keyword:`struct`, :keyword:`union` and :keyword:`enum`
-are supported:
-
-.. tabs::
-
- .. group-tab:: Pure Python
-
- .. literalinclude:: ../../examples/userguide/language_basics/struct_union_enum.py
-
- .. note:: Currently, Pure Python mode does not support enums. (GitHub issue :issue:`4252`)
-
- .. group-tab:: Cython
-
- .. literalinclude:: ../../examples/userguide/language_basics/struct_union_enum.pyx
-
- See also :ref:`struct-union-enum-styles`
-
- .. note::
-
- Structs can be declared as ``cdef packed struct``, which has
- the same effect as the C directive ``#pragma pack(1)``.
-
- Declaring an enum as ``cpdef`` will create a :pep:`435`-style Python wrapper::
-
- cpdef enum CheeseState:
- hard = 1
- soft = 2
- runny = 3
-
- There is currently no special syntax for defining a constant, but you can use
- an anonymous :keyword:`enum` declaration for this purpose, for example,::
-
- cdef enum:
- tons_of_spam = 3
-
- .. note::
- the words ``struct``, ``union`` and ``enum`` are used only when
- defining a type, not when referring to it. For example, to declare a variable
- pointing to a ``Grail`` struct, you would write::
-
- cdef Grail *gp
-
- and not::
-
- cdef struct Grail *gp # WRONG
.. note::
@@ -197,46 +152,82 @@ are supported:
ctypedef int* IntPtr
+.. _structs:
+
+Structs, Unions, Enums
+----------------------
-You can create a C function by declaring it with :keyword:`cdef` or by decorating a Python function with ``@cfunc``:
+In addition to the basic types, C :keyword:`struct`, :keyword:`union` and :keyword:`enum`
+are supported:
.. tabs::
.. group-tab:: Pure Python
- .. code-block:: python
-
- @cython.cfunc
- def eggs(l: cython.ulong, f: cython.float) -> cython.int:
- ...
+ .. literalinclude:: ../../examples/userguide/language_basics/struct.py
.. group-tab:: Cython
- .. code-block:: cython
+ .. literalinclude:: ../../examples/userguide/language_basics/struct.pyx
- cdef int eggs(unsigned long l, float f):
- ...
+Structs can be declared as ``cdef packed struct``, which has
+the same effect as the C directive ``#pragma pack(1)``::
-You can read more about them in :ref:`python_functions_vs_c_functions`.
+ cdef packed struct StructArray:
+ int spam[4]
+ signed char eggs[5]
-Classes can be declared as :ref:`extension-types`. Those will
-have a behavior very close to python classes, but are faster because they use a ``struct``
-internally to store attributes.
-They are declared with the :keyword:`cdef` keyword or the ``@cclass`` class decorator.
+.. note::
+ This declaration removes the empty
+ space between members that C automatically to ensure that they're aligned in memory
+ (see `Wikipedia article <https://en.wikipedia.org/wiki/Data_structure_alignment>`_ for more details).
+ The main use is that numpy structured arrays store their data in packed form, so a ``cdef packed struct``
+ can be :ref:`used in a memoryview<using_memoryviews>` to match that.
-Here is a simple example:
+ Pure python mode does not support packed structs.
+
+The following example shows a declaration of unions:
.. tabs::
.. group-tab:: Pure Python
- .. literalinclude:: ../../examples/userguide/extension_types/shrubbery.py
+ .. literalinclude:: ../../examples/userguide/language_basics/union.py
.. group-tab:: Cython
- .. literalinclude:: ../../examples/userguide/extension_types/shrubbery.pyx
+ .. literalinclude:: ../../examples/userguide/language_basics/union.pyx
-You can read more about them in :ref:`extension-types`.
+Enums are created by ``cdef enum`` statement:
+
+.. literalinclude:: ../../examples/userguide/language_basics/enum.pyx
+
+
+.. note:: Currently, Pure Python mode does not support enums. (GitHub issue :issue:`4252`)
+
+Declaring an enum as ``cpdef`` will create a :pep:`435`-style Python wrapper::
+
+ cpdef enum CheeseState:
+ hard = 1
+ soft = 2
+ runny = 3
+
+There is currently no special syntax for defining a constant, but you can use
+an anonymous :keyword:`enum` declaration for this purpose, for example,::
+
+ cdef enum:
+ tons_of_spam = 3
+
+.. note::
+ In the Cython syntax, the words ``struct``, ``union`` and ``enum`` are used only when
+ defining a type, not when referring to it. For example, to declare a variable
+ pointing to a ``Grail`` struct, you would write::
+
+ cdef Grail *gp
+
+ and not::
+
+ cdef struct Grail *gp # WRONG
.. _typing_types:
@@ -326,12 +317,30 @@ and is typically what one wants).
If you want to use these numeric Python types simply omit the
type declaration and let them be objects.
+Extension Types
+---------------
+
It is also possible to declare :ref:`extension-types` (declared with ``cdef class`` or the ``@cclass`` decorator).
-This does allow subclasses. This typing is mostly used to access
-``cdef``/``@cfunc`` methods and attributes of the extension type.
+Those will have a behaviour very close to python classes (e.g. creating subclasses),
+but access to their members is faster from Cython code. Typing a variable
+as extension type is mostly used to access ``cdef``/``@cfunc`` methods and attributes of the extension type.
The C code uses a variable which is a pointer to a structure of the
specific type, something like ``struct MyExtensionTypeObject*``.
+Here is a simple example:
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/userguide/extension_types/shrubbery.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/userguide/extension_types/shrubbery.pyx
+
+You can read more about them in :ref:`extension-types`.
+
Grouping multiple C declarations
--------------------------------
@@ -459,7 +468,7 @@ passed in directly using a normal C function call.
C Functions declared using :keyword:`cdef` or the ``@cfunc`` decorator with a
Python object return type, like Python functions, will return a :keyword:`None`
value when execution leaves the function body without an explicit return value. This is in
-contrast to C/C++, which leaves the return value undefined.
+contrast to C/C++, which leaves the return value undefined.
In the case of non-Python object return types, the equivalent of zero is returned, for example, 0 for ``int``, :keyword:`False` for ``bint`` and :keyword:`NULL` for pointer types.
A more complete comparison of the pros and cons of these different method
@@ -634,7 +643,15 @@ parameters and has two required keyword parameters.
Function Pointers
-----------------
-Functions declared in a ``struct`` are automatically converted to function pointers.
+.. note:: Pointers to functions are currently not supported by pure Python mode. (GitHub issue :issue:`4279`)
+
+The following example shows declaring a ``ptr_add`` function pointer and assigning the ``add`` function to it:
+
+.. literalinclude:: ../../examples/userguide/language_basics/function_pointer.pyx
+
+Functions declared in a ``struct`` are automatically converted to function pointers:
+
+.. literalinclude:: ../../examples/userguide/language_basics/function_pointer_struct.pyx
For using error return values with function pointers, see the note at the bottom
of :ref:`error_return_values`.
@@ -652,16 +669,14 @@ through defined error return values. For functions that return a Python object
``NULL`` pointer, so any function returning a Python object has a well-defined
error return value.
-While this is always the case for C functions, functions
+While this is always the case for Python functions, functions
defined as C functions or ``cpdef``/``@ccall`` functions can return arbitrary C types,
-which do not have such a well-defined error return value. Thus, if an
-exception is detected in such a function, a warning message is printed,
-the exception is ignored, and the function returns immediately without
-propagating the exception to its caller.
+which do not have such a well-defined error return value.
+Extra care must be taken to ensure Python exceptions are correctly
+propagated from such functions.
-If you want such a C function to be able to propagate exceptions, you need
-to declare an exception return value for it as a contract with the caller.
-Here is an example
+A ``cdef`` function may be declared with an exception return value for it
+as a contract with the caller. Here is an example:
.. tabs::
@@ -684,7 +699,12 @@ Here is an example
With this declaration, whenever an exception occurs inside ``spam``, it will
immediately return with the value ``-1``. From the caller's side, whenever
a call to spam returns ``-1``, the caller will assume that an exception has
-occurred and can now process or propagate it.
+occurred and can now process or propagate it. Calling ``spam()`` is roughly translated to the following C code:
+
+.. code-block:: C
+
+ ret_val = spam();
+ if (ret_val == -1) goto error_handler;
When you declare an exception value for a function, you should never explicitly
or implicitly return that value. This includes empty :keyword:`return`
@@ -710,7 +730,7 @@ form of exception value declaration
def spam() -> cython.int:
...
- The keyword argument ``check=True`` indicates that the value ``-1`` _may_ signal an error.
+ The keyword argument ``check=True`` indicates that the value ``-1`` **may** signal an error.
.. group-tab:: Cython
@@ -719,11 +739,17 @@ form of exception value declaration
cdef int spam() except? -1:
...
- The ``?`` indicates that the value ``-1`` _may_ signal an error.
+ The ``?`` indicates that the value ``-1`` **may** signal an error.
In this case, Cython generates a call to :c:func:`PyErr_Occurred` if the exception value
is returned, to make sure it really received an exception and not just a normal
-result.
+result. Calling ``spam()`` is roughly translated to the following C code:
+
+
+.. code-block:: C
+
+ ret_val = spam();
+ if (ret_val == -1 && PyErr_Occurred()) goto error_handler;
There is also a third form of exception value declaration
@@ -735,18 +761,25 @@ There is also a third form of exception value declaration
@cython.cfunc
@cython.exceptval(check=True)
- def spam() -> cython.int:
+ def spam() -> cython.void:
...
.. group-tab:: Cython
.. code-block:: cython
- cdef int spam() except *:
+ cdef void spam() except *:
...
This form causes Cython to generate a call to :c:func:`PyErr_Occurred` after
-*every* call to spam, regardless of what value it returns. If you have a
+*every* call to spam, regardless of what value it returns. Calling ``spam()`` is roughly translated to the following C code:
+
+.. code-block:: C
+
+ spam()
+ if (PyErr_Occurred()) goto error_handler;
+
+If you have a
function returning ``void`` that needs to propagate errors, you will have to
use this form, since there isn't any error return value to test.
Otherwise, an explicit error return value allows the C compiler to generate
@@ -760,12 +793,47 @@ An external C++ function that may raise an exception can be declared with::
See :ref:`wrapping-cplusplus` for more details.
+Finally, if you are certain that your function should not raise an exception, (e.g., it
+does not use Python objects at all, or you plan to use it as a callback in C code that
+is unaware of Python exceptions), you can declare it as such using ``noexcept`` or by ``@cython.exceptval(check=False)``:
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ @cython.exceptval(check=False)
+ def spam() -> cython.int:
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef int spam() noexcept:
+ ...
+
+If a ``noexcept`` function *does* finish with an exception then it will print a warning message but not allow the exception to propagate further.
+On the other hand, calling a ``noexcept`` function has zero overhead related to managing exceptions, unlike the previous declarations.
+
Some things to note:
+* ``cdef`` functions that are also ``extern`` are implicitly declared ``noexcept`` or ``@cython.exceptval(check=False)``.
+ In the uncommon case of external C/C++ functions that **can** raise Python exceptions,
+ e.g., external functions that use the Python C API, you should explicitly declare
+ them with an exception value.
+
+* ``cdef`` functions that are *not* ``extern`` are implicitly declared with a suitable
+ exception specification for the return type (e.g. ``except *`` or ``@cython.exceptval(check=True)`` for a ``void`` return
+ type, ``except? -1`` or ``@cython.exceptval(-1, check=True)`` for an ``int`` return type).
+
* Exception values can only be declared for functions returning a C integer,
enum, float or pointer type, and the value must be a constant expression.
Functions that return ``void``, or a struct/union by value, can only use
the ``except *`` or ``exceptval(check=True)`` form.
+
* The exception value specification is part of the signature of the function.
If you're passing a pointer to a function as a parameter or assigning it
to a variable, the declared type of the parameter or variable must have
@@ -776,6 +844,10 @@ Some things to note:
.. note:: Pointers to functions are currently not supported by pure Python mode. (GitHub issue :issue:`4279`)
+* If the returning type of a ``cdef`` function with ``except *`` or ``@cython.exceptval(check=True)`` is C integer,
+ enum, float or pointer type, Cython calls :c:func:`PyErr_Occurred` only when
+ dedicated value is returned instead of checking after every call of the function.
+
* You don't need to (and shouldn't) declare exception values for functions
which return Python objects. Remember that a function with no declared
return type implicitly returns a Python object. (Exceptions on such
@@ -1094,7 +1166,7 @@ direct equivalent in Python.
* There is an ``&`` operator in Cython, with the same semantics as in C.
In pure python mode, use the ``cython.address()`` function instead.
* The null C pointer is called ``NULL``, not ``0``. ``NULL`` is a reserved word in Cython
- and special object in pure python mode.
+ and ``cython.NULL`` is a special object in pure python mode.
* Type casts are written ``<type>value`` or ``cast(type, value)``, for example,
.. tabs::