Style Guidelines

Note: Code not following these style guidelines fastidiously is likely not to be accepted into the Limnoria core.

  • Read PEP 8 (Guido’s Style Guide) and know that we use almost all the same style guidelines. - We use a maximum of 79 characters per line and 4 spaces per indentation level - Exception: method and function names generally use camelCase for consistency with existing code.

  • Raw strings (r'' or r"") should be used for regular expressions.

  • Unless absolutely required by some external force, imports should be ordered by the string length of the module imported. I just think it looks prettier.

  • Database filenames should generally begin with the name of the plugin and the extension should be ‘db’. plugins.DBHandler does this already.

  • Whenever creating a file descriptor or socket, keep a reference around and be sure to close it. There should be no code like this:

    s = urllib.request.urlopen('url').read()
    

    Instead, do this:

    fd = urllib.request.urlopen('url')
    try:
        s = fd.read()
    finally:
        fd.close()
    

    This is to be sure the bot doesn’t leak file descriptors.

  • All plugin files should include a docstring describing what the plugin does. This docstring will be returned when the user is configuring the plugin.

  • All plugin classes should also include a docstring describing how to do things with the plugin; this docstring will be returned when the user requests help on a plugin name.

  • Method docstrings in classes deriving from callbacks.Privmsg should include an argument list as their first line, and after that a blank line followed by a longer description of what the command does. The argument list is used by the syntax command, and the longer description is used by the help command.

  • Whenever joining more than two strings, use f-strings or string interpolation, not addition:

    s = x + y + z # Bad.
    s = '%s%s%s' % (x, y, z) # Good.
    s = ''.join([x, y, z]) # Better, but not as general.
    s = f'{x}{y}{z}' # Best.
    
  • When writing strings that have formatting characters in them, don’t use anything but %s unless you absolutely must. Avoid %d in particular because it’s not as general and is likely to throw type errors if you make a mistake.

  • As a corollary to the above, note that sometimes %f is used, but on when floats need to be formatted, e.g., %.2f.

  • Use the log module to its fullest; when you need to print some values to debug, use self.log.debug to do so, and leave those statements in the code (commented out) so they can later be re-enabled. Remember that once code is buggy, it tends to have more bugs, and you’ll probably need those print statements again.

  • While on the topic of logs, note that we do not use % (i.e., str.__mod__) with logged strings; we simple pass the format parameters as additional arguments. The reason is simple: the logging module supports it, and it’s cleaner (fewer tokens/glyphs) to read.

  • While still on the topic of logs, it’s also important to pick the appropriate log level for given information.

    • DEBUG: Appropriate to tell a programmer how we’re doing something (i.e., debugging printfs, basically). If you’re trying to figure out why your code doesn’t work, DEBUG is the new printf – use that, and leave the statements in your code.
    • INFO: Appropriate to tell a user what we’re doing, when what we’re doing isn’t important for the user to pay attention to. A user who likes to keep up with things should enjoy watching our logging at the INFO level; it shouldn’t be too low-level, but it should give enough information that it keeps them relatively interested at peak times.
    • WARNING: Appropriate to tell a user when we’re doing something that they really ought to pay attention to. Users should see WARNING and think, “Hmm, should I tell the Limnoria developers about this?” Later, they should decide not to, but it should give the user a moment to pause and think about what’s actually happening with their bot.
    • ERROR: Appropriate to tell a user when something has gone wrong. Uncaught exceptions are ERRORs. Conditions that we absolutely want to hear about should be errors. Things that should scare the user should be errors.
    • CRITICAL: Not really appropriate. I can think of no absolutely critical issue yet encountered in Limnoria; the only possible thing I can imagine is to notify the user that the partition on which Limnoria is running has filled up. That would be a CRITICAL condition, but it would also be hard to log :)
  • All plugins should have test cases written for them. Even if it doesn’t actually test anything but just exists, it’s good to have the test there so there’s a place to add more tests later (and so we can be sure that all plugins are adequately documented; PluginTestCase checks that every command has documentation)

  • SQL table names should be all-lowercase and include underscores to separate words. This is because SQL itself is case-insensitive. This doesn’t change, however the fact that variable/member names should be camel case.

  • SQL statements in code should put SQL words in ALL CAPS:

    """SELECT quote FROM quotes ORDER BY random() LIMIT 1"""
    

    This makes SQL significantly easier to read.

  • Common variable names

    • L => an arbitrary list.
    • t => an arbitrary tuple.
    • x => an arbitrary float.
    • s => an arbitrary string.
    • f => an arbitrary function.
    • p => an arbitrary predicate.
    • i,n => an arbitrary integer.
    • cb => an arbitrary callback.
    • db => a database handle.
    • fd => a file-like object.
    • msg => an ircmsgs.IrcMsg object.
    • irc => an irclib.Irc object (or proxy)
    • nick => a string that is an IRC nick.
    • channel => a string that is an IRC channel.
    • hostmask => a string that is a user’s IRC prefix.

    When the semantic functionality (that is, the “meaning” of a variable is obvious from context), one of these names should be used. This just makes it easier for people reading our code to know what a variable represents without scouring the surrounding code.

  • Multiple variable assignments should always be surrounded with parentheses – i.e., if you’re using the partition function, then your assignment statement should look like:

    (good, bad) = partition(p, L)
    

    The parentheses make it obvious that you’re doing a multiple assignment, and that’s important because I hate reading code and wondering where a variable came from.