#

# SQLObject Developer Guide

Contents

- Development Installation
- Style Guide
- Testing
- Documentation

These are some notes on developing SQLObject. I'll try to expand them as things come up.

> -- Ian Bicking

# Development Installation

First install FormEncode:

    $ git clone git://github.com/formencode/formencode.git
    $ cd formencode
    $ sudo python setup.py develop

Then do the same for SQLObject:

    $ git clone git clone git://git.code.sf.net/p/sqlobject/sqlobject
    $ cd sqlobject
    $ sudo python setup.py develop

Voila! The packages are globally installed, but the files from the checkout were not copied into `site-packages`. See setuptools for more.

# Style Guide

Generally you should follow the recommendations in PEP 8, the Python Style Guide. Some things to take particular note of:

- With a few exceptions sources must be flake8-clean (and hence pep8-clean). Please consider using pre-commit hook installed by running `flake8 --install-hook`.

- No tabs. Not anywhere. Always indent with 4 spaces.

- I don't stress too much on line length. But try to break lines up by grouping with parenthesis instead of with backslashes (if you can). Do asserts like:

      assert some_condition(a, b), (
          "Some condition failed, %r isn't right!" % a)

- But if you are having problems with line length, maybe you should just break the expression up into multiple statements.

- Blank lines between methods, unless they are very small and closely bound to each other.

- Never use the form `condition and trueValue or falseValue`. Break it out and use a variable.

- Careful of namespace pollution. SQLObject does allow for `from sqlobject import *` so names should be fairly distinct, or they shouldn't be exported in `sqlobject.__init__`.

- I'm very picky about whitespace. There's one and only one right way to do it. Good examples:

      short = 3
      longerVar = 4

      if x == 4:
          do stuff

      func(arg1='a', arg2='b')
      func((a + b)*10)

  Bad examples:

      short    =3
      longerVar=4

      if x==4: do stuff

      func(arg1 = 'a', arg2 = 'b')
      func(a,b)
      func( a, b )
      [ 1, 2, 3 ]

  To me, the poor use of whitespace seems lazy. I'll think less of your code (justified or not) for this very trivial reason. I will fix all your code for you if you don't do it yourself, because I can't bear to look at sloppy whitespace.

- Use `@@` to mark something that is suboptimal, or where you have a concern that it's not right. Try to also date it and put your username there.

- Docstrings are good. They should look like:

      class AClass(object):
          """
          doc string...
          """

  Don't use single quotes ('''). Don't bother trying make the string less vertically compact.

- Comments go right before the thing they are commenting on.

- Methods never, ever, ever start with capital letters. Generally only classes are capitalized. But definitely never methods.

- mixedCase is preferred.

- Use `cls` to refer to a class. Use `meta` to refer to a metaclass (which also happens to be a class, but calling a metaclass `cls` will be confusing).

- Use `isinstance` instead of comparing types. E.g.:

      if isinstance(var, str): ...
      # Bad:
      if type(var) is StringType: ...

- Never, ever use two leading underscores. This is annoyingly private. If name clashes are a concern, use name mangling instead (e.g., `_SO_blahblah`). This is essentially the same thing as double-underscore, only it's transparent where double underscore obscures.

- Module names should be unique in the package. Subpackages shouldn't share module names with sibling or parent packages. Sadly this isn't possible for `__init__`, but it's otherwise easy enough.

- Module names should be all lower case, and probably have no underscores (smushedwords).

# Testing

Tests are important. Tests keep everything from falling apart. All new additions should have tests.

Testing uses py.test, an alternative to `unittest`. It is available at http://pytest.org/ and http://pypi.python.org/pypi/pytest. Read its getting started document for more.

To actually run the test, you have to give it a database to connect to. You do so with the option `-D`. You can either give a complete URI or one of several shortcuts like `mysql` (these shortcuts are defined in the top of `tests/dbtest.py`).

All the tests are modules in `sqlobject/tests`. Each module tests one kind of feature, more or less. If you are testing a module, call the test module `tests/test_modulename.py` -- only modules that start with `test_` will be picked up by py.test.

The "framework" for testing is in `tests/dbtest`. There's a couple of important functions:

`setupClass(soClass)` creates the tables for the class. It tries to avoid recreating tables if not necessary.

`supports(featureName)` checks if the database backend supports the named feature. What backends support what is defined at the top of `dbtest`.

If you `import *` you'll also get py.test's version of raises, an `inserts` function that can create instances for you, and a couple miscellaneous functions.

If you submit a patch or implement a feature without a test, I'll be forced to write the test. That's no fun for me, to just be writing tests. So please, write tests; everything at least needs to be exercised, even if the tests are absolutely complete.

We now use Travis CI to run tests. See the status:

# Documentation

Please write documentation. Documentation should live in the docs/ directory. Pudge converts documentation from Restructured Text to HTML. It presently requires kid 0.9.3, which must be obtained separately (for instance, from http://pypi.python.org/pypi/kid/0.9.3)