diff options
author | rgommers <ralf.gommers@googlemail.com> | 2010-09-04 09:52:24 +0000 |
---|---|---|
committer | rgommers <ralf.gommers@googlemail.com> | 2010-09-04 09:52:24 +0000 |
commit | 72665ab87449b9896dcb2db9aae36bb12c3daba7 (patch) | |
tree | d41998e6b68478188e49a8cd40252fae58e55e56 /doc/swig | |
parent | 9752b73d34f5e132d8e184d01e567ae693d204fc (diff) | |
download | numpy-72665ab87449b9896dcb2db9aae36bb12c3daba7.tar.gz |
DOC: integrate doc/swig/doc documentation with reference guide.
Diffstat (limited to 'doc/swig')
-rw-r--r-- | doc/swig/doc/numpy_swig.txt | 950 | ||||
-rw-r--r-- | doc/swig/doc/testing.txt | 173 |
2 files changed, 0 insertions, 1123 deletions
diff --git a/doc/swig/doc/numpy_swig.txt b/doc/swig/doc/numpy_swig.txt deleted file mode 100644 index bfde018bf..000000000 --- a/doc/swig/doc/numpy_swig.txt +++ /dev/null @@ -1,950 +0,0 @@ -========================================== - numpy.i: a SWIG Interface File for NumPy -========================================== - -:Author: Bill Spotz -:Institution: Sandia National Laboratories -:Date: 1 December, 2007 - -.. contents:: - -Introduction -============ - -The Simple Wrapper and Interface Generator (or `SWIG -<http://www.swig.org>`_) is a powerful tool for generating wrapper -code for interfacing to a wide variety of scripting languages. -`SWIG`_ can parse header files, and using only the code prototypes, -create an interface to the target language. But `SWIG`_ is not -omnipotent. For example, it cannot know from the prototype:: - - double rms(double* seq, int n); - -what exactly ``seq`` is. Is it a single value to be altered in-place? -Is it an array, and if so what is its length? Is it input-only? -Output-only? Input-output? `SWIG`_ cannot determine these details, -and does not attempt to do so. - -If we designed ``rms``, we probably made it a routine that takes an -input-only array of length ``n`` of ``double`` values called ``seq`` -and returns the root mean square. The default behavior of `SWIG`_, -however, will be to create a wrapper function that compiles, but is -nearly impossible to use from the scripting language in the way the C -routine was intended. - -For `python <http://www.python.org>`_, the preferred way of handling -contiguous (or technically, *strided*) blocks of homogeneous data is -with the module `NumPy <http://numpy.scipy.org>`_, which provides full -object-oriented access to multidimensial arrays of data. Therefore, -the most logical `python`_ interface for the ``rms`` function would be -(including doc string):: - - def rms(seq): - """ - rms: return the root mean square of a sequence - rms(numpy.ndarray) -> double - rms(list) -> double - rms(tuple) -> double - """ - -where ``seq`` would be a `NumPy`_ array of ``double`` values, and its -length ``n`` would be extracted from ``seq`` internally before being -passed to the C routine. Even better, since `NumPy`_ supports -construction of arrays from arbitrary `python`_ sequences, ``seq`` -itself could be a nearly arbitrary sequence (so long as each element -can be converted to a ``double``) and the wrapper code would -internally convert it to a `NumPy`_ array before extracting its data -and length. - -`SWIG`_ allows these types of conversions to be defined via a -mechanism called typemaps. This document provides information on how -to use ``numpy.i``, a `SWIG`_ interface file that defines a series of -typemaps intended to make the type of array-related conversions -described above relatively simple to implement. For example, suppose -that the ``rms`` function prototype defined above was in a header file -named ``rms.h``. To obtain the `python`_ interface discussed above, -your `SWIG`_ interface file would need the following:: - - %{ - #define SWIG_FILE_WITH_INIT - #include "rms.h" - %} - - %include "numpy.i" - - %init %{ - import_array(); - %} - - %apply (double* IN_ARRAY1, int DIM1) {(double* seq, int n)}; - %include "rms.h" - -Typemaps are keyed off a list of one or more function arguments, -either by type or by type and name. We will refer to such lists as -*signatures*. One of the many typemaps defined by ``numpy.i`` is used -above and has the signature ``(double* IN_ARRAY1, int DIM1)``. The -argument names are intended to suggest that the ``double*`` argument -is an input array of one dimension and that the ``int`` represents -that dimension. This is precisely the pattern in the ``rms`` -prototype. - -Most likely, no actual prototypes to be wrapped will have the argument -names ``IN_ARRAY1`` and ``DIM1``. We use the ``%apply`` directive to -apply the typemap for one-dimensional input arrays of type ``double`` -to the actual prototype used by ``rms``. Using ``numpy.i`` -effectively, therefore, requires knowing what typemaps are available -and what they do. - -A `SWIG`_ interface file that includes the `SWIG`_ directives given -above will produce wrapper code that looks something like:: - - 1 PyObject *_wrap_rms(PyObject *args) { - 2 PyObject *resultobj = 0; - 3 double *arg1 = (double *) 0 ; - 4 int arg2 ; - 5 double result; - 6 PyArrayObject *array1 = NULL ; - 7 int is_new_object1 = 0 ; - 8 PyObject * obj0 = 0 ; - 9 - 10 if (!PyArg_ParseTuple(args,(char *)"O:rms",&obj0)) SWIG_fail; - 11 { - 12 array1 = obj_to_array_contiguous_allow_conversion( - 13 obj0, NPY_DOUBLE, &is_new_object1); - 14 npy_intp size[1] = { - 15 -1 - 16 }; - 17 if (!array1 || !require_dimensions(array1, 1) || - 18 !require_size(array1, size, 1)) SWIG_fail; - 19 arg1 = (double*) array1->data; - 20 arg2 = (int) array1->dimensions[0]; - 21 } - 22 result = (double)rms(arg1,arg2); - 23 resultobj = SWIG_From_double((double)(result)); - 24 { - 25 if (is_new_object1 && array1) Py_DECREF(array1); - 26 } - 27 return resultobj; - 28 fail: - 29 { - 30 if (is_new_object1 && array1) Py_DECREF(array1); - 31 } - 32 return NULL; - 33 } - -The typemaps from ``numpy.i`` are responsible for the following lines -of code: 12--20, 25 and 30. Line 10 parses the input to the ``rms`` -function. From the format string ``"O:rms"``, we can see that the -argument list is expected to be a single `python`_ object (specified -by the ``O`` before the colon) and whose pointer is stored in -``obj0``. A number of functions, supplied by ``numpy.i``, are called -to make and check the (possible) conversion from a generic `python`_ -object to a `NumPy`_ array. These functions are explained in the -section `Helper Functions`_, but hopefully their names are -self-explanatory. At line 12 we use ``obj0`` to construct a `NumPy`_ -array. At line 17, we check the validity of the result: that it is -non-null and that it has a single dimension of arbitrary length. Once -these states are verified, we extract the data buffer and length in -lines 19 and 20 so that we can call the underlying C function at line -22. Line 25 performs memory management for the case where we have -created a new array that is no longer needed. - -This code has a significant amount of error handling. Note the -``SWIG_fail`` is a macro for ``goto fail``, refering to the label at -line 28. If the user provides the wrong number of arguments, this -will be caught at line 10. If construction of the `NumPy`_ array -fails or produces an array with the wrong number of dimensions, these -errors are caught at line 17. And finally, if an error is detected, -memory is still managed correctly at line 30. - -Note that if the C function signature was in a different order:: - - double rms(int n, double* seq); - -that `SWIG`_ would not match the typemap signature given above with -the argument list for ``rms``. Fortunately, ``numpy.i`` has a set of -typemaps with the data pointer given last:: - - %apply (int DIM1, double* IN_ARRAY1) {(int n, double* seq)}; - -This simply has the effect of switching the definitions of ``arg1`` -and ``arg2`` in lines 3 and 4 of the generated code above, and their -assignments in lines 19 and 20. - -Using numpy.i -============= - -The ``numpy.i`` file is currently located in the ``numpy/docs/swig`` -sub-directory under the ``numpy`` installation directory. Typically, -you will want to copy it to the directory where you are developing -your wrappers. If it is ever adopted by `SWIG`_ developers, then it -will be installed in a standard place where `SWIG`_ can find it. - -A simple module that only uses a single `SWIG`_ interface file should -include the following:: - - %{ - #define SWIG_FILE_WITH_INIT - %} - %include "numpy.i" - %init %{ - import_array(); - %} - -Within a compiled `python`_ module, ``import_array()`` should only get -called once. This could be in a C/C++ file that you have written and -is linked to the module. If this is the case, then none of your -interface files should ``#define SWIG_FILE_WITH_INIT`` or call -``import_array()``. Or, this initialization call could be in a -wrapper file generated by `SWIG`_ from an interface file that has the -``%init`` block as above. If this is the case, and you have more than -one `SWIG`_ interface file, then only one interface file should -``#define SWIG_FILE_WITH_INIT`` and call ``import_array()``. - -Available Typemaps -================== - -The typemap directives provided by ``numpy.i`` for arrays of different -data types, say ``double`` and ``int``, and dimensions of different -types, say ``int`` or ``long``, are identical to one another except -for the C and `NumPy`_ type specifications. The typemaps are -therefore implemented (typically behind the scenes) via a macro:: - - %numpy_typemaps(DATA_TYPE, DATA_TYPECODE, DIM_TYPE) - -that can be invoked for appropriate ``(DATA_TYPE, DATA_TYPECODE, -DIM_TYPE)`` triplets. For example:: - - %numpy_typemaps(double, NPY_DOUBLE, int) - %numpy_typemaps(int, NPY_INT , int) - -The ``numpy.i`` interface file uses the ``%numpy_typemaps`` macro to -implement typemaps for the following C data types and ``int`` -dimension types: - - * ``signed char`` - * ``unsigned char`` - * ``short`` - * ``unsigned short`` - * ``int`` - * ``unsigned int`` - * ``long`` - * ``unsigned long`` - * ``long long`` - * ``unsigned long long`` - * ``float`` - * ``double`` - -In the following descriptions, we reference a generic ``DATA_TYPE``, which -could be any of the C data types listed above, and ``DIM_TYPE`` which -should be one of the many types of integers. - -The typemap signatures are largely differentiated on the name given to -the buffer pointer. Names with ``FARRAY`` are for FORTRAN-ordered -arrays, and names with ``ARRAY`` are for C-ordered (or 1D arrays). - -Input Arrays ------------- - -Input arrays are defined as arrays of data that are passed into a -routine but are not altered in-place or returned to the user. The -`python`_ input array is therefore allowed to be almost any `python`_ -sequence (such as a list) that can be converted to the requested type -of array. The input array signatures are - -1D: - - * ``( DATA_TYPE IN_ARRAY1[ANY] )`` - * ``( DATA_TYPE* IN_ARRAY1, int DIM1 )`` - * ``( int DIM1, DATA_TYPE* IN_ARRAY1 )`` - -2D: - - * ``( DATA_TYPE IN_ARRAY2[ANY][ANY] )`` - * ``( DATA_TYPE* IN_ARRAY2, int DIM1, int DIM2 )`` - * ``( int DIM1, int DIM2, DATA_TYPE* IN_ARRAY2 )`` - * ``( DATA_TYPE* IN_FARRAY2, int DIM1, int DIM2 )`` - * ``( int DIM1, int DIM2, DATA_TYPE* IN_FARRAY2 )`` - -3D: - - * ``( DATA_TYPE IN_ARRAY3[ANY][ANY][ANY] )`` - * ``( DATA_TYPE* IN_ARRAY3, int DIM1, int DIM2, int DIM3 )`` - * ``( int DIM1, int DIM2, int DIM3, DATA_TYPE* IN_ARRAY3 )`` - * ``( DATA_TYPE* IN_FARRAY3, int DIM1, int DIM2, int DIM3 )`` - * ``( int DIM1, int DIM2, int DIM3, DATA_TYPE* IN_FARRAY3 )`` - -The first signature listed, ``( DATA_TYPE IN_ARRAY[ANY] )`` is for -one-dimensional arrays with hard-coded dimensions. Likewise, -``( DATA_TYPE IN_ARRAY2[ANY][ANY] )`` is for two-dimensional arrays -with hard-coded dimensions, and similarly for three-dimensional. - -In-Place Arrays ---------------- - -In-place arrays are defined as arrays that are modified in-place. The -input values may or may not be used, but the values at the time the -function returns are significant. The provided `python`_ argument -must therefore be a `NumPy`_ array of the required type. The in-place -signatures are - -1D: - - * ``( DATA_TYPE INPLACE_ARRAY1[ANY] )`` - * ``( DATA_TYPE* INPLACE_ARRAY1, int DIM1 )`` - * ``( int DIM1, DATA_TYPE* INPLACE_ARRAY1 )`` - -2D: - - * ``( DATA_TYPE INPLACE_ARRAY2[ANY][ANY] )`` - * ``( DATA_TYPE* INPLACE_ARRAY2, int DIM1, int DIM2 )`` - * ``( int DIM1, int DIM2, DATA_TYPE* INPLACE_ARRAY2 )`` - * ``( DATA_TYPE* INPLACE_FARRAY2, int DIM1, int DIM2 )`` - * ``( int DIM1, int DIM2, DATA_TYPE* INPLACE_FARRAY2 )`` - -3D: - - * ``( DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY] )`` - * ``( DATA_TYPE* INPLACE_ARRAY3, int DIM1, int DIM2, int DIM3 )`` - * ``( int DIM1, int DIM2, int DIM3, DATA_TYPE* INPLACE_ARRAY3 )`` - * ``( DATA_TYPE* INPLACE_FARRAY3, int DIM1, int DIM2, int DIM3 )`` - * ``( int DIM1, int DIM2, int DIM3, DATA_TYPE* INPLACE_FARRAY3 )`` - -These typemaps now check to make sure that the ``INPLACE_ARRAY`` -arguments use native byte ordering. If not, an exception is raised. - -Argout Arrays -------------- - -Argout arrays are arrays that appear in the input arguments in C, but -are in fact output arrays. This pattern occurs often when there is -more than one output variable and the single return argument is -therefore not sufficient. In `python`_, the convential way to return -multiple arguments is to pack them into a sequence (tuple, list, etc.) -and return the sequence. This is what the argout typemaps do. If a -wrapped function that uses these argout typemaps has more than one -return argument, they are packed into a tuple or list, depending on -the version of `python`_. The `python`_ user does not pass these -arrays in, they simply get returned. For the case where a dimension -is specified, the python user must provide that dimension as an -argument. The argout signatures are - -1D: - - * ``( DATA_TYPE ARGOUT_ARRAY1[ANY] )`` - * ``( DATA_TYPE* ARGOUT_ARRAY1, int DIM1 )`` - * ``( int DIM1, DATA_TYPE* ARGOUT_ARRAY1 )`` - -2D: - - * ``( DATA_TYPE ARGOUT_ARRAY2[ANY][ANY] )`` - -3D: - - * ``( DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY] )`` - -These are typically used in situations where in C/C++, you would -allocate a(n) array(s) on the heap, and call the function to fill the -array(s) values. In `python`_, the arrays are allocated for you and -returned as new array objects. - -Note that we support ``DATA_TYPE*`` argout typemaps in 1D, but not 2D -or 3D. This is because of a quirk with the `SWIG`_ typemap syntax and -cannot be avoided. Note that for these types of 1D typemaps, the -`python`_ function will take a single argument representing ``DIM1``. - -Argoutview Arrays ------------------ - -Argoutview arrays are for when your C code provides you with a view of -its internal data and does not require any memory to be allocated by -the user. This can be dangerous. There is almost no way to guarantee -that the internal data from the C code will remain in existence for -the entire lifetime of the `NumPy`_ array that encapsulates it. If -the user destroys the object that provides the view of the data before -destroying the `NumPy`_ array, then using that array my result in bad -memory references or segmentation faults. Nevertheless, there are -situations, working with large data sets, where you simply have no -other choice. - -The C code to be wrapped for argoutview arrays are characterized by -pointers: pointers to the dimensions and double pointers to the data, -so that these values can be passed back to the user. The argoutview -typemap signatures are therefore - -1D: - - * ``( DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1 )`` - * ``( DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1 )`` - -2D: - - * ``( DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2 )`` - * ``( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2 )`` - * ``( DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2 )`` - * ``( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2 )`` - -3D: - - * ``( DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)`` - * ``( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)`` - * ``( DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)`` - * ``( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)`` - -Note that arrays with hard-coded dimensions are not supported. These -cannot follow the double pointer signatures of these typemaps. - -Output Arrays -------------- - -The ``numpy.i`` interface file does not support typemaps for output -arrays, for several reasons. First, C/C++ return arguments are -limited to a single value. This prevents obtaining dimension -information in a general way. Second, arrays with hard-coded lengths -are not permitted as return arguments. In other words:: - - double[3] newVector(double x, double y, double z); - -is not legal C/C++ syntax. Therefore, we cannot provide typemaps of -the form:: - - %typemap(out) (TYPE[ANY]); - -If you run into a situation where a function or method is returning a -pointer to an array, your best bet is to write your own version of the -function to be wrapped, either with ``%extend`` for the case of class -methods or ``%ignore`` and ``%rename`` for the case of functions. - -Other Common Types: bool ------------------------- - -Note that C++ type ``bool`` is not supported in the list in the -`Available Typemaps`_ section. NumPy bools are a single byte, while -the C++ ``bool`` is four bytes (at least on my system). Therefore:: - - %numpy_typemaps(bool, NPY_BOOL, int) - -will result in typemaps that will produce code that reference -improper data lengths. You can implement the following macro -expansion:: - - %numpy_typemaps(bool, NPY_UINT, int) - -to fix the data length problem, and `Input Arrays`_ will work fine, -but `In-Place Arrays`_ might fail type-checking. - -Other Common Types: complex ---------------------------- - -Typemap conversions for complex floating-point types is also not -supported automatically. This is because `python`_ and `NumPy`_ are -written in C, which does not have native complex types. Both -`python`_ and `NumPy`_ implement their own (essentially equivalent) -``struct`` definitions for complex variables:: - - /* Python */ - typedef struct {double real; double imag;} Py_complex; - - /* NumPy */ - typedef struct {float real, imag;} npy_cfloat; - typedef struct {double real, imag;} npy_cdouble; - -We could have implemented:: - - %numpy_typemaps(Py_complex , NPY_CDOUBLE, int) - %numpy_typemaps(npy_cfloat , NPY_CFLOAT , int) - %numpy_typemaps(npy_cdouble, NPY_CDOUBLE, int) - -which would have provided automatic type conversions for arrays of -type ``Py_complex``, ``npy_cfloat`` and ``npy_cdouble``. However, it -seemed unlikely that there would be any independent (non-`python`_, -non-`NumPy`_) application code that people would be using `SWIG`_ to -generate a `python`_ interface to, that also used these definitions -for complex types. More likely, these application codes will define -their own complex types, or in the case of C++, use ``std::complex``. -Assuming these data structures are compatible with `python`_ and -`NumPy`_ complex types, ``%numpy_typemap`` expansions as above (with -the user's complex type substituted for the first argument) should -work. - -NumPy Array Scalars and SWIG -============================ - -`SWIG`_ has sophisticated type checking for numerical types. For -example, if your C/C++ routine expects an integer as input, the code -generated by `SWIG`_ will check for both `python`_ integers and -`python`_ long integers, and raise an overflow error if the provided -`python`_ integer is too big to cast down to a C integer. With the -introduction of `NumPy`_ scalar arrays into your `python`_ code, you -might conceivably extract an integer from a `NumPy`_ array and attempt -to pass this to a `SWIG`_-wrapped C/C++ function that expects an -``int``, but the `SWIG`_ type checking will not recognize the `NumPy`_ -array scalar as an integer. (Often, this does in fact work -- it -depends on whether `NumPy`_ recognizes the integer type you are using -as inheriting from the `python`_ integer type on the platform you are -using. Sometimes, this means that code that works on a 32-bit machine -will fail on a 64-bit machine.) - -If you get a `python`_ error that looks like the following:: - - TypeError: in method 'MyClass_MyMethod', argument 2 of type 'int' - -and the argument you are passing is an integer extracted from a -`NumPy`_ array, then you have stumbled upon this problem. The -solution is to modify the `SWIG`_ type conversion system to accept -`Numpy`_ array scalars in addition to the standard integer types. -Fortunately, this capabilitiy has been provided for you. Simply copy -the file:: - - pyfragments.swg - -to the working build directory for you project, and this problem will -be fixed. It is suggested that you do this anyway, as it only -increases the capabilities of your `python`_ interface. - -Why is There a Second File? ---------------------------- - -The `SWIG`_ type checking and conversion system is a complicated -combination of C macros, `SWIG`_ macros, `SWIG`_ typemaps and `SWIG`_ -fragments. Fragments are a way to conditionally insert code into your -wrapper file if it is needed, and not insert it if not needed. If -multiple typemaps require the same fragment, the fragment only gets -inserted into your wrapper code once. - -There is a fragment for converting a `python`_ integer to a C -``long``. There is a different fragment that converts a `python`_ -integer to a C ``int``, that calls the rountine defined in the -``long`` fragment. We can make the changes we want here by changing -the definition for the ``long`` fragment. `SWIG`_ determines the -active definition for a fragment using a "first come, first served" -system. That is, we need to define the fragment for ``long`` -conversions prior to `SWIG`_ doing it internally. `SWIG`_ allows us -to do this by putting our fragment definitions in the file -``pyfragments.swg``. If we were to put the new fragment definitions -in ``numpy.i``, they would be ignored. - -Helper Functions -================ - -The ``numpy.i`` file containes several macros and routines that it -uses internally to build its typemaps. However, these functions may -be useful elsewhere in your interface file. These macros and routines -are implemented as fragments, which are described briefly in the -previous section. If you try to use one or more of the following -macros or functions, but your compiler complains that it does not -recognize the symbol, then you need to force these fragments to appear -in your code using:: - - %fragment("NumPy_Fragments"); - -in your `SWIG`_ interface file. - -Macros ------- - - **is_array(a)** - Evaluates as true if ``a`` is non-``NULL`` and can be cast to a - ``PyArrayObject*``. - - **array_type(a)** - Evaluates to the integer data type code of ``a``, assuming ``a`` can - be cast to a ``PyArrayObject*``. - - **array_numdims(a)** - Evaluates to the integer number of dimensions of ``a``, assuming - ``a`` can be cast to a ``PyArrayObject*``. - - **array_dimensions(a)** - Evaluates to an array of type ``npy_intp`` and length - ``array_numdims(a)``, giving the lengths of all of the dimensions - of ``a``, assuming ``a`` can be cast to a ``PyArrayObject*``. - - **array_size(a,i)** - Evaluates to the ``i``-th dimension size of ``a``, assuming ``a`` - can be cast to a ``PyArrayObject*``. - - **array_data(a)** - Evaluates to a pointer of type ``void*`` that points to the data - buffer of ``a``, assuming ``a`` can be cast to a ``PyArrayObject*``. - - **array_is_contiguous(a)** - Evaluates as true if ``a`` is a contiguous array. Equivalent to - ``(PyArray_ISCONTIGUOUS(a))``. - - **array_is_native(a)** - Evaluates as true if the data buffer of ``a`` uses native byte - order. Equivalent to ``(PyArray_ISNOTSWAPPED(a))``. - - **array_is_fortran(a)** - Evaluates as true if ``a`` is FORTRAN ordered. - -Routines --------- - - **pytype_string()** - - Return type: ``char*`` - - Arguments: - - * ``PyObject* py_obj``, a general `python`_ object. - - Return a string describing the type of ``py_obj``. - - - **typecode_string()** - - Return type: ``char*`` - - Arguments: - - * ``int typecode``, a `NumPy`_ integer typecode. - - Return a string describing the type corresponding to the `NumPy`_ - ``typecode``. - - **type_match()** - - Return type: ``int`` - - Arguments: - - * ``int actual_type``, the `NumPy`_ typecode of a `NumPy`_ array. - - * ``int desired_type``, the desired `NumPy`_ typecode. - - Make sure that ``actual_type`` is compatible with - ``desired_type``. For example, this allows character and - byte types, or int and long types, to match. This is now - equivalent to ``PyArray_EquivTypenums()``. - - - **obj_to_array_no_conversion()** - - Return type: ``PyArrayObject*`` - - Arguments: - - * ``PyObject* input``, a general `python`_ object. - - * ``int typecode``, the desired `NumPy`_ typecode. - - Cast ``input`` to a ``PyArrayObject*`` if legal, and ensure that - it is of type ``typecode``. If ``input`` cannot be cast, or the - ``typecode`` is wrong, set a `python`_ error and return ``NULL``. - - - **obj_to_array_allow_conversion()** - - Return type: ``PyArrayObject*`` - - Arguments: - - * ``PyObject* input``, a general `python`_ object. - - * ``int typecode``, the desired `NumPy`_ typecode of the resulting - array. - - * ``int* is_new_object``, returns a value of 0 if no conversion - performed, else 1. - - Convert ``input`` to a `NumPy`_ array with the given ``typecode``. - On success, return a valid ``PyArrayObject*`` with the correct - type. On failure, the `python`_ error string will be set and the - routine returns ``NULL``. - - - **make_contiguous()** - - Return type: ``PyArrayObject*`` - - Arguments: - - * ``PyArrayObject* ary``, a `NumPy`_ array. - - * ``int* is_new_object``, returns a value of 0 if no conversion - performed, else 1. - - * ``int min_dims``, minimum allowable dimensions. - - * ``int max_dims``, maximum allowable dimensions. - - Check to see if ``ary`` is contiguous. If so, return the input - pointer and flag it as not a new object. If it is not contiguous, - create a new ``PyArrayObject*`` using the original data, flag it - as a new object and return the pointer. - - - **obj_to_array_contiguous_allow_conversion()** - - Return type: ``PyArrayObject*`` - - Arguments: - - * ``PyObject* input``, a general `python`_ object. - - * ``int typecode``, the desired `NumPy`_ typecode of the resulting - array. - - * ``int* is_new_object``, returns a value of 0 if no conversion - performed, else 1. - - Convert ``input`` to a contiguous ``PyArrayObject*`` of the - specified type. If the input object is not a contiguous - ``PyArrayObject*``, a new one will be created and the new object - flag will be set. - - - **require_contiguous()** - - Return type: ``int`` - - Arguments: - - * ``PyArrayObject* ary``, a `NumPy`_ array. - - Test whether ``ary`` is contiguous. If so, return 1. Otherwise, - set a `python`_ error and return 0. - - - **require_native()** - - Return type: ``int`` - - Arguments: - - * ``PyArray_Object* ary``, a `NumPy`_ array. - - Require that ``ary`` is not byte-swapped. If the array is not - byte-swapped, return 1. Otherwise, set a `python`_ error and - return 0. - - **require_dimensions()** - - Return type: ``int`` - - Arguments: - - * ``PyArrayObject* ary``, a `NumPy`_ array. - - * ``int exact_dimensions``, the desired number of dimensions. - - Require ``ary`` to have a specified number of dimensions. If the - array has the specified number of dimensions, return 1. - Otherwise, set a `python`_ error and return 0. - - - **require_dimensions_n()** - - Return type: ``int`` - - Arguments: - - * ``PyArrayObject* ary``, a `NumPy`_ array. - - * ``int* exact_dimensions``, an array of integers representing - acceptable numbers of dimensions. - - * ``int n``, the length of ``exact_dimensions``. - - Require ``ary`` to have one of a list of specified number of - dimensions. If the array has one of the specified number of - dimensions, return 1. Otherwise, set the `python`_ error string - and return 0. - - - **require_size()** - - Return type: ``int`` - - Arguments: - - * ``PyArrayObject* ary``, a `NumPy`_ array. - - * ``npy_int* size``, an array representing the desired lengths of - each dimension. - - * ``int n``, the length of ``size``. - - Require ``ary`` to have a specified shape. If the array has the - specified shape, return 1. Otherwise, set the `python`_ error - string and return 0. - - - **require_fortran()** - - Return type: ``int`` - - Arguments: - - * ``PyArrayObject* ary``, a `NumPy`_ array. - - Require the given ``PyArrayObject`` to to be FORTRAN ordered. If - the the ``PyArrayObject`` is already FORTRAN ordered, do nothing. - Else, set the FORTRAN ordering flag and recompute the strides. - - -Beyond the Provided Typemaps -============================ - -There are many C or C++ array/`NumPy`_ array situations not covered by -a simple ``%include "numpy.i"`` and subsequent ``%apply`` directives. - -A Common Example ----------------- - -Consider a reasonable prototype for a dot product function:: - - double dot(int len, double* vec1, double* vec2); - -The `python`_ interface that we want is:: - - def dot(vec1, vec2): - """ - dot(PyObject,PyObject) -> double - """ - -The problem here is that there is one dimension argument and two array -arguments, and our typemaps are set up for dimensions that apply to a -single array (in fact, `SWIG`_ does not provide a mechanism for -associating ``len`` with ``vec2`` that takes two `python`_ input -arguments). The recommended solution is the following:: - - %apply (int DIM1, double* IN_ARRAY1) {(int len1, double* vec1), - (int len2, double* vec2)} - %rename (dot) my_dot; - %exception my_dot { - $action - if (PyErr_Occurred()) SWIG_fail; - } - %inline %{ - double my_dot(int len1, double* vec1, int len2, double* vec2) { - if (len1 != len2) { - PyErr_Format(PyExc_ValueError, - "Arrays of lengths (%d,%d) given", - len1, len2); - return 0.0; - } - return dot(len1, vec1, vec2); - } - %} - -If the header file that contains the prototype for ``double dot()`` -also contains other prototypes that you want to wrap, so that you need -to ``%include`` this header file, then you will also need a ``%ignore -dot;`` directive, placed after the ``%rename`` and before the -``%include`` directives. Or, if the function in question is a class -method, you will want to use ``%extend`` rather than ``%inline`` in -addition to ``%ignore``. - -**A note on error handling:** Note that ``my_dot`` returns a -``double`` but that it can also raise a `python`_ error. The -resulting wrapper function will return a `python`_ float -representation of 0.0 when the vector lengths do not match. Since -this is not ``NULL``, the `python`_ interpreter will not know to check -for an error. For this reason, we add the ``%exception`` directive -above for ``my_dot`` to get the behavior we want (note that -``$action`` is a macro that gets expanded to a valid call to -``my_dot``). In general, you will probably want to write a `SWIG`_ -macro to perform this task. - -Other Situations ----------------- - -There are other wrapping situations in which ``numpy.i`` may be -helpful when you encounter them. - - * In some situations, it is possible that you could use the - ``%numpy_templates`` macro to implement typemaps for your own - types. See the `Other Common Types: bool`_ or `Other Common - Types: complex`_ sections for examples. Another situation is if - your dimensions are of a type other than ``int`` (say ``long`` for - example):: - - %numpy_typemaps(double, NPY_DOUBLE, long) - - * You can use the code in ``numpy.i`` to write your own typemaps. - For example, if you had a four-dimensional array as a function - argument, you could cut-and-paste the appropriate - three-dimensional typemaps into your interface file. The - modifications for the fourth dimension would be trivial. - - * Sometimes, the best approach is to use the ``%extend`` directive - to define new methods for your classes (or overload existing ones) - that take a ``PyObject*`` (that either is or can be converted to a - ``PyArrayObject*``) instead of a pointer to a buffer. In this - case, the helper routines in ``numpy.i`` can be very useful. - - * Writing typemaps can be a bit nonintuitive. If you have specific - questions about writing `SWIG`_ typemaps for `NumPy`_, the - developers of ``numpy.i`` do monitor the - `Numpy-discussion <mailto:Numpy-discussion@scipy.org>`_ and - `Swig-user <mailto:Swig-user@lists.sourceforge.net>`_ mail lists. - -A Final Note ------------- - -When you use the ``%apply`` directive, as is usually necessary to use -``numpy.i``, it will remain in effect until you tell `SWIG`_ that it -shouldn't be. If the arguments to the functions or methods that you -are wrapping have common names, such as ``length`` or ``vector``, -these typemaps may get applied in situations you do not expect or -want. Therefore, it is always a good idea to add a ``%clear`` -directive after you are done with a specific typemap:: - - %apply (double* IN_ARRAY1, int DIM1) {(double* vector, int length)} - %include "my_header.h" - %clear (double* vector, int length); - -In general, you should target these typemap signatures specifically -where you want them, and then clear them after you are done. - -Summary -======= - -Out of the box, ``numpy.i`` provides typemaps that support conversion -between `NumPy`_ arrays and C arrays: - - * That can be one of 12 different scalar types: ``signed char``, - ``unsigned char``, ``short``, ``unsigned short``, ``int``, - ``unsigned int``, ``long``, ``unsigned long``, ``long long``, - ``unsigned long long``, ``float`` and ``double``. - - * That support 41 different argument signatures for each data type, - including: - - + One-dimensional, two-dimensional and three-dimensional arrays. - - + Input-only, in-place, argout and argoutview behavior. - - + Hard-coded dimensions, data-buffer-then-dimensions - specification, and dimensions-then-data-buffer specification. - - + Both C-ordering ("last dimension fastest") or FORTRAN-ordering - ("first dimension fastest") support for 2D and 3D arrays. - -The ``numpy.i`` interface file also provides additional tools for -wrapper developers, including: - - * A `SWIG`_ macro (``%numpy_typemaps``) with three arguments for - implementing the 41 argument signatures for the user's choice of - (1) C data type, (2) `NumPy`_ data type (assuming they match), and - (3) dimension type. - - * Nine C macros and 13 C functions that can be used to write - specialized typemaps, extensions, or inlined functions that handle - cases not covered by the provided typemaps. - -Acknowledgements -================ - -Many people have worked to glue `SWIG`_ and `NumPy`_ together (as well -as `SWIG`_ and the predecessors of `NumPy`_, Numeric and numarray). -The effort to standardize this work into ``numpy.i`` began at the 2005 -`SciPy <http://scipy.org>`_ Conference with a conversation between -Fernando Perez and myself. Fernando collected helper functions and -typemaps from Eric Jones, Michael Hunter, Anna Omelchenko and Michael -Sanner. Sebastian Hasse and Georg Holzmann have also provided -additional error checking and use cases. The work of these -contributors has made this end result possible. diff --git a/doc/swig/doc/testing.txt b/doc/swig/doc/testing.txt deleted file mode 100644 index bfd5218e8..000000000 --- a/doc/swig/doc/testing.txt +++ /dev/null @@ -1,173 +0,0 @@ -============================ -Testing the numpy.i Typemaps -============================ - -:Author: Bill Spotz -:Institution: Sandia National Laboratories -:Date: 6 April, 2007 - -.. contents:: - -Introduction -============ - -Writing tests for the ``numpy.i`` `SWIG <http://www.swig.org>`_ -interface file is a combinatorial headache. At present, 12 different -data types are supported, each with 23 different argument signatures, -for a total of 276 typemaps supported "out of the box". Each of these -typemaps, in turn, might require several unit tests in order to verify -expected behavior for both proper and improper inputs. Currently, -this results in 1,020 individual unit tests that are performed when -``make test`` is run in the ``numpy/docs/swig`` subdirectory. - -To facilitate this many similar unit tests, some high-level -programming techniques are employed, including C and `SWIG`_ macros, -as well as `python <http://www.python.org>`_ inheritance. The -purpose of this document is to describe the testing infrastructure -employed to verify that the ``numpy.i`` typemaps are working as -expected. - -Testing Organization -==================== - -There are three indepedent testing frameworks supported, for one-, -two-, and three-dimensional arrays respectively. For one-dimensional -arrays, there are two C++ files, a header and a source, named:: - - Vector.h - Vector.cxx - -that contain prototypes and code for a variety of functions that have -one-dimensional arrays as function arguments. The file:: - - Vector.i - -is a `SWIG`_ interface file that defines a python module ``Vector`` -that wraps the functions in ``Vector.h`` while utilizing the typemaps -in ``numpy.i`` to correctly handle the C arrays. - -The ``Makefile`` calls ``swig`` to generate ``Vector.py`` and -``Vector_wrap.cxx``, and also executes the ``setup.py`` script that -compiles ``Vector_wrap.cxx`` and links together the extension module -``_Vector.so`` or ``_Vector.dylib``, depending on the platform. This -extension module and the proxy file ``Vector.py`` are both placed in a -subdirectory under the ``build`` directory. - -The actual testing takes place with a `python`_ script named:: - - testVector.py - -that uses the standard `python`_ library module ``unittest``, which -performs several tests of each function defined in ``Vector.h`` for -each data type supported. - -Two-dimensional arrays are tested in exactly the same manner. The -above description applies, but with ``Matrix`` substituted for -``Vector``. For three-dimensional tests, substitute ``Tensor`` for -``Vector``. For the descriptions that follow, we will reference the -``Vector`` tests, but the same information applies to ``Matrix`` and -``Tensor`` tests. - -The command ``make test`` will ensure that all of the test software is -built and then run all three test scripts. - -Testing Header Files -==================== - -``Vector.h`` is a C++ header file that defines a C macro called -``TEST_FUNC_PROTOS`` that takes two arguments: ``TYPE``, which is a -data type name such as ``unsigned int``; and ``SNAME``, which is a -short name for the same data type with no spaces, e.g. ``uint``. This -macro defines several function prototypes that have the prefix -``SNAME`` and have at least one argument that is an array of type -``TYPE``. Those functions that have return arguments return a -``TYPE`` value. - -``TEST_FUNC_PROTOS`` is then implemented for all of the data types -supported by ``numpy.i``: - - * ``signed char`` - * ``unsigned char`` - * ``short`` - * ``unsigned short`` - * ``int`` - * ``unsigned int`` - * ``long`` - * ``unsigned long`` - * ``long long`` - * ``unsigned long long`` - * ``float`` - * ``double`` - -Testing Source Files -==================== - -``Vector.cxx`` is a C++ source file that implements compilable code -for each of the function prototypes specified in ``Vector.h``. It -defines a C macro ``TEST_FUNCS`` that has the same arguments and works -in the same way as ``TEST_FUNC_PROTOS`` does in ``Vector.h``. -``TEST_FUNCS`` is implemented for each of the 12 data types as above. - -Testing SWIG Interface Files -============================ - -``Vector.i`` is a `SWIG`_ interface file that defines python module -``Vector``. It follows the conventions for using ``numpy.i`` as -described in the `numpy.i documentation <numpy_swig.html>`_. It -defines a `SWIG`_ macro ``%apply_numpy_typemaps`` that has a single -argument ``TYPE``. It uses the `SWIG`_ directive ``%apply`` as -described in the `numpy.i documentation`_ to apply the provided -typemaps to the argument signatures found in ``Vector.h``. This macro -is then implemented for all of the data types supported by -``numpy.i``. It then does a ``%include "Vector.h"`` to wrap all of -the function prototypes in ``Vector.h`` using the typemaps in -``numpy.i``. - -Testing Python Scripts -====================== - -After ``make`` is used to build the testing extension modules, -``testVector.py`` can be run to execute the tests. As with other -scripts that use ``unittest`` to facilitate unit testing, -``testVector.py`` defines a class that inherits from -``unittest.TestCase``:: - - class VectorTestCase(unittest.TestCase): - -However, this class is not run directly. Rather, it serves as a base -class to several other python classes, each one specific to a -particular data type. The ``VectorTestCase`` class stores two strings -for typing information: - - **self.typeStr** - A string that matches one of the ``SNAME`` prefixes used in - ``Vector.h`` and ``Vector.cxx``. For example, ``"double"``. - - **self.typeCode** - A short (typically single-character) string that represents a - data type in numpy and corresponds to ``self.typeStr``. For - example, if ``self.typeStr`` is ``"double"``, then - ``self.typeCode`` should be ``"d"``. - -Each test defined by the ``VectorTestCase`` class extracts the python -function it is trying to test by accessing the ``Vector`` module's -dictionary:: - - length = Vector.__dict__[self.typeStr + "Length"] - -In the case of double precision tests, this will return the python -function ``Vector.doubleLength``. - -We then define a new test case class for each supported data type with -a short definition such as:: - - class doubleTestCase(VectorTestCase): - def __init__(self, methodName="runTest"): - VectorTestCase.__init__(self, methodName) - self.typeStr = "double" - self.typeCode = "d" - -Each of these 12 classes is collected into a ``unittest.TestSuite``, -which is then executed. Errors and failures are summed together and -returned as the exit argument. Any non-zero result indicates that at -least one test did not pass. |