summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatus Valo <matusvalo@users.noreply.github.com>2021-07-20 22:51:57 +0200
committerGitHub <noreply@github.com>2021-07-20 22:51:57 +0200
commitafc8a2c2f54624b1739c85fca6c8d9322881b05b (patch)
tree87688cd9ba555a152755e50f80fc5edc996bab2b
parentc47742190b3614789b3c16c439764e05a630bca6 (diff)
downloadcython-afc8a2c2f54624b1739c85fca6c8d9322881b05b.tar.gz
Introduce pure Python mode in the "language basics" documentation (GH-4242)
See https://github.com/cython/cython/issues/4187
-rw-r--r--docs/examples/userguide/extension_types/shrubbery.py14
-rw-r--r--docs/examples/userguide/extension_types/shrubbery.pyx4
-rw-r--r--docs/examples/userguide/language_basics/casting_python.pxd2
-rw-r--r--docs/examples/userguide/language_basics/casting_python.py22
-rw-r--r--docs/examples/userguide/language_basics/open_file.py19
-rw-r--r--docs/examples/userguide/language_basics/optional_subclassing.py19
-rw-r--r--docs/examples/userguide/language_basics/optional_subclassing.pyx6
-rw-r--r--docs/examples/userguide/language_basics/override.py17
-rw-r--r--docs/examples/userguide/language_basics/override.pyx4
-rw-r--r--docs/examples/userguide/language_basics/parameter_refcount.py23
-rw-r--r--docs/examples/userguide/language_basics/parameter_refcount.pyx3
-rw-r--r--docs/examples/userguide/language_basics/struct_union_enum.py7
-rw-r--r--docs/src/userguide/language_basics.rst679
13 files changed, 678 insertions, 141 deletions
diff --git a/docs/examples/userguide/extension_types/shrubbery.py b/docs/examples/userguide/extension_types/shrubbery.py
new file mode 100644
index 000000000..075664527
--- /dev/null
+++ b/docs/examples/userguide/extension_types/shrubbery.py
@@ -0,0 +1,14 @@
+from __future__ import print_function
+
+@cython.cclass
+class Shrubbery:
+ width: cython.int
+ height: cython.int
+
+ def __init__(self, w, h):
+ self.width = w
+ self.height = h
+
+ def describe(self):
+ print("This shrubbery is", self.width,
+ "by", self.height, "cubits.")
diff --git a/docs/examples/userguide/extension_types/shrubbery.pyx b/docs/examples/userguide/extension_types/shrubbery.pyx
index 780735c9d..b74dfbd1b 100644
--- a/docs/examples/userguide/extension_types/shrubbery.pyx
+++ b/docs/examples/userguide/extension_types/shrubbery.pyx
@@ -1,7 +1,9 @@
from __future__ import print_function
+
cdef class Shrubbery:
- cdef int width, height
+ cdef int width
+ cdef int height
def __init__(self, w, h):
self.width = w
diff --git a/docs/examples/userguide/language_basics/casting_python.pxd b/docs/examples/userguide/language_basics/casting_python.pxd
new file mode 100644
index 000000000..fa3d46030
--- /dev/null
+++ b/docs/examples/userguide/language_basics/casting_python.pxd
@@ -0,0 +1,2 @@
+cdef extern from *:
+ ctypedef Py_ssize_t Py_intptr_t
diff --git a/docs/examples/userguide/language_basics/casting_python.py b/docs/examples/userguide/language_basics/casting_python.py
new file mode 100644
index 000000000..1c02c461c
--- /dev/null
+++ b/docs/examples/userguide/language_basics/casting_python.py
@@ -0,0 +1,22 @@
+from cython.cimports.cpython.ref import PyObject
+
+def main():
+
+ python_string = "foo"
+
+ # Note that the variables below are automatically inferred
+ # as the correct pointer type that is assigned to them.
+ # They do not need to be typed explicitly.
+
+ ptr = cython.cast(cython.p_void, python_string)
+ adress_in_c = cython.cast(Py_intptr_t, ptr)
+ address_from_void = adress_in_c # address_from_void is a python int
+
+ ptr2 = cython.cast(cython.pointer(PyObject), python_string)
+ address_in_c2 = cython.cast(Py_intptr_t, ptr2)
+ address_from_PyObject = address_in_c2 # address_from_PyObject is a python int
+
+ assert address_from_void == address_from_PyObject == id(python_string)
+
+ print(cython.cast(object, ptr)) # Prints "foo"
+ print(cython.cast(object, ptr2)) # prints "foo"
diff --git a/docs/examples/userguide/language_basics/open_file.py b/docs/examples/userguide/language_basics/open_file.py
new file mode 100644
index 000000000..ad3ae0374
--- /dev/null
+++ b/docs/examples/userguide/language_basics/open_file.py
@@ -0,0 +1,19 @@
+from cython.cimports.libc.stdio import FILE, fopen
+from cython.cimports.libc.stdlib import malloc, free
+from cython.cimports.cpython.exc import PyErr_SetFromErrnoWithFilenameObject
+
+def open_file():
+ p = fopen("spam.txt", "r") # The type of "p" is "FILE*", as returned by fopen().
+
+ if p is cython.NULL:
+ PyErr_SetFromErrnoWithFilenameObject(OSError, "spam.txt")
+ ...
+
+
+def allocating_memory(number=10):
+ # Note that the type of the variable "my_array" is automatically inferred from the assignment.
+ my_array = cython.cast(p_double, malloc(number * cython.sizeof(double)))
+ if not my_array: # same as 'is NULL' above
+ raise MemoryError()
+ ...
+ free(my_array)
diff --git a/docs/examples/userguide/language_basics/optional_subclassing.py b/docs/examples/userguide/language_basics/optional_subclassing.py
new file mode 100644
index 000000000..480ae100b
--- /dev/null
+++ b/docs/examples/userguide/language_basics/optional_subclassing.py
@@ -0,0 +1,19 @@
+from __future__ import print_function
+
+@cython.cclass
+class A:
+ @cython.cfunc
+ def foo(self):
+ print("A")
+
+@cython.cclass
+class B(A):
+ @cython.cfunc
+ def foo(self, x=None):
+ print("B", x)
+
+@cython.cclass
+class C(B):
+ @cython.ccall
+ def foo(self, x=True, k:cython.int = 3):
+ print("C", x, k)
diff --git a/docs/examples/userguide/language_basics/optional_subclassing.pyx b/docs/examples/userguide/language_basics/optional_subclassing.pyx
index 88371ab41..b2a3d4dec 100644
--- a/docs/examples/userguide/language_basics/optional_subclassing.pyx
+++ b/docs/examples/userguide/language_basics/optional_subclassing.pyx
@@ -1,13 +1,19 @@
from __future__ import print_function
+
cdef class A:
+
cdef foo(self):
print("A")
+
cdef class B(A):
+
cdef foo(self, x=None):
print("B", x)
+
cdef class C(B):
+
cpdef foo(self, x=True, int k=3):
print("C", x, k)
diff --git a/docs/examples/userguide/language_basics/override.py b/docs/examples/userguide/language_basics/override.py
new file mode 100644
index 000000000..f9e0be83f
--- /dev/null
+++ b/docs/examples/userguide/language_basics/override.py
@@ -0,0 +1,17 @@
+from __future__ import print_function
+
+@cython.cclass
+class A:
+ @cython.cfunc
+ def foo(self):
+ print("A")
+
+@cython.cclass
+class B(A):
+ @cython.ccall
+ def foo(self):
+ print("B")
+
+class C(B): # NOTE: no cclass decorator
+ def foo(self):
+ print("C")
diff --git a/docs/examples/userguide/language_basics/override.pyx b/docs/examples/userguide/language_basics/override.pyx
index 13f0b2a64..1a7ceefb7 100644
--- a/docs/examples/userguide/language_basics/override.pyx
+++ b/docs/examples/userguide/language_basics/override.pyx
@@ -1,10 +1,14 @@
from __future__ import print_function
+
cdef class A:
+
cdef foo(self):
print("A")
+
cdef class B(A):
+
cpdef foo(self):
print("B")
diff --git a/docs/examples/userguide/language_basics/parameter_refcount.py b/docs/examples/userguide/language_basics/parameter_refcount.py
new file mode 100644
index 000000000..2b25915ba
--- /dev/null
+++ b/docs/examples/userguide/language_basics/parameter_refcount.py
@@ -0,0 +1,23 @@
+from __future__ import print_function
+
+from cython.cimports.cpython.ref import PyObject
+
+import sys
+
+python_dict = {"abc": 123}
+python_dict_refcount = sys.getrefcount(python_dict)
+
+@cython.cfunc
+def owned_reference(obj: object):
+ refcount = sys.getrefcount(python_dict)
+ print('Inside owned_reference: {refcount}'.format(refcount=refcount))
+
+@cython.cfunc
+def borrowed_reference(obj: cython.pointer(PyObject)):
+ refcount = obj.ob_refcnt
+ print('Inside borrowed_reference: {refcount}'.format(refcount=refcount))
+
+def main():
+ print('Initial refcount: {refcount}'.format(refcount=python_dict_refcount))
+ owned_reference(python_dict)
+ borrowed_reference(cython.cast(cython.pointer(PyObject), python_dict))
diff --git a/docs/examples/userguide/language_basics/parameter_refcount.pyx b/docs/examples/userguide/language_basics/parameter_refcount.pyx
index 5ffd28c44..6fe3ffadd 100644
--- a/docs/examples/userguide/language_basics/parameter_refcount.pyx
+++ b/docs/examples/userguide/language_basics/parameter_refcount.pyx
@@ -7,14 +7,17 @@ import sys
python_dict = {"abc": 123}
python_dict_refcount = sys.getrefcount(python_dict)
+
cdef owned_reference(object obj):
refcount = sys.getrefcount(python_dict)
print('Inside owned_reference: {refcount}'.format(refcount=refcount))
+
cdef borrowed_reference(PyObject * obj):
refcount = obj.ob_refcnt
print('Inside borrowed_reference: {refcount}'.format(refcount=refcount))
+
print('Initial refcount: {refcount}'.format(refcount=python_dict_refcount))
owned_reference(python_dict)
borrowed_reference(<PyObject *>python_dict)
diff --git a/docs/examples/userguide/language_basics/struct_union_enum.py b/docs/examples/userguide/language_basics/struct_union_enum.py
new file mode 100644
index 000000000..b78c0aa02
--- /dev/null
+++ b/docs/examples/userguide/language_basics/struct_union_enum.py
@@ -0,0 +1,7 @@
+Grail = cython.struct(
+ age=cython.int,
+ volume=cython.float)
+
+Food = cython.union(
+ spam=cython.p_char,
+ eggs=cython.p_float)
diff --git a/docs/src/userguide/language_basics.rst b/docs/src/userguide/language_basics.rst
index 9a17d464a..6ccd7e804 100644
--- a/docs/src/userguide/language_basics.rst
+++ b/docs/src/userguide/language_basics.rst
@@ -11,6 +11,9 @@
Language Basics
*****************
+.. include::
+ ../two-syntax-variants-used
+
.. _declaring_data_types:
Declaring Data Types
@@ -44,72 +47,152 @@ the use of ‘early binding’ programming techniques.
C variable and type definitions
===============================
-The :keyword:`cdef` statement is used to declare C variables, either local or
-module-level::
+C variables can be declared by
- cdef int i, j, k
- cdef float f, g[42], *h
+* using the Cython specific :keyword:`cdef` statement,
+* using PEP-484/526 type annotations with C data types or
+* using the function ``cython.declare()``.
-and C :keyword:`struct`, :keyword:`union` or :keyword:`enum` types:
+The :keyword:`cdef` statement and ``declare()`` can define function-local and
+module-level variables as well as attributes in classes, but type annotations only
+affect local variables and attributes and are ignored at the module level.
+This is because type annotations are not Cython specific, so Cython keeps
+the variables in the module dict (as Python values) instead of making them
+module internal C variables. Use ``declare()`` in Python code to explicitly
+define global C variables.
-.. literalinclude:: ../../examples/userguide/language_basics/struct_union_enum.pyx
+.. tabs::
-See also :ref:`struct-union-enum-styles`
+ .. group-tab:: Pure Python
-.. note::
+ .. code-block:: python
+
+ a_global_variable = declare(cython.int)
+
+ def func():
+ i: cython.int
+ j: cython.int
+ k: cython.int
+ f: cython.float
+ g: cython.int[42]
+ h: cython.p_float
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef int a_global_variable
+
+ def func():
+ cdef int i, j, k
+ cdef float f, g[42], *h
+
+Moreover, C :keyword:`struct`, :keyword:`union` and :keyword:`enum` are supported:
+
+.. tabs::
+
+ .. group-tab:: Pure Python
- Structs can be declared as ``cdef packed struct``, which has
- the same effect as the C directive ``#pragma pack(1)``.
+ .. literalinclude:: ../../examples/userguide/language_basics/struct_union_enum.py
-Declaring an enum as ``cpdef`` will create a :pep:`435`-style Python wrapper::
+ .. note:: Currently, Pure Python mode does not support enums. (GitHub issue :issue:`4252`)
- cpdef enum CheeseState:
- hard = 1
- soft = 2
- runny = 3
+ .. group-tab:: Cython
+ .. literalinclude:: ../../examples/userguide/language_basics/struct_union_enum.pyx
+ See also :ref:`struct-union-enum-styles`
-There is currently no special syntax for defining a constant, but you can use
-an anonymous :keyword:`enum` declaration for this purpose, for example,::
+ .. note::
- cdef enum:
- tons_of_spam = 3
+ 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::
- 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`` you would write::
- cdef Grail *gp
+ There is also support for giving names to types using the
+ ``ctypedef`` statement or the ``cython.typedef()`` function, e.g.
- and not::
+ .. tabs::
- cdef struct Grail *gp # WRONG
+ .. group-tab:: Pure Python
- There is also a ``ctypedef`` statement for giving names to types, e.g.::
+ .. code-block:: python
- ctypedef unsigned long ULong
+ ULong = cython.typedef(cython.ulong)
- ctypedef int* IntPtr
+ IntPtr = cython.typedef(cython.p_int)
+ .. group-tab:: Cython
-It is also possible to declare functions with :keyword:`cdef`, making them c functions.
+ .. code-block:: cython
-::
+ ctypedef unsigned long ULong
- cdef int eggs(unsigned long l, float f):
- ...
+ ctypedef int* IntPtr
+
+
+You can create a C function by declaring it with :keyword:`cdef` or by decorating a Python function with ``@cfunc``:
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ def eggs(l: cython.ulong, f: cython.float) -> cython.int:
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef int eggs(unsigned long l, float f):
+ ...
You can read more about them in :ref:`python_functions_vs_c_functions`.
-You can declare classes with :keyword:`cdef`, making them :ref:`extension-types`. Those will
+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.
Here is a simple example:
-.. literalinclude:: ../../examples/userguide/extension_types/shrubbery.pyx
+.. 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`.
@@ -119,15 +202,21 @@ You can read more about them in :ref:`extension-types`.
Types
-----
-Cython uses the normal C syntax for C types, including pointers. It provides
+The Cython language uses the normal C syntax for C types, including pointers. It provides
all the standard C types, namely ``char``, ``short``, ``int``, ``long``,
-``long long`` as well as their ``unsigned`` versions, e.g. ``unsigned int``.
+``long long`` as well as their ``unsigned`` versions,
+e.g. ``unsigned int`` (``cython.uint`` in Python code).
The special ``bint`` type is used for C boolean values (``int`` with 0/non-0
values for False/True) and ``Py_ssize_t`` for (signed) sizes of Python
containers.
-Pointer types are constructed as in C, by appending a ``*`` to the base type
-they point to, e.g. ``int**`` for a pointer to a pointer to a C int.
+Pointer types are constructed as in C when using Cython syntax, by appending a ``*`` to the base type
+they point to, e.g. ``int**`` for a pointer to a pointer to a C int. In Pure python mode, simple pointer types
+use a naming scheme with "p"s instead, separated from the type name with an underscore, e.g. ``cython.pp_int`` for a pointer to
+a pointer to a C int. Further pointer types can be constructed with the ``cython.pointer()`` function,
+e.g. ``cython.pointer(cython.int)``.
+
+
Arrays use the normal C array syntax, e.g. ``int[10]``, and the size must be known
at compile time for stack allocated arrays. Cython doesn't support variable length arrays from C99.
Note that Cython uses array access for pointer dereferencing, as ``*x`` is not valid Python syntax,
@@ -135,24 +224,53 @@ whereas ``x[0]`` is.
Also, the Python types ``list``, ``dict``, ``tuple``, etc. may be used for
static typing, as well as any user defined :ref:`extension-types`.
-For example::
+For example
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ def main():
+ foo: list = []
- cdef list foo = []
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef list foo = []
This requires an *exact* match of the class, it does not allow subclasses.
This allows Cython to optimize code by accessing internals of the builtin class,
which is the main reason for declaring builtin types in the first place.
For declared builtin types, Cython uses internally a C variable of type ``PyObject*``.
-The Python types int, long, and float are not available for static
-typing and instead interpreted as C ``int``, ``long``, and ``float``
-respectively, as statically typing variables with these Python
-types has zero advantages.
+
+.. note:: The Python types ``int``, ``long``, and ``float`` are not available for static
+ typing in ``.pyx`` files and instead interpreted as C ``int``, ``long``, and ``float``
+ respectively, as statically typing variables with these Python
+ types has zero advantages. On the other hand, annotating in Pure Python with
+ ``int``, ``long``, and ``float`` Python types will be interpreted as
+ Python object types.
Cython provides an accelerated and typed equivalent of a Python tuple, the ``ctuple``.
-A ``ctuple`` is assembled from any valid C types. For example::
+A ``ctuple`` is assembled from any valid C types. For example
+
+.. tabs::
+
+ .. group-tab:: Pure Python
- cdef (double, int) bar
+ .. code-block:: python
+
+ def main():
+ bar: (cython.double, cython.int)
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef (double, int) bar
They compile down to C-structures and can be used as efficient alternatives to
Python tuples.
@@ -165,9 +283,9 @@ 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.
-It is also possible to declare :ref:`extension-types` (declared with ``cdef class``).
+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`` methods and attributes of the extension type.
+``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*``.
@@ -178,8 +296,11 @@ Grouping multiple C declarations
If you have a series of declarations that all begin with :keyword:`cdef`, you
can group them into a :keyword:`cdef` block like this:
+.. note:: This is supported only in Cython's ``cdef`` syntax.
+
.. literalinclude:: ../../examples/userguide/language_basics/cdef_block.pyx
+
.. _cpdef:
.. _cdef:
.. _python_functions_vs_c_functions:
@@ -189,48 +310,95 @@ Python functions vs. C functions
There are two kinds of function definition in Cython:
-Python functions are defined using the def statement, as in Python. They take
+Python functions are defined using the :keyword:`def` statement, as in Python. They take
:term:`Python objects<Python object>` as parameters and return Python objects.
-C functions are defined using the new :keyword:`cdef` statement. They take
+C functions are defined using the :keyword:`cdef` statement in Cython syntax or with the ``@cfunc`` decorator. They take
either Python objects or C values as parameters, and can return either Python
objects or C values.
Within a Cython module, Python functions and C functions can call each other
freely, but only Python functions can be called from outside the module by
interpreted Python code. So, any functions that you want to "export" from your
-Cython module must be declared as Python functions using def.
-There is also a hybrid function, called :keyword:`cpdef`. A :keyword:`cpdef`
-can be called from anywhere, but uses the faster C calling conventions
-when being called from other Cython code. A :keyword:`cpdef` can also be overridden
+Cython module must be declared as Python functions using ``def``.
+There is also a hybrid function, declared with :keyword:`cpdef` in ``.pyx`` files or with the ``@ccall`` decorator. These functions
+can be called from anywhere, but use the faster C calling convention
+when being called from other Cython code. They can also be overridden
by a Python method on a subclass or an instance attribute, even when called from Cython.
If this happens, most performance gains are of course lost and even if it does not,
-there is a tiny overhead in calling a :keyword:`cpdef` method from Cython compared to
-calling a :keyword:`cdef` method.
+there is a tiny overhead in calling such a method from Cython compared to
+calling a C method.
Parameters of either type of function can be declared to have C data types,
-using normal C declaration syntax. For example,::
+using normal C declaration syntax. For example,
- def spam(int i, char *s):
- ...
+.. tabs::
- cdef int eggs(unsigned long l, float f):
- ...
+ .. group-tab:: Pure Python
-``ctuples`` may also be used::
+ .. code-block:: python
- cdef (int, float) chips((long, long, double) t):
- ...
+ def spam(i: cython.int, s: cython.p_char):
+ ...
+
+ @cython.cfunc
+ def eggs(l: cython.ulong, f: cython.float) -> cython.int:
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ def spam(int i, char *s):
+ ...
+
+
+ cdef int eggs(unsigned long l, float f):
+ ...
+``ctuples`` may also be used
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ def chips(t: (cython.long, cython.long, cython.double)) -> (cython.int, cython.float):
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef (int, float) chips((long, long, double) t):
+ ...
When a parameter of a Python function is declared to have a C data type, it is
passed in as a Python object and automatically converted to a C value, if
possible. In other words, the definition of ``spam`` above is equivalent to
-writing::
+writing
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ def spam(python_i, python_s):
+ i: cython.int = python_i
+ s: cython.p_char = python_s
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ def spam(python_i, python_s):
+ cdef int i = python_i
+ cdef char* s = python_s
+ ...
- def spam(python_i, python_s):
- cdef int i = python_i
- cdef char* s = python_s
- ...
Automatic conversion is currently only possible for numeric types,
string types and structs (composed recursively of any of these types);
@@ -243,7 +411,8 @@ with string attributes if they are to be used after the function returns.
C functions, on the other hand, can have parameters of any type, since they're
passed in directly using a normal C function call.
-Functions declared using :keyword:`cdef` with Python object return type, like Python functions, will return a :keyword:`None`
+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.
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.
@@ -257,10 +426,24 @@ Python objects as parameters and return values
If no type is specified for a parameter or return value, it is assumed to be a
Python object. (Note that this is different from the C convention, where it
would default to int.) For example, the following defines a C function that
-takes two Python objects as parameters and returns a Python object::
+takes two Python objects as parameters and returns a Python object
- cdef spamobjs(x, y):
- ...
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ def spamobjs(x, y):
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef spamobjs(x, y):
+ ...
Reference counting for these objects is performed automatically according to
the standard Python/C API rules (i.e. borrowed references are taken as
@@ -274,17 +457,34 @@ parameters and a new reference is returned).
The name object can also be used to explicitly declare something as a Python
object. This can be useful if the name being declared would otherwise be taken
-as the name of a type, for example,::
+as the name of a type, for example,
- cdef ftang(object int):
- ...
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ def ftang(int: object):
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
-declares a parameter called int which is a Python object. You can also use
+ cdef ftang(object int):
+ ...
+
+declares a parameter called ``int`` which is a Python object. You can also use
object as the explicit return type of a function, e.g.::
cdef object ftang(object int):
...
+.. note:: Currently, Cython contains a bug not allowing ``object`` as return annotation in
+ pure Python from a C function. (GitHub issue :issue:`2529`)
+
In the interests of clarity, it is probably a good idea to always be explicit
about object parameters in C functions.
@@ -292,7 +492,15 @@ about object parameters in C functions.
To create a borrowed reference, specify the parameter type as ``PyObject*``.
Cython won't perform automatic ``Py_INCREF``, or ``Py_DECREF``, e.g.:
-.. literalinclude:: ../../examples/userguide/language_basics/parameter_refcount.pyx
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/userguide/language_basics/parameter_refcount.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/userguide/language_basics/parameter_refcount.pyx
will display::
@@ -305,23 +513,34 @@ will display::
Optional Arguments
------------------
-Unlike C, it is possible to use optional arguments in ``cdef`` and ``cpdef`` functions.
-There are differences though whether you declare them in a ``.pyx``
+Unlike C, it is possible to use optional arguments in C and ``cpdef``/``@ccall`` functions.
+There are differences though whether you declare them in a ``.pyx``/``.py``
file or the corresponding ``.pxd`` file.
To avoid repetition (and potential future inconsistencies), default argument values are
not visible in the declaration (in ``.pxd`` files) but only in
the implementation (in ``.pyx`` files).
-When in a ``.pyx`` file, the signature is the same as it is in Python itself:
+When in a ``.pyx``/``.py`` file, the signature is the same as it is in Python itself:
-.. literalinclude:: ../../examples/userguide/language_basics/optional_subclassing.pyx
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/userguide/language_basics/optional_subclassing.py
+ :caption: optional_subclassing.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/userguide/language_basics/optional_subclassing.pyx
+ :caption: optional_subclassing.pyx
When in a ``.pxd`` file, the signature is different like this example: ``cdef foo(x=*)``.
This is because the program calling the function just needs to know what signatures are
possible in C, but doesn't need to know the value of the default arguments.:
.. literalinclude:: ../../examples/userguide/language_basics/optional_subclassing.pxd
+ :caption: optional_subclassing.pxd
.. note::
The number of arguments may increase when subclassing,
@@ -374,8 +593,8 @@ 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 :keyword:`def` functions, functions
-defined as :keyword:`cdef` or :keyword:`cpdef` can return arbitrary C types,
+While this is always the case for C 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
@@ -383,10 +602,25 @@ propagating the exception to its caller.
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::
+Here is an example
- cdef int spam() except -1:
- ...
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ @cython.exceptval(-1)
+ def spam() -> cython.int:
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef int spam() except -1:
+ ...
With this declaration, whenever an exception occurs inside ``spam``, it will
immediately return with the value ``-1``. From the caller's side, whenever
@@ -404,20 +638,53 @@ returns small results.
If all possible return values are legal and you
can't reserve one entirely for signalling errors, you can use an alternative
-form of exception value declaration::
+form of exception value declaration
- cdef int spam() except? -1:
- ...
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ @cython.exceptval(-1, check=True)
+ def spam() -> cython.int:
+ ...
+
+ The keyword argument ``check=True`` indicates that the value ``-1`` _may_ signal an error.
-The "?" indicates that the value ``-1`` only signals a possible error. In this
-case, Cython generates a call to :c:func:`PyErr_Occurred` if the exception value
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef int spam() except? -1:
+ ...
+
+ 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.
-There is also a third form of exception value declaration::
+There is also a third form of exception value declaration
- cdef int spam() except *:
- ...
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ @cython.cfunc
+ @cython.exceptval(check=True)
+ def spam() -> cython.int:
+ ...
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef int 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
@@ -430,6 +697,8 @@ An external C++ function that may raise an exception can be declared with::
cdef int spam() except +
+.. note:: These declarations are not used in Python code, only in ``.pxd`` and ``.pyx`` files.
+
See :ref:`wrapping-cplusplus` for more details.
Some things to note:
@@ -437,7 +706,7 @@ Some things to note:
* 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 *`` form.
+ 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
@@ -446,6 +715,8 @@ Some things to note:
int (*grail)(int, char*) except -1
+ .. note:: Pointers to functions are currently not supported by pure Python mode. (GitHub issue :issue:`4279`)
+
* 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
@@ -470,7 +741,15 @@ function or a C function that calls Python/C API routines. To get an exception
from a non-Python-aware function such as :func:`fopen`, you will have to check the
return value and raise it yourself, for example:
-.. literalinclude:: ../../examples/userguide/language_basics/open_file.pyx
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/userguide/language_basics/open_file.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/userguide/language_basics/open_file.pyx
.. _overriding_in_extension_types:
@@ -478,15 +757,30 @@ Overriding in extension types
-----------------------------
-``cpdef`` methods can override ``cdef`` methods:
+``cpdef``/``@ccall`` methods can override C methods:
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/userguide/language_basics/optional_subclassing.py
+
+ .. group-tab:: Cython
-.. literalinclude:: ../../examples/userguide/language_basics/optional_subclassing.pyx
+ .. literalinclude:: ../../examples/userguide/language_basics/optional_subclassing.pyx
When subclassing an extension type with a Python class,
-``def`` methods can override ``cpdef`` methods but not ``cdef``
-methods:
+Python methods can override ``cpdef``/``@ccall`` methods but not plain C methods:
-.. literalinclude:: ../../examples/userguide/language_basics/override.pyx
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/userguide/language_basics/override.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/userguide/language_basics/override.pyx
If ``C`` above would be an extension type (``cdef class``),
this would not work correctly.
@@ -548,10 +842,24 @@ as the C string is needed. If you can't guarantee that the Python string will
live long enough, you will need to copy the C string.
Cython detects and prevents some mistakes of this kind. For instance, if you
-attempt something like::
+attempt something like
+
+.. tabs::
+
+ .. group-tab:: Pure Python
- cdef char *s
- s = pystring1 + pystring2
+ .. code-block:: python
+
+ def main():
+ s: cython.p_char
+ s = pystring1 + pystring2
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef char *s
+ s = pystring1 + pystring2
then Cython will produce the error message ``Storing unsafe C derivative of temporary
Python reference``. The reason is that concatenating the two Python strings
@@ -562,11 +870,26 @@ leaving ``s`` dangling. Since this code could not possibly work, Cython refuses
compile it.
The solution is to assign the result of the concatenation to a Python
-variable, and then obtain the ``char*`` from that, i.e.::
+variable, and then obtain the ``char*`` from that, i.e.
+
+.. tabs::
- cdef char *s
- p = pystring1 + pystring2
- s = p
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ def main():
+ s: cython.p_char
+ p = pystring1 + pystring2
+ s = p
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef char *s
+ p = pystring1 + pystring2
+ s = p
It is then your responsibility to hold the reference p for as long as
necessary.
@@ -581,44 +904,93 @@ be careful what you do.
Type Casting
------------
-Where C uses ``"("`` and ``")"``, Cython uses ``"<"`` and ``">"``. For example::
+The Cython language supports type casting in a similar way as C. Where C uses ``"("`` and ``")"``,
+Cython uses ``"<"`` and ``">"``. In pure python mode, the ``cython.cast()`` function is used. For example:
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
+
+ def main():
+ p: cython.p_char
+ q: cython.p_float
+ p = cast(cython.p_char, q)
+
+ When casting a C value to a Python object type or vice versa,
+ Cython will attempt a coercion. Simple examples are casts like ``cast(int, pyobj_value)``,
+ which convert a Python number to a plain C ``int`` value, or the statement ``cast(bytes, charptr_value)``,
+ which copies a C ``char*`` string into a new Python bytes object.
+
+ .. note:: Cython will not prevent a redundant cast, but emits a warning for it.
- cdef char *p
- cdef float *q
- p = <char*>q
+ To get the address of some Python object, use a cast to a pointer type
+ like ``cast(p_void, ...)`` or ``cast(pointer(PyObject), ...)``.
+ You can also cast a C pointer back to a Python object reference
+ with ``cast(object, ...)``, or to a more specific builtin or extension type
+ (e.g. ``cast(MyExtType, ptr)``). This will increase the reference count of
+ the object by one, i.e. the cast returns an owned reference.
+ Here is an example:
-When casting a C value to a Python object type or vice versa,
-Cython will attempt a coercion. Simple examples are casts like ``<int>pyobj``,
-which converts a Python number to a plain C ``int`` value, or ``<bytes>charptr``,
-which copies a C ``char*`` string into a new Python bytes object.
- .. note:: Cython will not prevent a redundant cast, but emits a warning for it.
+ .. group-tab:: Cython
-To get the address of some Python object, use a cast to a pointer type
-like ``<void*>`` or ``<PyObject*>``.
-You can also cast a C pointer back to a Python object reference
-with ``<object>``, or a more specific builtin or extension type
-(e.g. ``<MyExtType>ptr``). This will increase the reference count of
-the object by one, i.e. the cast returns an owned reference.
-Here is an example:
+ .. code-block:: cython
-.. literalinclude:: ../../examples/userguide/language_basics/casting_python.pyx
+ cdef char *p
+ cdef float *q
+ p = <char*>q
-The precedence of ``<...>`` is such that ``<type>a.b.c`` is interpreted as ``<type>(a.b.c)``.
+ When casting a C value to a Python object type or vice versa,
+ Cython will attempt a coercion. Simple examples are casts like ``<int>pyobj_value``,
+ which convert a Python number to a plain C ``int`` value, or the statement ``<bytes>charptr_value``,
+ which copies a C ``char*`` string into a new Python bytes object.
-Casting to ``<object>`` creates an owned reference. Cython will automatically
-perform a ``Py_INCREF`` and ``Py_DECREF`` operation. Casting to
-``<PyObject *>`` creates a borrowed reference, leaving the refcount unchanged.
+ .. note:: Cython will not prevent a redundant cast, but emits a warning for it.
+
+ To get the address of some Python object, use a cast to a pointer type
+ like ``<void*>`` or ``<PyObject*>``.
+ You can also cast a C pointer back to a Python object reference
+ with ``<object>``, or to a more specific builtin or extension type
+ (e.g. ``<MyExtType>ptr``). This will increase the reference count of
+ the object by one, i.e. the cast returns an owned reference.
+ Here is an example:
+
+.. tabs::
+
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/userguide/language_basics/casting_python.pxd
+ :caption: casting_python.pxd
+ .. literalinclude:: ../../examples/userguide/language_basics/casting_python.py
+ :caption: casting_python.py
+
+ Casting with ``cast(object, ...)`` creates an owned reference. Cython will automatically
+ perform a ``Py_INCREF`` and ``Py_DECREF`` operation. Casting to
+ ``cast(pointer(PyObject), ...)`` creates a borrowed reference, leaving the refcount unchanged.
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/userguide/language_basics/casting_python.pyx
+ :caption: casting_python.pyx
+
+ The precedence of ``<...>`` is such that ``<type>a.b.c`` is interpreted as ``<type>(a.b.c)``.
+
+ Casting to ``<object>`` creates an owned reference. Cython will automatically
+ perform a ``Py_INCREF`` and ``Py_DECREF`` operation. Casting to
+ ``<PyObject *>`` creates a borrowed reference, leaving the refcount unchanged.
.. _checked_type_casts:
Checked Type Casts
------------------
-A cast like ``<MyExtensionType>x`` will cast x to the class
+A cast like ``<MyExtensionType>x`` or ``cast(MyExtensionType, x)`` will cast ``x`` to the class
``MyExtensionType`` without any checking at all.
-To have a cast checked, use the syntax like: ``<MyExtensionType?>x``.
+To have a cast checked, use ``<MyExtensionType?>x`` in Cython syntax
+or ``cast(MyExtensionType, x, typecheck=True)``.
In this case, Cython will apply a runtime check that raises a ``TypeError``
if ``x`` is not an instance of ``MyExtensionType``.
This tests for the exact class for builtin types,
@@ -650,17 +1022,37 @@ direct equivalent in Python.
* An integer literal is treated as a C constant, and will
be truncated to whatever size your C compiler thinks appropriate.
- To get a Python integer (of arbitrary precision) cast immediately to
- an object (e.g. ``<object>100000000000000000000``). The ``L``, ``LL``,
- and ``U`` suffixes have the same meaning as in C.
+ To get a Python integer (of arbitrary precision), cast immediately to
+ an object (e.g. ``<object>100000000000000000000`` or ``cast(object, 100000000000000000000)``). The ``L``, ``LL``,
+ and ``U`` suffixes have the same meaning in Cython syntax as in C.
* There is no ``->`` operator in Cython. Instead of ``p->x``, use ``p.x``
* There is no unary ``*`` operator in Cython. Instead of ``*p``, use ``p[0]``
-* There is an ``&`` operator, with the same semantics as in C.
-* The null C pointer is called ``NULL``, not ``0`` (and ``NULL`` is a reserved word).
-* Type casts are written ``<type>value`` , for example,::
+* 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.
+* Type casts are written ``<type>value`` or ``cast(type, value)``, for example,
+
+ .. tabs::
+
+ .. group-tab:: Pure Python
- cdef char* p, float* q
- p = <char*>q
+ .. code-block:: python
+
+ def main():
+ p: cython.p_char
+ q: cython.p_float
+
+ p = cast(cython.p_char, q)
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ cdef char* p
+ cdef float* q
+
+ p = <char*>q
Scope rules
-----------
@@ -742,6 +1134,8 @@ Python and C, and that Cython uses the Python precedences, not the C ones.
Integer for-loops
------------------
+.. note:: This syntax is supported only in Cython files. Use a normal `for-in-range()` loop instead.
+
Cython recognises the usual Python for-in-range integer loop pattern::
for i in range(n):
@@ -890,6 +1284,11 @@ Conditional Compilation
Some features are available for conditional compilation and compile-time
constants within a Cython source file.
+.. note::
+
+ Cython currently does not support conditional compilation and compile-time
+ definitions in Pure Python mode.
+
Compile-Time Definitions
------------------------