+++++++++++++++++++++++++
SQLObject Developer Guide
+++++++++++++++++++++++++

.. contents::
   :backlinks: none

.. _start:

These are some notes on developing SQLObject.  I'll try to expand them
as things come up.  If you are committing to the SQLObject repository,
please also read ``/using-this-repository.txt``, which is just about
file layout and repository cooperation.

  -- Ian Bicking

Development Installation
========================

First install `FormEncode <http://formencode.org>`_::

    $ svn co http://svn.colorstudy.com/FormEncode/trunk FormEncode
    $ cd FormEncode
    $ sudo python setup.py develop

Then do the same for SQLObject::

    $ svn co http://svn.colorstudy.com/trunk/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
<http://peak.telecommunity.com/DevCenter/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:

.. _PEP 8: http://www.python.org/peps/pep-0008.html

* **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 via subversion at http://codespeak.net/svn/py/dist.  Read
its `getting started`_ document for more.

.. __: http://codespeak.net/py/current/doc/test.html
.. _getting started: http://codespeak.net/py/current/doc/getting_started.html

To actually run the test, you have to give it a database to connect
to.  You do this with the ``TESTDB`` environmental variable (right now
py.test doesn't have a better way to add custom options).  You can
give the complete URI to access your test database, or you can give it
a shortcut 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
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.

.. _raises: http://codespeak.net/py/current/doc/test.html#id4


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.
