Python Coding Standards
How to write Python code for the project
Goals
- Make the code as easy to read and understand as possible
- Straightforward to translate to C code, should speed become an issue
Given these goals, it is important to have a set of formatting conventions that result in uniform code, guidelines for how a given task should be accomplished, and a small but well-documented series of idioms used throughout the project.
Formatting
- Attempt to keep line lengths within 100 characters
- Naming conventions:
- lowercase_with_underscores for function, variable and module names
- StudlyCase for class names (usually)
- UPPERCASE_WITH_UNDERSCORES for constants
- Whitespace:
- Use tabs, not spaces, for indentation. Use a tab width of 4. Line continuations can be made flush with spaces.
- Include two blank lines between function definitions.
- One or two blank lines between method definitions (depending on the length of the class; classes with more than a screenful of code should have two blank lines)
- Code ordering:
- Copyright notice and GNU GPL3 license block
- __future__ imports
- A docstring (it is important that you include a docstring that briefly describes the module)
- Python standard library imports (imports from libraries bundled with Python)
- Other library imports (imports from libraries we have as dependencies but that are not part of Python)
- Local imports (imports from the rb module, or other things that we have written)
- Constants (you should generally include a comment for each constant stating its purpose)
- Class and function definitions, grouped by purpose (utility functions should go at the bottom)
Style
Avoid unnecessary classes
Classes should mainly be used as structs are in C --- a convenient way to store related data together. Simple primitive types, such as tuples, lists and dictionaries are generally preferred. In particular, tuples are easy to generate in C.
For example, there is no need for a Point class to represent an x/y coordinate. A tuple --- (x, y) --- is easier to write, and easier to deal with from C. Additionally, it permits destructured assignment:
>>> point = (1, 2) >>> x, y = point >>> x 1 >>> y 2
Idioms
GTK event callbacks
The names of GTK+ event callback methods/functions should end with _cb, e.g. key_press_cb or nav_cb. The docstring for callback methods should indicate when the method is called, e.g. "Callback for navigator selection toggle buttons" for the main window. Wherever possible set the callback methods in the GtkBuilder (.gtk) file using Glade.
gobject.idle_add as a decorator
(For a brief overview of what Python decorators are, see this page.)
Generally, all manipulation of the GUI must be done from within the GTK mainloop thread to avoid data corruption and random crashes. Since this is a different thread from that used for data processing, it is commonly necessary to set up a function to be called in that thread. This can be done using the gobject.idle_add() function, which takes a function to be called there when the thread isn't busy with other tasks. It can conveniently be used as a decorator:
def update_gui(data): # do something with the data gui_data = process(data) # perform the update in the GUI thread @gobject.idle_add def update_gui(): # do the actual updates gtk_object.set_text(str(data)) return True # don't call this function again
Note that you must return True from the idle_add callback function or it will be called repeatedly.
Because of the use of this idiom, the update_gui() function can be safely called from any thread.
Queue try/except Empty idiom
When repeatedly retrieving events from a Queue.Queue object it is necessary to use a try/except block around calls to the queue object's get() method:
while True: try: event = queue.get(timeout=0.1) # Blocks for a fraction of a second except Empty: continue # Process `event`
This is because pulling an item from a thread-safe Queue object requires acquisition of a lock. A Python program will become immortal --- that is, ignore kill signals --- while it is attempting to acquire a lock (this is a quirk caused by the inconsistent capabilities of the many platforms Python supports).
The way to work around this is to have the acquisition of the lock time out after a short time, allowing Python to respond to signals again. The Queue object throws an Empty exception when it times out, so to process a stream of events this exception must be caught and the call to get() retried, hence the continue.

