1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
Cython
General concept of how the wrapping is done:
Step 1: The author declares which classes and which functions of these
classes s/he wants to wrap (expose to Python). This is done by writing the
function declaration in a file in the pxds/ folder.
Step 2: The Python tool "autowrap" (developed for this project) creates the
wrapping code automatically from the function declaration - see
https://github.com/uweschmitt/autowrap for an explanation of the autowrap
tool. Since not all code can be wrapped automatically, also manual code can
be written in the addons/ folder. Autowrap will create an output file at
pyopenms/pyopenms.pyx
Step 3: Cython translates the pyopenms/pyopenms.pyx to Cpp code at
pyopenms/pyopenms.cpp
Step 4: A compiler compiles the Cpp code to a Python module which is then
importable in Python with "import pyopenms"
What if you changed an API in C++ and now pyOpenMS is broken?
=> find the corresponding file in the pxds/ folder and adjust the function
declaration accordingly
How to wrap new classes:
To wrap a new OpenMS class: Create a new "pxd" file in the folder "./pxds". As
a small example, look at the CVTerm.pxd to get you started. Start with the
following structure:
from xxx cimport *
cdef extern from "<OpenMS/path/to/header.h>" namespace "OpenMS":
cdef cppclass ClassName(DefaultParamHandler):
# wrap-inherits:
# DefaultParamHandler
ClassName() nogil except +
ClassName(ClassName) nogil except +
- make sure to use "ClassName:" instead of "ClassName(DefaultParamHandler)" to
wrap a class that does not inherit from another class and also remove the two
comments below that line.
- always use "cimport" and not Python "import"
- always add default constructor AND copy constructor to the code (note that the C++
compiler will add a default copy constructur to any class, so there is always
one if it is not declared, see http://www.cplusplus.com/articles/y8hv0pDG/ "The
implicit copy constructor does a member-wise copy of the source object.")
- to expose a function to Python, copy the signature to your pxd file, e.g.
"DataValue getValue()" and make sure you "cimport" all corresponding classes.
Replace std::vector with vector from libcpp.vector (see for example PepXMLFile.pxd)
Possible Error messages:
item0.inst = shared_ptr[_XXX](new _XXX(deref(it_terms)))
pyopenms/pyopenms.pyx:7277:55: Call with wrong number of arguments (expected 0, got 1)
means you forgot to add the copy constructor in the class XXX and it is expected somewhere else => go fix it
- inheriting: some limitations, see Precursor.pxd
- arguments by reference may be copied under some circumstances. For example,
if they are in an array then not the original argument is handed back, so
comparisons might fail.
- operator+=: see AASequence.iadd in AASequence.pxd
- operator==, !=, <=, <, >=, > are wrapped automatically
- limitations: iterator, reference
- copy-constructor -> __copy__
= Hints to autowrap
- wrap-ignore: is a hint for autowrap to not wrap the function (but the declaration might still be important for Cython to know about)
- wrap-as - see for example AASequence ":"
- wrap-iter-begin, wrap-iter-end (see ConsensusMap.pxd)
- wrap-instances - for templated classes (see MSSpectrum.pxd)
- wrap-attach: enums, static methods (see for example VersionInfo.pxd)
- wrap-upper-limit:size or size() (see MSSpectrum.pxd)
|