Page to hold style and practice guidelines for contributions to Exaile. Patches to make the existing core codebase follow these guidelines are always welcome and a good way to start learning about the internal workings of Exaile.
Note that this document will generally reflect the ‘trunk’ version of Exaile, and may not be fully applicable to stable releases. If in doubt, ask!
Exaile uses the black code formatter to enforce a consistent style across the project. You can run black like so:
For things that the code formatter doesn’t do for you, the following applies:
- In general, PEP 8 applies
- Keep imports on one line each to make sure imports cannot be missed:
# Not recommended from gi.repository import Gtk, GLib, GObject # Preferred from gi.repository import Gtk from gi.repository import GLib from gi.repository import GObject
- Always write out variable names to keep them descriptive. Thus
notebook_pageis to be preferred over
- Names which are prone to spelling mistakes like
utilare perfectly fine.
- If a very-long-named (like foooooo.bar_baz_biz_boz) variable or function needs to be accessed by a large percentage of lines in a small space, it may be shortened as long as 1) the name it is shortened to is consistent across all uses of this shortcut, and 2) the shortcut is limited in scope to just the area where it is used repeatedly. If in doubt, do NOT use this exception.
- Names which are prone to spelling mistakes like
- Try to group related methods within a class, this makes it easier to debug. If it’s a particularly significant group of methods, mark them with a triple-comment at the beginning and end, like so:
### Methods for FOOBAR ### ## more-detailed description (if needed) def meth1(self): ... ### End FOOBAR ###
- The closing triple-comment may be omitted if at the end of a class or if another triple-comment starter comes after it.
- If you need a collection of constants for some purpose, it is
recommended to use the
xl.commonto construct one. The constant type should be UpperCamelCase, the possible values UPPERCASE:
from xl.common import enum ActionType = enum(ADD='add', EDIT='edit', ...) # ... if action.type == ActionType.EDIT: # ...
- Always add docstrings to your public classes, methods and functions.
- Follow the Sphinx format for documentation within docstrings.
Events and Signals¶
- Items internal to Exaile (ie. anything under
xl/) should generally prefer
GObjectsignals. Items that tie deeply into the (GTK) UI should prefer
- Keep in mind all events are synchronous - if your callback might take a while, run it in a separate thread.
- Make sure that every access to GTK UI components is run in the
GTK main thread. Otherwise unpredictable issues can occur
including crashes due to cross-thread access. This can be
accomplished by running the specific code through the
function. Please use the function decorator
common.idle_add. A typical mistake:
- Make sure that every access to GTK UI components is run in the GTK main thread. Otherwise unpredictable issues can occur including crashes due to cross-thread access. This can be accomplished by running the specific code through the GLib.idle_add function. Please use the function decorator
def __init__(self): """ Set up a label in the GTK main thead and connect to the playback_track_start event """ self.label = Gtk.Label() event.add_callback(self.on_playback_track_start, 'playback_track_start') def on_playback_track_start(event, player, track): """ Serious problem: this event is run in a different thread, a crash is likely to occur """ self.label.set_text(track.get_tag_display('title'))
- Event names should be all lower-case, using underscores to separate
- Names should be prefixed by the general name indicating the
category or sender of the event. For example, events sent from
xl.playerstart with a
- The remainder of the name should indicate what action just
- The data sent in an event should be whatever piece (or pieces) of data are most relevant to the event. For example, if the event is signaling that a state has changed, the new state should be sent, or if the event indicates that an item was added, the new item should be sent.
- Names should be prefixed by the general name indicating the category or sender of the event. For example, events sent from
- Callbacks for
xl.eventshould always be named “
on_” + the name of the event. This avoids confusion and draws a line between regular methods and signal/event callbacks.
- If you need to handle the same signal/event for multiple objects but
differently (as in: different callbacks), include the name of the
object in the callback name. Thus the event “
clicked” for the
play_button” would become “
on_play_button_clicked”. A small exception to this rule is when a word would be repeated. Thus “
on_play_button_press_event” should be preferred over “
on_play_button_button_press_event” for the “
button-press-event” signal of the button.
- If you use Gtk.Builder
for UI descriptions, apply the rules above, make the callbacks methods
of your class and simply call
Managed object access¶
- To keep classes interchangeable, try to make use of existing signals/events wherever possible. Avoid reaching deeply into property hierarchies under all circumstances. This is bound to break sooner than later.
- If you need access to the main exaile object, call
xl.main.exaile(), if you need access to the main GUI object, call
xlgui.get_controller(), for the main window
- Many systems are already ported to singleton managers. Examples are
xlgui.icons. Simply use their
MANAGERproperty to access them.
- Use .ui files to define most widgets - reduces code clutter. A lot of basic structure can be easily prepared with the Glade interface designer, especially objects where cell renderers and models are involved.
- Try to avoid dialogs, as they are intrusive and users generally don’t
read them anyway. Inline alternatives like
and its convenience wrapper
xlgui.widgets.dialogs.MessageBarare much more effective.
- Messages should
- Be short but descriptive.
- Be proper English sentences, minus the period.
- Happen after the thing they are logging, UNLESS the thing might
take a while, in which case it may be printed before, with a
confirmation after the action completes.
- The tense of the message should match when it’s sent - if after the action, use the past tense (“Logged into Audioscrobbler”), if before, use the present(?) tense (“Logging into audioscrobbler…”).
- Messages which are present tense may use an ellipsis (”…”) to indicate the different state more clearly than by tense alone.
- Not be given prefixes to identify module, as –debug will automatically add module names. It is acceptable to use related names in the message to increase clarity however. For example, “Logged into Audioscrobbler” is much clearer than “Logged in”, but “Audioscrobbler: Logged in” is not acceptable.
- There are 4 standard logging levels built into Exaile, their names
and purpose are as follows:
- DEBUG - A significant internal event happened. Not shown by default.
- INFO - A major but expected event happened.
- WARNING - Something suboptimal happened. Exaile will continue to work properly but some features may be unavailable.
- ERROR - A critical error occurred. Exaile was unable to perform a requested action and may be in an inconsistent state if the error was not fully handled.
- When writing messages, please run both with and without –debug to ensure it looks correct and does not duplicate the information provided by any other message.
- Be sparing in the use of logging messages, particularly non-DEBUG messages. Logging messages are not an alternative to inserting print statements when debugging!
- If you create a new on-disk format, add a version flag to it. This makes forwards and backwards compatibility MUCH easier should the format ever need to change.