# Webware Style Guidelines

## Table of Contents

Introduction
Syntax and Naming
Methods and Attributes
Method Categories
Abbreviations
Compound Names
Component Names
Rules
Structure and Patterns
Classes
Configuration Files
Accessing Named Objects
Components
Miscellaneous Notes
Permissions
Sending Updates
Breaking the Rules
checksrc.py
Future Work

## 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:

- Consistent conventions reduce the learning curve because users know what to expect from the API.
- Consistent conventions can actually enable certain code or features. For example, the method categories convention allows for better file summaries to be generated by Webware's installer.
- Webware is open source resulting in many people reading the code in an attempt to understand it, extend it and debug it.
- Webware consists of tens of thousands of lines of source code and will undoubtedly continue to grow.

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:

- Method implementations can be changed with minimal impact on the users of the class.
- Subclasses can customize methods to suit their needs and still be used in the usual fashion (as defined by the superclass).

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:

- `_status` - the attribute
- `status()` - the retrieval method
- `setStatus()` - the setter method

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` to make their semantics more obvious. Examples include `app.isRunning()`, `user.isActive()` and `appServer.isPersistent()`.

### 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 it's 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:

    numRequest

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 (`has_key`, `getattr`, `isSet`), 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. { 'class': 'Convention', 'what': 'Filenames are capitalized.', 'why': '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.', } { 'class': 'Convention', 'what': 'Files do not contain \\r (on POSIX).', 'why': '''To avoid problems with UNIX tools that don't like \\r and confusion in the repository which is hosted on UNIX.
Note that the Win32 CVS client will properly translate back and forth between \\r\\n and \\n upon check out and check in.
This problem typically happens because a Windows file is FTPed in binary mode to a UNIX machine from which it is then checked in.''', } { 'class': 'Convention', 'what': 'Spaces are not used for indentation.', 'why': "The real problem is mixing tabs and spaces for indentation as people have their tabs set to different widths (commonly 4 or 8). Tabs have been chosen because they're convenient, efficient and fast to cursor through.", } { 'class': 'Convention', 'what': 'Tabs are not used past initial indentation.', 'why': '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': 'Convention', 'what': 'Class names start with a capital letter.', 'why': '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', } { 'class': 'Convention', 'what': 'Method names start with a lower case letter.', 'why': 'For consistency and easy of typing. This convention is also used in almost every object-oriented language.', 'examples': 'respond() isSecure() setTimeout() htForDict()', 'negatives': 'Respond() IsSecure()', } { 'class': 'Convention', 'what': 'Method names do not start with get.', 'why': 'See Methods and Attributes above.', 'examples': 'status() numRequests()', 'negatives': 'getStatus() getNumRequests()', } { 'class': 'Convention', 'what': 'Data attributes start with an underscore (_).', 'why': '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', } { 'class': 'Convention', 'what': 'Method and attribute names have no underscores after the first character.', 'why': 'Capitalization is used instead.', 'examples': 'self.numRequests() factory.servletForTransaction()', 'negatives': 'self.num_requests() factory.servlet_for_transaction()', } { 'class': 'Convention', 'what': 'Expressions following if, while and return are not enclosed in parentheses.', 'why': "Because this isn't C or Perl. `:-)`", 'examples': 'if serialNum>0:', 'negatives': 'if (serialNum>0):', } { 'class': 'Convention', 'what': 'Class definitions and category comments, `## Like this ##`, are preceded by 2 blank lines and followed by one (unless the class implementation is `pass`).', 'why': 'To help with spacing and readability. Also, see method categories.', }

## Structure and Patterns

### Classes

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

- Classes allow for subclassing and therefore, a proven method of software extension and customization.
- Classes allow for creating multiple instances (or objects) to be used in various ways. Examples include caching created objects for increased performance and created multi-threaded servers.
- Classes can provide for and encourage encapsulation of data, which gives implementers more freedom to improve their software without breaking what depends on it.

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 source code for several reasons:

- Those who know Python will already understand the syntax.
- Python dictionaries can be quickly and easily read (via Python's `eval()` function).
- Dictionaries can contain nested structures such as lists and other dictionaries, providing a richer configuration language.

By convention, these configuration files:

- Contain a Python dictionary.
- Use a `.config` extension.
- Capitalize their keys.
- Are named after the class that reads them.
- Are located in a `Configs/` subdirectory or in the same directory as the program.

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 1 if the value exists, 0 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 it's dictionary components. Usually these classes inherit from `UserDict`.

### 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

### Permissions

When creating standalone Python programs, like those found in Webware/bin, you should make them executable by all (`chmod a+X filename.py`) and must first check them in on UNIX. Otherwise UNIX-based developers using CVS will not have execute permissions by default. Also, that problem cannot be fixed by changing the permissions and committing. Instead, a support request must be made to modify the CVS repository.

### Sending Updates

Those who actually develop Webware should send messages to webware-discuss@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 CVS 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 cliche: "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.

### Future Work

A list of future work for this document:

- Document the conventions for Webware components.