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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html >
<head><title>NOTE 259: pyrap binding to casacore</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="generator" content="TeX4ht (http://www.cse.ohio-state.edu/~gurari/TeX4ht/)">
<meta name="originator" content="TeX4ht (http://www.cse.ohio-state.edu/~gurari/TeX4ht/)">
<!-- html -->
<meta name="src" content="259.tex">
<meta name="date" content="2007-11-14 15:18:00">
<link rel="stylesheet" type="text/css" href="259.css">
</head><body
>
<div class="maketitle">
<h2 class="titleHead">NOTE 259: pyrap binding to casacore</h2>
<div class="author" ><span
class="cmr-12">Ger van Diepen, ASTRON Dwingeloo</span></div>
<br />
<div class="date" ><span
class="cmr-12">November 10, 2006</span></div>
</div><table
class="abstract"><tr><td
>
<div class="center"
>
<!--l. 12--><p class="noindent" >
<!--l. 12--><p class="noindent" ><span
class="cmbx-10">Abstract</span></div>
<!--l. 13--><p class="indent" > <span
class="cmr-10">pyrap is a Python binding to casacore classes using Boost.Python.</span>
<span
class="cmr-10">It consists of a set of standard converters and bindings to the classes.</span>
<span
class="cmr-10">As much as possible the bindings are the same as in glish.</span>
</td></tr></table>
<h3 class="likesectionHead"><a
id="x1-1000"></a>Contents</h3>
<div class="tableofcontents">
<span class="sectionToc" >1 <a
href="#x1-20001" id="QQ2-1-2">Introduction</a></span>
<br /> <span class="sectionToc" >2 <a
href="#x1-30002" id="QQ2-1-3">Converters</a></span>
<br />  <span class="subsectionToc" >2.1 <a
href="#x1-40002.1" id="QQ2-1-4">Array conversion to/from numpy and numarray</a></span>
<br /> <span class="sectionToc" >3 <a
href="#x1-50003" id="QQ2-1-5">Class wrappers</a></span>
<br />  <span class="subsectionToc" >3.1 <a
href="#x1-60003.1" id="QQ2-1-6">More complicated wrappers</a></span>
<br />  <span class="subsectionToc" >3.2 <a
href="#x1-70003.2" id="QQ2-1-7">Combining multiple classes</a></span>
<br /> <span class="sectionToc" >4 <a
href="#x1-80004" id="QQ2-1-8">Python specifics</a></span>
</div>
<h3 class="sectionHead"><span class="titlemark">1 </span> <a
id="x1-20001"></a>Introduction</h3>
<!--l. 2--><p class="noindent" >Since long glish bindings to the <a
href="http://casacore.googlecode.com" >casacore</a> system have been in place. Quite
recently Python bindings have been created in the general casapy framework
using tools like CCMTools, Xerces, Xalan, and IDL. Albeit very flexible, it is
quite complicated and it is not straightforward to build on other systems than
RedHat and OS-X.
<!--l. 8--><p class="indent" > Therefore an attempt has been made to make a simpler Python binding using
<a
href="http://www.boost.org/libs/python/doc" >Boost.Python</a>. This proved to be very easy and succesful. The binding consists of
two parts:
<ul class="itemize1">
<li class="itemize">Converters to translate objects between Python and C++.
</li>
<li class="itemize">Class wrappers to map a C++ class and its functions to Python.</li></ul>
<!--l. 16--><p class="noindent" >The Python numarray and numpy (version 1.0 or higher) packages are supported. At
build time one can choose which ones should be used.
<!--l. 19--><p class="noindent" >
<h3 class="sectionHead"><span class="titlemark">2 </span> <a
id="x1-30002"></a>Converters</h3>
<!--l. 20--><p class="noindent" >Boost.Python offers a nice way to convert objects to and from Python. Ralf W.
Grosse-Kunstleve <span
class="cmtt-10x-x-109"><rwgk@yahoo.com> </span>of <a
href="http://www.lbl.gov" >Lawrence Berkeley National Laboratory</a>
has built converters for standard STL containers. This has been extended to
convert to/from other objects.
<br class="newline" />The following C++ objects are currently supported:
<ul class="itemize1">
<li class="itemize">scalars (bool, integer, real, complex)
</li>
<li class="itemize"><span
class="cmtt-10x-x-109">std::string</span>
</li>
<li class="itemize"><span
class="cmtt-10x-x-109">casa::String</span>
</li>
<li class="itemize"><span
class="cmtt-10x-x-109">std::vector<T></span>
</li>
<li class="itemize"><span
class="cmtt-10x-x-109">casa::Vector<T></span>
</li>
<li class="itemize"><span
class="cmtt-10x-x-109">casa::IPosition</span>
</li>
<li class="itemize"><span
class="cmtt-10x-x-109">casa::Record</span>
</li>
<li class="itemize"><span
class="cmtt-10x-x-109">casa::ValueHolder</span>
</li>
<li class="itemize">exceptions (<span
class="cmtt-10x-x-109">casa::IterError </span>and <span
class="cmtt-10x-x-109">std::exception</span>)</li></ul>
<!--l. 37--><p class="noindent" >These C++ objects can usually be created from several types of Python objects and
are converted to a specific Python object.
<ul class="itemize1">
<li class="itemize">A vector or IPosition object is converted to a Python list.
<br class="newline" />It can be constructed from the following Python objects:
<ul class="itemize2">
<li class="itemize">scalar
</li>
<li class="itemize">list or tuple
</li>
<li class="itemize">numarray scalar or 1-dim array
</li>
<li class="itemize">numpy scalar or 1-dim array</li></ul>
<!--l. 49--><p class="noindent" >Note that a list or tuple of arbitrary objects can be given. For example, it is
possible to get a <span
class="cmtt-10x-x-109">Vector<TableProxy> </span>from Python.
</li>
<li class="itemize">A casa::Record is mapped to a Python dict.
</li>
<li class="itemize">Every C++ exception is mapped to a Python <span
class="cmtt-10x-x-109">RunTimeError </span>exception.
However, <span
class="cmtt-10x-x-109">casa::IterError </span>is special and is mapped to an end-of-iteration
exception (<span
class="cmtt-10x-x-109">StopIteration</span>) in Python.
</li>
<li class="itemize">A casa::ValueHolder is a special <a
href="http://casacore.googlecode.com" >casacore</a> object that can hold a record
or a scalar value or n-dim array of many types (bool, numeric,
string). It is meant to conceal the actual type which is useful in
functions that can accept a variety of types (like <span
class="cmtt-10x-x-109">getcell </span>in the table
binding).
<br class="newline" />Converting a ValueHolder to Python creates the appropriate Python scalar,
array, or dict object. When converting from Python to ValueHolder,
the appropriate internal ValueHolder value is constructed; a list,
tuple, and array object are converted to an <a
href="http://casacore.googlecode.com" >casacore</a> array in the
ValueHolder.</li></ul>
<!--l. 69--><p class="noindent" >It means there is no direct Array conversion to/from Python. A ValueHolder
object is always needed to do the conversion. Note that this is a cheap
operation, as it uses Array reference semantics. ValueHolder has functions to
convert between types, so one can get out an Array with the required
type.
<!--l. 75--><p class="noindent" >
<h4 class="subsectionHead"><span class="titlemark">2.1 </span> <a
id="x1-40002.1"></a>Array conversion to/from numpy and numarray</h4>
<!--l. 76--><p class="noindent" ><a
href="http://casacore.googlecode.com" >casacore</a> arrays are kept in Fortran-order, while Python arrays are kept in
C-order. It was felt that the Python interface should be as pythonic as possible.
Therefore it was decided that the array axes are reversed when converting
to/from Python. The values in an IPosition object (describing shape or position)
are also reversed when converting to/from Python.
<br class="newline" />Note that although numarray and numpy have Fortran-array provisions by
setting the appropriate internal strides, they do not really support them. When
adding, for instance, the scalar value 0 to a Fortran-array, the result
is a transposed version of the original (which can be a quite expensive
operation).
<!--l. 88--><p class="indent" > A function binding could be such that shape information is passed via, say, a
<span
class="cmtt-10x-x-109">Record </span>and not via an <span
class="cmtt-10x-x-109">IPosition </span>object. In that case its values are
not reversed automatically, so the programmer is responsible for doing
it.
<!--l. 94--><p class="indent" > An <a
href="http://casacore.googlecode.com" >casacore</a> array is returned to Python as an array object containing a copy
of the <a
href="http://casacore.googlecode.com" >casacore</a> array data. If pyrap has been built with support for only one
Python array package (numpy or numarray), it is clear which array type is
returned. If support for both packages has been built in, by default an array of
the imported package is returned. If both or no array packages have been
imported, a numpy array is returned.
<br class="newline" />Note that there is no support for the old Numeric package.
<!--l. 103--><p class="indent" > An <a
href="http://casacore.googlecode.com" >casacore</a> array constructed from a Python array is regarded as a
temporary object. So if possible, the <a
href="http://casacore.googlecode.com" >casacore</a> array refers to the Python array
data to avoid a needless copy. This is not possible if the element size in Python
differs from <a
href="http://casacore.googlecode.com" >casacore</a>. It is also not possible if the Python array is not
contiguous (or not aligned or byte swapped). In those cases a copy is
made.
<!--l. 110--><p class="indent" > A few more numarray/numpy specific issues are dealt with:
<ul class="itemize1">
<li class="itemize">An empty N-dim <a
href="http://casacore.googlecode.com" >casacore</a> array (i.e. an array containing no elements)
is returned as an empty N-dim Python array. If the dimensionality
is zero, it is returned as an empty 1-dim array, to prevent
numarray/numpy from treating it as a scalar value.
</li>
<li class="itemize">In numarray <span
class="cmtt-10x-x-109">array() </span>results in <span
class="cmtt-10x-x-109">Py</span><span
class="cmtt-10x-x-109">_None</span>. This is accepted by the
converters as an empty 1-dim array.
</li>
<li class="itemize">Empty arrays can be constructed in Python using empty lists. For
example, <span
class="cmtt-10x-x-109">array([[]]) </span>results in an empty 2-dim array. The converters
accept such empty N-dim Python arrays. The type of an empty array
is set to Int by numarray and to Double by numpy.
</li>
<li class="itemize">Because the type of an empty Python array cannot easily be set, the
converters can convert an empty integer or real array to any type.
</li>
<li class="itemize">The converters accept a numpy string array. However, it is returned
to Python as the special <span
class="cmtt-10x-x-109">dict </span>object described above.</li></ul>
<!--l. 128--><p class="noindent" >
<h3 class="sectionHead"><span class="titlemark">3 </span> <a
id="x1-50003"></a>Class wrappers</h3>
<!--l. 129--><p class="noindent" >Usually a binding to an existing Proxy class is made, for example <span
class="cmtt-10x-x-109">TableProxy</span>,
which should be the same class used in the glish-binding. For a simple binding,
only some simple C++ code has to be written in pyrap_xx/src/pyxx.cc, where
XX is the name of the package/class.
<table
class="verbatim"><tr class="verbatim"><td
class="verbatim"><div class="verbatim">
// Include files for converters being used.
 <br />#include <casacore/python/Converters/PycExcp.h>
 <br />#include <casacore/python/Converters/PycBasicData.h>
 <br />#include <casacore/python/Converters/PycRecord.h>
 <br />// Include file for boost python.
 <br />#include <boost/python.hpp>
 <br />
 <br />using namespace boost::python;
 <br />
 <br />namespace casa { namespace pyrap {
 <br />  void wrap_xx()
 <br />  {
 <br />    // Define the class; "xx" is the class name in Python.
 <br />    class_<XX> ("xx")
 <br />      // Define the constructor.
 <br />      // Multiple constructors can be defined.
 <br />      // They have to have different number of arguments.
 <br />      .def (init<>())
 <br />      // Add a .def line for each function to be wrapped.
 <br />      // An arg line should be added for each argument giving
 <br />      // its name and possibly default value.
 <br />      .def ("func1", &XX::func1,
 <br />            (boost::python::arg("arg1"),
 <br />             boost::python::arg("arg2")=0))
 <br />    ;
 <br />  }
 <br />}}
 <br />
 <br />BOOST_PYTHON_MODULE(_xx)
 <br />{
 <br />  // Register the conversion functions.
 <br />  casa::pyrap::register_convert_excp();
 <br />  casa::pyrap::register_convert_basicdata();
 <br />  casa::pyrap::register_convert_casa_record();
 <br />  // Initialize the wrapping.
 <br />  casa::pyrap::wrap_xx();
 <br />}
</div>
</td></tr></table>
<!--l. 172--><p class="nopar" > Python requires for each package a file <span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_init</span><span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_.py</span>, so such an empty file
should be created as well.
<!--l. 176--><p class="noindent" >
<h4 class="subsectionHead"><span class="titlemark">3.1 </span> <a
id="x1-60003.1"></a>More complicated wrappers</h4>
<!--l. 177--><p class="noindent" >Sometimes a C++ function cannot be wrapped directly, because the argument
order needs to be changed or because some extra Python checks are
necessary. In such a case the class needs to be implemented in Python
itself.
<br class="newline" />The C++ wrapped class name needs to get a different name, usually by
preceeding it with an underscore like:
<table
class="verbatim"><tr class="verbatim"><td
class="verbatim"><div class="verbatim">
    class_<XX> ("_xx")
</div>
</td></tr></table>
<!--l. 185--><p class="nopar" > The Python class should be derived from it and implement the constructor by
calling the constructor of _xx.
<table
class="verbatim"><tr class="verbatim"><td
class="verbatim"><div class="verbatim">
class xx(_xx):
 <br />    def __init__(self):
 <br />        _xx.__init__(self)
</div>
</td></tr></table>
<!--l. 192--><p class="nopar" > Now <span
class="cmtt-10x-x-109">xx </span>inherits all functions from <span
class="cmtt-10x-x-109">_xx</span>. The required function can be written in
Python like
<table
class="verbatim"><tr class="verbatim"><td
class="verbatim"><div class="verbatim">
    def func1 (self, arg1, arg2):
 <br />        return self._func1 (arg2, arg1);
</div>
</td></tr></table>
<!--l. 198--><p class="nopar" > Note that in the wrapper the function name also needs to be preceeded by an
underscore to make it different.
<!--l. 202--><p class="noindent" >
<h4 class="subsectionHead"><span class="titlemark">3.2 </span> <a
id="x1-70003.2"></a>Combining multiple classes</h4>
<!--l. 203--><p class="noindent" >Sometimes one wants to combine multiple classes in a package. A example is
package <span
class="cmtt-10x-x-109">pyrap</span><span
class="cmtt-10x-x-109">_tables </span>which contains the classes <span
class="cmtt-10x-x-109">table</span>, <span
class="cmtt-10x-x-109">tablecolumn</span>,
<span
class="cmtt-10x-x-109">tablerow</span>, <span
class="cmtt-10x-x-109">tableiter</span>, and <span
class="cmtt-10x-x-109">tableindex</span>. One is referred to the code of this
package to see how to do it.
<!--l. 210--><p class="noindent" >
<h3 class="sectionHead"><span class="titlemark">4 </span> <a
id="x1-80004"></a>Python specifics</h3>
<!--l. 211--><p class="noindent" >Besides an array being in C-order, there are a few more Python specific
issues.
<ul class="itemize1">
<li class="itemize">Indexing starts at 0 (vs. 1 in glish).
</li>
<li class="itemize">The end value in a range like <span
class="cmtt-10x-x-109">[10:20] </span>is exclusive (vs. inclusive in
glish). Furthermore Python supports a step and reversed ranges.
</li>
<li class="itemize">Where useful, the function <span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_str</span><span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_ </span>should be added giving the name
of the object. This function is used when printing an object.
</li>
<li class="itemize">Where useful, the functions <span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_len</span><span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_</span>, <span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_setitem</span><span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_(index, value)</span>,
and <span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_getitem</span><span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_(index) </span>should be added to make it possible
that a user indexes an object directly like <span
class="cmtt-10x-x-109">tabcol[i] </span>or
<span
class="cmtt-10x-x-109">tabcol[start:stop:step]</span>.
</li>
<li class="itemize">When these functions are added, Python supports iteration in
an object. Explicit iteration can also be done by adding the
functions <span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_iter</span><span
class="cmtt-10x-x-109">_</span><span
class="cmtt-10x-x-109">_ </span>and <span
class="cmtt-10x-x-109">next</span>. At the end <span
class="cmtt-10x-x-109">next </span>should raise the
Python <span
class="cmtt-10x-x-109">StopIteration </span>exception (or throw <span
class="cmtt-10x-x-109">casa::IterError </span>when
implemented in C++) to stop the iteration.</li></ul>
</body></html>
|