SQLObject Developer Guide

Contents

These are some notes on developing SQLObject.

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://github.com/sqlobject/sqlobject
$ cd sqlobject
$ sudo python setup.py develop

Or rather fork it and clone your fork. To develop a feature or a bugfix create a separate branch, push it to your fork and create a pull request to the original repo. That way CI will be triggered to test your code.

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

Architecture

There are three main kinds of objects in SQLObject: tables, columns and connections.

Tables-related objects are in sqlobject/main.py module. There are two main classes: SQLObject and sqlmeta; the latter is not a metaclass but a parent class for sqlmeta attribute in every class - the authors tried to move there all attributes and methods not directly related to columns to avoid cluttering table namespace.

Connections are instances of DBConnection class (from sqlobject/dbconnection.py) and its concrete descendants. DBConnection contains generic code for generating SQL, working with transactions and so on. Concrete connection classes (like PostgresConnection and SQLiteConnection) provide backend-specific functionality.

Columns, validators and converters

Columns are instances of classes from sqlobject/col.py. There are two classes for every column: one is for user to include into an instance of SQLObject, an instance of the other is automatically created by SQLObject's metaclass. The two classes are usually named Col and SOCol; for example, BoolCol and SOBoolCol. User-visible classes, descendants of Col, seldom contain any code; the main code for a column is in SOCol descendants and in validators.

Every column has a list of validators. Validators validate input data and convert input data to python data and back. Every validator must have methods from_python and to_python. The former converts data from python to internal representation that will be converted by converters to SQL strings. The latter converts data from SQL data to python. Also please bear in mind that validators can receive None (for SQL NULL) and SQLExpression (an object that represents SQLObject expressions); both objects must be passed unchanged by validators.

Converters from sqlobject/converters.py aren't visible to users. They are used behind the scene to convert objects returned by validators to backend-specific SQL strings. The most elaborated converter is StringLikeConverter. Yes, it converts strings to strings. It converts python strings to SQL strings using backend-specific quoting rules.

Let look into BoolCol as an example. The very BoolCol doesn't have any code. SOBoolCol has a method to create BoolValidator and methods to create backend-specific column type. BoolValidator has identical methods from_python and to_python; the method passes None, SQLExpression and bool values unchanged; int and objects that have method __nonzero__ are converted to bool; other objects trigger validation error. Bool values that are returned by call to from_python will be converted to SQL strings by BoolConverter; bool values from to_python (is is supposed they are originated from the backend via DB API driver) are passed to the application.

Objects that are returned from from_python must be registered with converters. Another approach for from_python is to return an object that has __sqlrepr__ method. Such objects convert to SQL strings themselves, converters are not used.

Style Guide

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

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 https://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, we'll be forced to write the test. That's no fun for us, 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 and Circle CI to run tests.

Visit Travis CI status <<https://travis-ci.org/sqlobject/sqlobject> or Circle CI status <https://circleci.com/gh/sqlobject/sqlobject> to see the current status.

To avoid triggering unnecessary test run at CI services add text [skip ci] or [ci skip] anywhere in your commit messages for commits that don't change code (documentation updates and such).

We use coverage.py to measures code coverage by tests and upload the result for analyzis to Coveralls and Codecov.

Visit Coveralls status <https://coveralls.io/github/sqlobject/sqlobject?branch=master> or Codecov status <https://codecov.io/gh/sqlobject/sqlobject> to see the current 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.6, which must be obtained separately (for instance, from https://pypi.python.org/pypi/kid/0.9.6).