Caveats and Traps

Python scripting gives you direct access to Regina's mathematical core, and as such requires some care. Probably the easiest way to crash Regina is to bring up a Python console and do something untoward (like gluing together two tetrahedra that belong to different triangulations). Please tread gently.

In particular, you should note the following issues:

Uneditable Packets

If you cannot edit a packet within Regina, there is generally a reason for this. For instance, a normal surface list needs its parent triangulation to remain fixed, which means that any triangulation with normal surface lists beneath it will be uneditable.

You should not use Python as a way to get around this barrier. Instead you can work around the problem by cloning the packet and editing the clone instead.

International and Special Characters

If you use special characters (such as accented letters, other international characters, exotic punctuation, mathematical symbols and so on), you need to think about text encodings when passing strings between Python and Regina.

If you only ever deal with plain ASCII text (plain English letters, digits and basic punctuation), you do not need to worry about text encodings at all (and you can stop reading this section).

Regina uses UTF-8 throughout for special characters. If you ever pass a string into one of Regina's functions, any special characters must be encoded in UTF-8; conversely, when a string is returned from a function you should assume that any special characters are encoded in UTF-8.

Python, on the other hand, does not use UTF-8 strings by default. See the Python UTF-8 HOWTO for more information on how to work with UTF-8 strings in Python.

As a single exception: for file names, Regina uses whatever encoding the operating system expects. Any files names that you pass to Regina will be sent through to low-level C/C++ I/O routines without any changes or re-encoding.

Ownership Issues and Boost.Python.ArgumentError / TypeError

If you create a packet in Python, it is “owned” by the Python interpreter, which means it will be destroyed when the Python interpreter closes. However, if you insert this packet into the tree using NPacket.insertChildLast(), then the packet becomes owned by the tree. This means it becomes part of your data file, and will survive even after you close your Python console.

Whenever the Python interpreter loses ownership of an object like this, the variable representing that object becomes invalid. Any attempt to access the variable will result in an ugly Python error: depending on your system, this is typically a Boost.Python.ArgumentError or a TypeError.

>>> p = NTriangulation()
>>> print p
Triangulation with 0 tetrahedra.
>>> root.insertChildLast(p)
>>> print p
Traceback (most recent call last):
  File "<console>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    ShareableObject.__str__(NTriangulation)
did not match C++ signature:
    __str__(regina::ShareableObject {lvalue})
...

The error is easily fixed: just re-fetch the packet from its new owner (the packet tree):

...
<<< p = root.getLastTreeChild()
<<< print p
Triangulation with 0 tetrahedra.

This is a general pattern: whenever Python loses ownership of an object, the corresponding variable will become invalid, and you will have to re-fetch it if you want to keep working with it. In practice this only happens with the tree insertion routines NPacket.insertChildFirst(), NPacket.insertChildLast() and NPacket.insertChildAfter(), as well as one or two other routines (such as NGroupPresentation.addRelation()).

The example above generated a Boost.Python.ArgumentError. Some systems will generate a TypeError instead, as illustrated below.

>>> p = NTriangulation()
>>> print p
Triangulation with 0 tetrahedra.
>>> root.insertChildLast(p)
>>> print p

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: bad argument type for built-in operation

Tip

If you are moving a packet around in the tree, do not call makeOrphan() followed by an insertion routine like insertChildFirst(). This will trigger this ownership issue, since makeOrphan() passes ownership of the packet to Python and then insertChildFirst() takes it away again. Instead use the single routine NPacket.reparent(), which avoids this problem since Python will never own the packet at all.