diff --git a/docs/STYLE b/docs/STYLE new file mode 100644 index 000000000..df2970b24 --- /dev/null +++ b/docs/STYLE @@ -0,0 +1,188 @@ +==================================================================== +Code not following these style guidelines fastidiously is likely +(*very* likely) not to be accepted into the Supybot core. +==================================================================== + +Read PEP 8 (Guido's Style Guide) and know that we use almost all the +same style guidelines. + +Maximum line length is 79 characters. 78 is a safer bet, though. +This is **NON-NEGOTIABLE**. Your code will not be accepted while you +are violating this guidline. + +Identation is 4 spaces per level. No tabs. This also is +**NON-NEGOTIABLE**. Your code, again, will *never* be accepted while +you have literal tabs in it. + +Single quotes are used for all string literals that aren't docstrings. +They're just easier to type. + +Triple double quotes (""") are always used for docstrings. + +Raw strings (r'' or r"") should be used for regular expressions. + +Spaces go around all operators (except around '=' in default +arguments to functions) and after all commas (unless doing so keeps a +line within the 79 character limit). + +Functions calls should look like this: "foo(bar(baz(x), y))". They +should not look like "foo (bar (baz (x), y))", or like +"foo(bar(baz(x), y) )" or like anything else. I hate extraneous +spaces. + +Class names are StudlyCaps. Method and function names are camelCaps +(StudlyCaps with an initial lowercase letter). If variable and +attribute names can maintain readability without being camelCaps, +then they should be entirely in lowercase, otherwise they should also +use camelCaps. Plugin names are StudlyCaps. + +Imports should always happen at the top of the module, one import per +line (so if imports need to be added or removed later, it can be done +easily). + +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. + +A blank line should be between all consecutive method declarations in +a class definition. Two blank lines should be between all +consecutive class definitions in a file. Comments are even better +than blank lines for separating classes. + +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 = urllib2.urlopen('url').read() +Instead, do this: + fd = urllib2.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 decsribing 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 string interpolation, not +addition: + s = x + y + z # Bad. + s = '%s%s%s' % (x, y, z) # Good. + s = ''.join([x, y, z]) # Best, but not as general. +This has to do with efficiency; the intermediate string x+y is made +(and thus copied) before x+y+z is made, so it's less efficient. +People who use string concatenation in a for loop will be swiftly +kicked in the head. + +When writing strings that have formatting characters in them, don't +use anything but %s unless you absolutely must. In particular, %d +should never be used, it's less general than %s and serves no useful +purpose. If you got the %d wrong, you'll get an exception that says, +"foo instance can't be converted to an integer." But if you use %s, +you'll get to see your nice little foo instance, if it doesn't +convert to a string cleanly, and if it does convert cleanly, you'll +get to see what you expect to see. Basically, %d just sucks. + +As a corrolary 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 him relatively interested at peak times. + WARNING: Appropriate to tell a user when we're doing something + that he really ought to pay attention to. Users should + see WARNING and think, "Hmm, should I tell the Supybot + developers about this?" Later, he should decide not to, + but it should give the user a moment to pause and think + about what's actually happening with his 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 Supybot; the only + possible thing I can imagine is to notify the user that + the partition on which Supybot 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) + +All uses of eval() that expect to get integrated in Supybot must be +approved by jemfinch, no exceptions. Chances are, it won't be +accepted. Have you looked at utils.safeEval? + +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.