Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cython
Commits
4a8e481a
Commit
4a8e481a
authored
Sep 25, 2018
by
mattip
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
DOC, TST: test and document external extension type attribute aliasing
parent
9f286906
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
176 additions
and
0 deletions
+176
-0
docs/src/userguide/extension_types.rst
docs/src/userguide/extension_types.rst
+66
-0
docs/src/userguide/external_C_code.rst
docs/src/userguide/external_C_code.rst
+1
-0
tests/run/ext_attr_getter.srctree
tests/run/ext_attr_getter.srctree
+109
-0
No files found.
docs/src/userguide/extension_types.rst
View file @
4a8e481a
...
...
@@ -771,6 +771,72 @@ 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.
Attribute name matching and aliasing
------------------------------------
Sometimes the type's C struct as specified in ``object_struct_name`` may use
different labels for the fields. For example we may have an extension module
``foo_extension``::
cdef class Foo:
cdef public int field0, field1, field2;
def __init__(self, f0, f1, f2):
self.field0 = f0
self.field1 = f1
self.field2 = f2
but a C struct in a file ``foo_nominal.h``::
typedef struct {
PyObject_HEAD
int f0;
int f1;
int f2;
} FooStructNominal;
Note that the struct uses ``f0``, ``f1``, ``f2`` but they are ``field0``,
``field1``, and ``field2`` in ``Foo``. We are given this situation, including
a header file with that struct, and we wish to write a function to sum the
values. If we write an extension module ``wrapper``::
cdef extern from "foo_nominal.h":
ctypedef class foo_extension.Foo [object FooStructNominal]:
cdef:
int field0
int field1
int feild2
def sum(Foo f):
return f.field0 + f.field1 + f.field2
then ``wrapper.sum(f)`` (where ``f = foo_extension.Foo(1, 2, 3)``) will still
use the C-API equivalent of::
return f.__getattr__('field0') +
f.__getattr__('field1') +
f.__getattr__('field1')
instead of the desired C equivalent of ``return f->f0 + f->f1 + f->f2``. We can
alias the fields by using::
cdef extern from "foo_nominal.h":
ctypedef class foo_extension.Foo [object FooStructNominal]:
cdef:
int field0 "f0"
int field1 "f1"
int field2 "f2"
def sum(Foo f) except -1:
return f.field0 + f.field1 + f.field2
and now Cython will replace the slow ``__getattr__`` with direct C access to
the FooStructNominal fields. This is useful when directly processing Python
code. No changes to Python need be made to achieve significant speedups, even
though the field names in Python and C are different. Of course, one should
make sure the fields are equivalent.
Implicit importing
------------------
...
...
docs/src/userguide/external_C_code.rst
View file @
4a8e481a
...
...
@@ -219,6 +219,7 @@ same applies equally to union and enum declarations.
| } Foo; | | |
+-------------------------+---------------------------------------------+-----------------------------------------------------------------------+
See also use of :ref:`external_extension_types`.
Note that in all the cases below, you refer to the type in Cython code simply
as :c:type:`Foo`, not ``struct Foo``.
...
...
tests/run/ext_attr_getter.srctree
0 → 100644
View file @
4a8e481a
PYTHON setup.py build_ext --inplace
PYTHON -c "import runner"
######## setup.py ########
from Cython.Build.Dependencies import cythonize
from distutils.core import setup
# force the build order
setup(ext_modules= cythonize("foo_extension.pyx"))
setup(ext_modules = cythonize("getter*.pyx"))
######## foo_nominal.h ########
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
PyObject_HEAD
int f0;
int f1;
int f2;
} FooStructNominal;
#ifdef __cplusplus
}
#endif
######## foo_extension.pyx ########
cdef class Foo:
cdef public int field0, field1, field2;
def __init__(self, f0, f1, f2):
self.field0 = f0
self.field1 = f1
self.field2 = f2
cdef get_field0(Foo f):
return f.field0
cdef get_field1(Foo f):
return f.field1
cdef get_field2(Foo f):
return f.field2
# A pure-python class that disallows direct access to fields
class OpaqueFoo(Foo):
@property
def field0(self):
raise AttributeError('no direct access to field0')
@property
def field1(self):
raise AttributeError('no direct access to field1')
@property
def field2(self):
raise AttributeError('no direct access to field2')
######## getter0.pyx ########
# Access base Foo fields from C via aliased field names
cdef extern from "foo_nominal.h":
ctypedef class foo_extension.Foo [object FooStructNominal]:
cdef:
int field0 "f0"
int field1 "f1"
int field2 "f2"
def sum(Foo f):
# the f.__getattr__('field0') is replaced in c by f->f0
return f.field0 + f.field1 + f.field2
######## runner.py ########
import foo_extension, getter0
foo = foo_extension.Foo(23, 123, 1023)
assert foo.field0 == 23
assert foo.field1 == 123
assert foo.field2 == 1023
ret = getter0.sum(foo)
assert ret == foo.field0 + foo.field1 + foo.field2
opaque_foo = foo_extension.OpaqueFoo(23, 123, 1023)
# C can access the fields through the aliases
opaque_ret = getter0.sum(opaque_foo)
assert opaque_ret == ret
try:
# Python cannot access the fields
f0 = opaque_ret.field0
assert False
except AttributeError as e:
pass
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment