Webware Style Guidelines

Contents

  • Webware Style Guidelines
  • Introduction

    Style refers to various aspects of coding including indentation practices, naming conventions, use of design patterns, treatment of constants, etc. One of the goals of Webware is to maintain fairly consistent coding style with respect to certain basics as described in this document.

    This document is therefore very important for those who routinely develop Webware or who wish to contribute code, although ordinary users may still find it interesting and useful in understanding the Webware APIs.

    Consistent source code style conventions are important for several reasons:

    This document divides the conventions into three major categories:

    1. Syntax and Naming
    2. Structure and Patterns
    3. Miscellaneous

    Syntax and Naming

    Methods and Attributes

    Methods and attributes are an important topic because they are used so frequently and therefore have an impact on using the classes, learning them, remembering them, etc.

    The first thing that is important to understand is that Webware is constructed in an object-oriented fashion, including full encapsulation of object attributes. In other words, you communicate and use objects completely via their methods (except that classes and subclasses access their own attributes – somebody's got to).

    The primary advantages of using methods are:

    In the case of a method that returns a value, that value may manifest in several ways:

    1. Stored as attribute.
    2. Stored as an attribute, only on demand (e.g., lazy and cached).
    3. Computed upon every request.
    4. Delegated to another object.

    By requiring that object access is done solely through methods, the implementer of the class is free to switch between the techniques above.

    Keeping that in mind, it is apparent that naming conventions are needed for attributes, the methods that return them and the methods that set them. Let's suppose the basic "thing" is status. The convention then is:

    The underscore that precedes the attribute denotes that it is semi-private and allows for a cleanly named retrieval method. The status() and setStatus() convention originated many years ago with Smalltalk and proved successful with that language as well as others, including Objective-C.

    Some styles prefix the retrieval method with get, but Webware does not for the sake of brevity and because there are methods for which it is not always clear if a get prefix would make sense.

    Methods that return a Boolean are prefixed with is or has to make their semantics more obvious. Examples include request.isSecure(), user.isActive() and response.hasHeader().

    Method Categories

    Webware classes divide their methods into logical groups called categories purely for organizational purposes. This often helps in understanding the interface of a class, especially when looking at its summary.

    Upon installation, Webware generates HTML summaries for each Python source file. See the summary of WebKit.HTTPResponse for an example.

    By convention, a category is named with a comment beginning and ending with two pound signs, ##, with two blanks lines before and one blank line after.

    Abbreviations

    Using abbreviations is a common coding practice to reduce typing and make lines shorter. However, removing random characters to abbreviate is a poor way to go about this. For example, transaction could be abbreviated as trnsact or trnsactn, but which letters are left out is not obvious or easy to remember.

    The typical technique in Webware is to use the first whole syllable of the word. For example, trans is easy to remember, pronounce and type.

    Attributes and methods that return the number of some kind of object are quite common. Suppose that the objects in questions are requests. Possible styles include numRequests, numberOfRequests, requestCount and so on. Webware uses the first style in all cases, for consistency:

    numRequests
    

    If there is a corresponding attribute, it should have the same name (prefixed by an underscore).

    Compound Names

    Identifiers often consist of multiple names. There are three major styles for handling compound names:

    1. serverSidePath - the Webware convention
    2. serversidepath
    3. server_side_path

    Python itself uses all three styles (isSet, getattr, has_key), but Webware uses only the first which is more readable than the second and easier to type that the third.

    This rule also applies to class names such as HTTPRequest and ServletFactory.

    Component Names

    Names of object-oriented Webware components are often suffixed with Kit, as in WebKit and MiddleKit.

    The occasional component that serves as a collective library (rather than an OO framework) is suffixed with Utils, as in MiscUtils and WebUtils.

    Rules

    What follows is a list of additional syntax and naming rules that are actually policed by the checksrc utility, described later in this document.

    Filenames are capitalized.
    For consistency and to match the capitalization convention used for classes.
    Examples: Servlet.py WebKit.cgi HTTPServlet.py
    Negatives: myClass.py session.py
    Exceptions: Files located in Webware/bin/ that are intended solely for command line use are left in all lower case similar to other command line programs such as grep and find.
    Files do not contain \r (on POSIX systems).
    To avoid problems with POSIX tools that don't like \r and confusion in the SVN repository which is hosted on Unix.
    This problem typically happens when a Windows file is FTPed in binary mode to a Unix machine from which it is then checked in. Note that the SVN client on Windows properly translates back and forth between \r\n and \n upon check out and check in, since we have set the svn:eol-style property to native.
    Indentation is done with 4 spaces per level.
    In the past, Webware for Python used tabs for indentation, but nowadays, 4 spaces per indentation level is the most popular and therefore recommended style.
    So you will not need to switch your editor any more when working on different projects.
    Tabs are not used at all.
    To avoid alignment problems between people who have tabs set to different widths.
    A common use of tabs is to align the values of a dictionary or the right hand sides of several consecutive assignment statements. There is no problem when using tabs for indentation, but when they are used past non-tab characters, the alignment will be skewed when the tab width is changed (e.g., when a different developer with a different tab width views the code).
    Class names start with a capital letter.
    For consistency and to make class names prominent in source code. This convention is used in almost every object-oriented language.
    Examples: Servlet HTTPServlet ThreadedAppServer
    Negatives: servlet myClass
    Method names start with a lower case letter.
    For consistency and ease of typing. This convention is also used in almost every object-oriented language.
    Examples: respond() isSecure() setTimeout() htForDict()
    Negatives: Respond() IsSecure() SetTimeout() HtForDict()
    Method names do not start with get.
    See Methods and Attributes above.
    Examples: status() numRequests()
    Negatives: getStatus() getNumRequests()
    Data attributes start with an underscore (_).
    The non-underscored version of the name is reserved for the accessor method. See Methods and Attributes above.
    Examples: self._status self._numRequests
    Negatives: self.status self.numRequests
    Method and attribute names have no underscores after the first character.
    Capitalization is used instead.
    Examples: self.numRequests() factory.servletForTransaction()
    Negatives: self.num_requests() factory.servlet_for_transaction()
    Expressions following if, while and return are not enclosed in parentheses.
    Because this isn't C or Perl. :-)
    Examples: if serialNum>0:
    Negatives: if (serialNum>0):
    Class definitions and category comments are preceded by 2 blank lines and followed by one.
    To help with spacing and readability. Also, see Method Categories.
    Examples:
    ## Transactions ##
    
    def transaction(self):
        return self._transaction
    
    
    ## Values ##
    
    def hasValue(self, name):
        return name in self._fields or name in self._cookies
    Exceptions: No category comment is added when the class implementation is pass.
    Use docstrings and the PEP 257 conventions.
    This allows for creating appropriate code documentation automatically. Please note that in the past, this has not been heeded very well. But as the project matures and grows, the docstrings should be improved.
    Examples:
    def function(a, b):
        """Do X and return a list."""
    Negatives:
    def function(a, b):
        """function(a, b) -> list"""
    "To do" marks should be marked as @@ yyyy-mm-dd initials: comment.
    These things are easier to track if they are dated and marked in a consistent way.
    Examples: # @@ 2000-05-03 ce: This is bogus. Disregard for now.
    Negatives: # I think this is bogus !!
    Limit all lines to a maximum of 100 characters if possible.
    This has been settled as a compromise between the people wanting to utilize their huge monitors with line sizes of 130 characters and those wanting to follow the stricter Python style guide recommendation of only 80 characters.
    If not specified otherwise, follow the PEP 008 conventions.
    For historical and opinionated reasons of Webware's BDFL, Webware follows its own style guidelines which overrule the standard guidelines for Python. :-)

    Structure and Patterns

    Classes

    Webware overwhelmingly uses classes rather than collections of functions for several reasons:

    By using classes, all three options above are available to the developer on an on-going basis. By using collections of functions, none of the above are readily available.

    Note that making classes in Python is extraordinarily easy. Doing so requires one line:

    class Foo(SuperFoo):
    

    and the use of self when accessing attributes and methods. The difference in time between creating a class with several methods vs. a set of several functions is essentially zero.

    Configuration Files

    Specific numbers and strings often appear in source code for determining things such as the size of a cache, the duration before timeout, the name of the server and so on. Rather than place these values directly in source, Webware provides configuration files that are easily discerned and edited by users, without requiring a walk through the source.

    Webware uses ordinary Python dictionaries for configuration files for several reasons:

    By convention, these configuration files:

    WebKit provides a Configurable mix-in class that is used to read configuration files. It allows subclasses to say self.setting('Something') so that the use of configuration information is easy and recognizable throughout the code.

    Accessing Named Objects

    Several classes in Webware store dictionaries of objects keyed by their name. HTTPRequest is one such class which stores a dictionary of form fields. The convention for providing an interface to this information is as follows:

    ## Fields ##
    def field(self, name, default=_NoDefault):
    def hasField(self, name):
    def fields(self):
    

    A typical use would be:

    req.field('city')
    

    which returns the field value of the given name or raises an exception if there is none. Like the get() method of Python's dictionary type, a second parameter can be specified which will be returned if the value is not found:

    req.field('city', None)
    

    req.hasField('city') is a convenience method that returns True if the value exists, False otherwise.

    req.fields() returns the entire dictionary so that users have complete access to the full suite of dictionary methods such as keys(), values(), items(), etc. Users of this method are trusted not to modify the dictionary in any way. A paranoid class may choose to return a copy of the dictionary to help reduce abuse (although Webware classes normally do not for performance reasons).

    In cases where the user of the class should be able to modify the named objects, the following methods are provided:

    def setValue(self, name, value):
    def delValue(self, name):
    

    Often Python programmers will provide dictionary-style access to their objects by implementing __getitem__ so that users may say:

    req['city']
    

    Webware generally avoids this approach for two reasons. The first and most important is that Webware classes often have more than one set of named objects. For example, HTTPRequest has both fields and cookies. HTTPResponse has both cookies and headers. These objects have their own namespaces, making the semantics of obj['name'] ambiguous. Also, the occasional class that has only one dictionary could potentially have more in the future.

    The second reason is the readability provided by expressions such as response.cookie(name) which states clearly what is being asked for.

    In those cases where objects provide dictionary-like access, the class is typically a lightweight container that is naturally thought of in terms of its dictionary components. Usually these classes inherit from dict.

    Components

    Webware consists of multiple components that follow particular conventions, not only for the sake of consistency, but also to enable scripts to manipulate them (such as generating documentation upon installation).

    Example components include WebKit, PSP and MiscUtils.

    These conventions are not yet formally documented, however if you quickly browse through a couple components, some conventions about directory structure and source code become apparent.

    Also, if a component serves as a WebKit plug-in, then there are additional conventions for them to follow in order to work correctly. See Plug-ins in the WebKit User's Guide.

    Miscellaneous Notes

    Limitations

    Some features that have been introduced in newer Python versions, like properties or decorators, could be used to create more readable code. However, since we want Webware to be backward compatible, these newer features should currently not be used. In future versions of Webware we may certainly lift these restrictions and adapt the style guidelines accordingly.

    Permissions

    When creating standalone Python scripts, like those found in Webware/bin, you should set the svn:executable property when you check them in to the SVN repository. Otherwise Unix-based developers will not have execute permissions after checking the scripts out.

    Sending Updates

    Those who actually develop Webware should send messages to webware-devel@lists.sourceforge.net when they update the repository.

    The subject should contain the word "update" so that release notes can be easily compiled.

    Note that automatic messages from SVN are not a good substitute for these types of messages which are more informative and more easily compiled into interesting release notes.

    Breaking the Rules

    Of course, there are times when the rules are broken for good reason. To quote a cliché: "Part of being an expert is knowing when to break the rules."

    But regarding style, Webware developers do this very infrequently for the reasons stated in the introduction.

    checksrc.py

    checksrc.py is a program located in Webware/bin used to check the syntax and naming conventions in the actual Webware source code. You can invoke the program with -h or --help to get information on how to use it and there is a good doc string at the top of file if you're interested in more details. checksrc.py should be run periodically and especially before each release of Webware.

    You can also use pylint (available from Logilab.org) for this purpose, which can be easily integrated in most IDEs. A suitable rcfile .pylintrc has been placed in the Webware root directory.

    Future Work

    A list of future work for this document: