From 694361d1d058c8f9559cb29e1696b1554d5fc960 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Sun, 25 Jan 2009 01:07:18 -0800 Subject: [PATCH] extension types and special methods --- docs/extension_types.rst | 105 ++++++++++++++++++++++++++------------- docs/special_methods.rst | 49 +++++++++--------- 2 files changed, 97 insertions(+), 57 deletions(-) diff --git a/docs/extension_types.rst b/docs/extension_types.rst index 5ac7f75c..8ecea3ef 100644 --- a/docs/extension_types.rst +++ b/docs/extension_types.rst @@ -65,17 +65,14 @@ and the depth attribute readable but not writable. .. note:: - You can only expose simple C types, such as ints, floats and - strings, for Python access. You can also expose Python-valued attributes, - although read-write exposure is only possible for generic Python attributes - (of type object). If the attribute is declared to be of an extension type, it - must be exposed :keyword:`readonly`. + You can only expose simple C types, such as ints, floats, and + strings, for Python access. You can also expose Python-valued attributes. .. note:: Also the :keyword:`public` and :keyword:`readonly` options apply only to Python access, not direct access. All the attributes of an extension type - are always readable and writable by direct access. + are always readable and writable by C-level access. Type declarations =================== @@ -114,6 +111,45 @@ The same consideration applies to local variables, for example,:: sh2.height = sh1.height return sh2 + +Type Testing and Casting +------------------------ + +Suppose I have a method :meth:`quest` which returns an object of type :class:`Shrubbery`. +To access it's width I could write:: + + cdef Shrubbery sh = quest() + print sh.width + +which requires the use of a local variable and performs a type test on assignment. +If you *know* the return value of :meth:`quest` will be of type :class:`Shrubbery` +you can use a cast to write:: + + print (quest()).width + +This may be dangerous if :meth:`quest()` is not actually a :class:`Shrubbery`, as it +will try to access width as a C struct member which may not exist. At the C level, +rather than raising an :class:`AttributeError`, either an nonsensical result will be +returned (interpreting whatever data is at at that address as an int) or a segfault +may result from trying to access invalid memory. Instead, one can write:: + + print (quest()).width + +which performs a type check (possibly raising a :class:`TypeError`) before making the +cast and allowing the code to proceed. + +To explicitly test the type of an object, use the :meth:`isinstance` method. By default, +in Python, the :meth:`isinstance` method checks the :class:`__class__` attribute of the +first argument to determine if it is of the required type. However, this is potentially +unsafe as the :class:`__class__` attribute can be spoofed or changed, but the C structure +of an extension type must be correct to access its :keyword:`cdef` attributes and call its :keyword:`cdef` methods. Cython detects if the second argument is a known extension +type and does a type check instead, analogous to Pyrex's :meth:`typecheck`. +The old behavior is always available by passing a tuple as the second parameter:: + + print isinstance(sh, Shrubbery) # Check the type of sh + print isinstance(sh, (Shrubbery,)) # Check sh.__class__ + + Extension types and None ========================= @@ -178,7 +214,7 @@ Special methods Although the principles are similar, there are substantial differences between many of the :meth:`__xxx__` special methods of extension types and their Python -counterparts. There is a separate page devoted to this subject, and you should +counterparts. There is a :ref:`separate page ` devoted to this subject, and you should read it carefully before attempting to use any special methods in your extension types. @@ -281,7 +317,7 @@ must be compatible). C methods ========= Extension types can have C methods as well as Python methods. Like C -functions, C methods are declared using :keyword:`cdef` instead of +functions, C methods are declared using :keyword:`cdef` or :keyword:`cpdef` instead of :keyword:`def`. C methods are "virtual", and may be overridden in derived extension types.:: @@ -366,14 +402,14 @@ Cython module. A public extension type declaration makes an extension type defined in a Cython module available to external C code. External extension types -^^^^^^^^^^^^^^^^^^^^^^^^ +------------------------ An extern extension type allows you to gain access to the internals of Python objects defined in the Python core or in a non-Cython extension module. .. note:: - In Pyrex versions before 0.8, extern extension types were also used to + In previous versions of Pyrex, extern extension types were also used to reference extension types defined in another Pyrex module. While you can still do that, Cython provides a better mechanism for this. See :ref:`sharing-declarations`. @@ -418,8 +454,30 @@ built-in complex object.:: declaration is inside a :keyword:`cdef` extern from block, you only need to declare those C members which you wish to access. +Name specification clause +------------------------- + +The part of the class declaration in square brackets is a special feature only +available for extern or public extension types. The full form of this clause +is:: + + [object object_struct_name, type type_object_name ] + +where ``object_struct_name`` is the name to assume for the type's C struct, +and type_object_name is the name to assume for the type's statically declared +type object. (The object and type clauses can be written in either order.) + +If the extension type declaration is inside a :keyword:`cdef` extern from +block, the object clause is required, because Cython must be able to generate +code that is compatible with the declarations in the header file. Otherwise, +for extern extension types, the object clause is optional. + +For public extension types, the object and type clauses are both required, +because Cython must be able to generate code that is compatible with external C +code. + Implicit importing -^^^^^^^^^^^^^^^^^^ +------------------ Cython requires you to include a module name in an extern extension class declaration, for example,:: @@ -452,7 +510,7 @@ which corresponds to the implicit import statement:: from My.Nested.Package import Spam as Yummy Type names vs. constructor names -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +-------------------------------- Inside a Cython module, the name of an extension type serves two distinct purposes. When used in an expression, it refers to a module-level global @@ -482,33 +540,12 @@ there are other ways that you could get hold of the constructor, but only Yummy is usable as a type name. Public extension types -^^^^^^^^^^^^^^^^^^^^^^ +====================== An extension type can be declared public, in which case a ``.h`` file is generated containing declarations for its object struct and type object. By including the ``.h`` file in external C code that you write, that code can access the attributes of the extension type. -Name specification clause -^^^^^^^^^^^^^^^^^^^^^^^^^ - -The part of the class declaration in square brackets is a special feature only -available for extern or public extension types. The full form of this clause -is:: - - [object object_struct_name, type type_object_name ] - -where ``object_struct_name`` is the name to assume for the type's C struct, -and type_object_name is the name to assume for the type's statically declared -type object. (The object and type clauses can be written in either order.) - -If the extension type declaration is inside a :keyword:`cdef` extern from -block, the object clause is required, because Cython must be able to generate -code that is compatible with the declarations in the header file. Otherwise, -for extern extension types, the object clause is optional. - -For public extension types, the object and type clauses are both required, -because Cython must be able to generate code that is compatible with external C -code. diff --git a/docs/special_methods.rst b/docs/special_methods.rst index d1866e25..1cd20b52 100644 --- a/docs/special_methods.rst +++ b/docs/special_methods.rst @@ -1,3 +1,5 @@ +.. _special-methods: + Special Methods of Extension Types =================================== @@ -14,16 +16,17 @@ mention. Declaration ------------ Special methods of extension types must be declared with :keyword:`def`, not -:keyword:`cdef`. +:keyword:`cdef`. This does not impact their performance--Python uses different +calling conventions to invoke these special methods. Docstrings ----------- -Currently, docstrings are not fully supported in special methods of extension +Currently, docstrings are not fully supported in some special methods of extension types. You can place a docstring in the source to serve as a comment, but it -won't show up in the corresponding :attr:`__doc__` attribute at run time. (This is a -Python limitation -- there's nowhere in the `PyTypeObject` data structure to put -such docstrings.) +won't show up in the corresponding :attr:`__doc__` attribute at run time. (This +seems to be is a Python limitation -- there's nowhere in the `PyTypeObject` +data structure to put such docstrings.) Initialisation methods: :meth:`__cinit__` and :meth:`__init__` --------------------------------------------------------------- @@ -32,10 +35,9 @@ There are two methods concerned with initialising the object. The :meth:`__cinit__` method is where you should perform basic C-level initialisation of the object, including allocation of any C data structures that your object will own. You need to be careful what you do in the -:meth:`__cinit__` method, because the object may not yet be a valid Python -object when it is called. Therefore, you must not invoke any Python operations -which might touch the object; in particular, do not try to call any of its -methods. +:meth:`__cinit__` method, because the object may not yet be fully valid Python +object when it is called. Therefore, you should be careful invoking any Python +operations which might touch the object; in particular, its methods. By the time your :meth:`__cinit__` method is called, memory has been allocated for the object and any C attributes it has have been initialised to 0 or null. (Any @@ -64,11 +66,18 @@ subclassing your extension type in Python, you may find it useful to give the ignore extra arguments. Otherwise, any Python subclass which has an :meth:`__init__` with a different signature will have to override :meth:`__new__` as well as :meth:`__init__`, which the writer of a Python -class wouldn't expect to have to do. Finalization method: :meth:`__dealloc__` +class wouldn't expect to have to do. As a convenience, if you declare +your :meth:`__cinit__`` method to take no arguments (other than self) it +will simply ignore any extra arguments passed to the constructor without +complaining about the signature mismatch. + +Finalization method: :meth:`__dealloc__` +---------------------------------------- + The counterpart to the :meth:`__cinit__` method is the :meth:`__dealloc__` method, which should perform the inverse of the :meth:`__cinit__` method. Any -C data structures that you allocated in your :meth:`__cinit__` method should -be freed in your :meth:`__dealloc__` method. +C data that you explicitly allocated (e.g. via malloc) in your +:meth:`__cinit__` method should be freed in your :meth:`__dealloc__` method. You need to be careful what you do in a :meth:`__dealloc__` method. By the time your :meth:`__dealloc__` method is called, the object may already have been partially @@ -80,7 +89,7 @@ deallocating C data. You don't need to worry about deallocating Python attributes of your object, because that will be done for you by Cython after your :meth:`__dealloc__` method -returns. +returns. .. Note: There is no :meth:`__del__` method for extension types. @@ -94,9 +103,9 @@ operation, the same method of the second operand is called, with the operands in the same order. This means that you can't rely on the first parameter of these methods being -"self", and you should test the types of both operands before deciding what to -do. If you can't handle the combination of types you've been given, you should -return `NotImplemented`. +"self" or being the right type, and you should test the types of both operands +before deciding what to do. If you can't handle the combination of types you've +been given, you should return `NotImplemented`. This also applies to the in-place arithmetic method :meth:`__ipow__`. It doesn't apply to any of the other in-place methods (:meth:`__iadd__`, etc.) which always @@ -130,13 +139,7 @@ The :meth:`__next__` method Extension types wishing to implement the iterator interface should define a method called :meth:`__next__`, not next. The Python system will automatically supply a next method which calls your :meth:`__next__`. Do *NOT* explicitly -give your type a next method, or bad things could happen. - -Type Testing in Special Methods --------------------------------- - -.. TODO document the Cython way using the overridden isinstance - +give your type a :meth:`next` method, or bad things could happen. Special Method Table --------------------- -- 2.26.2