Author: ianb
Date: Tue Feb 10 20:13:17 2004
New Revision: 13
Modified:
trunk/SQLObject/docs/News.txt
trunk/SQLObject/sqlobject/__init__.py
trunk/SQLObject/sqlobject/col.py
trunk/SQLObject/sqlobject/dbconnection.py
trunk/SQLObject/sqlobject/dbm/__init__.py
trunk/SQLObject/sqlobject/dbm/dbmconnection.py
trunk/SQLObject/sqlobject/firebird/__init__.py
trunk/SQLObject/sqlobject/firebird/firebirdconnection.py
trunk/SQLObject/sqlobject/main.py
trunk/SQLObject/sqlobject/mysql/__init__.py
trunk/SQLObject/sqlobject/mysql/mysqlconnection.py
trunk/SQLObject/sqlobject/postgres/__init__.py
trunk/SQLObject/sqlobject/postgres/pgconnection.py
trunk/SQLObject/sqlobject/sqlite/__init__.py
trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py
trunk/SQLObject/sqlobject/sybase/__init__.py
trunk/SQLObject/sqlobject/sybase/sybaseconnection.py
trunk/SQLObject/tests/SQLObjectTest.py
Log:
Bunch of fixes to database refactoring-
made database modules load lazily (only when they are used), did
actual regression testing on the databases
Modified: trunk/SQLObject/docs/News.txt
==============================================================================
--- trunk/SQLObject/docs/News.txt (original)
+++ trunk/SQLObject/docs/News.txt Tue Feb 10 20:13:17 2004
@@ -18,6 +18,7 @@
``obj.sync()`` (``sync`` also refetches the data from the database,
which ``syncUpdate`` does not do). When enabled, instances have a
property ``dirty``, which indicates if they have pending updates.
+ Inserts are still done immediately.
* Separated database drivers (PostgresConnection, MySQLConnection,
etc.) into separate packages. You can access the driver through
URIs, like ``mysql://user:pass@host/dbname`` -- to set drivers after
@@ -32,6 +33,11 @@
* We're now using a Subversion repository instead of CVS. It is
located at svn://colorstudy.com/trunk/SQLObject
+Bugs
+----
+
+* SQLite booleans fixed.
+
SQLObject 0.5.2
===============
Modified: trunk/SQLObject/sqlobject/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/__init__.py (original)
+++ trunk/SQLObject/sqlobject/__init__.py Tue Feb 10 20:13:17 2004
@@ -4,9 +4,12 @@
from styles import *
from joins import *
from include import validators
+from dbconnection import connectionForURI
## Each of these imports allows the driver to install itself
+import firebird
+del firebird
import mysql
del mysql
import postgres
Modified: trunk/SQLObject/sqlobject/col.py
==============================================================================
--- trunk/SQLObject/sqlobject/col.py (original)
+++ trunk/SQLObject/sqlobject/col.py Tue Feb 10 20:13:17 2004
@@ -352,6 +352,9 @@
def _sybaseType(self):
return "BIT"
+ def _firebirdType(self):
+ return 'INT'
+
class BoolCol(Col):
baseClass = SOBoolCol
@@ -405,7 +408,7 @@
SOKeyCol.__init__(self, **kw)
def postgresCreateSQL(self):
- from SQLObject import findClass
+ from main import findClass
sql = SOKeyCol.postgresCreateSQL(self)
if self.cascade is not None:
other = findClass(self.foreignKey)
Modified: trunk/SQLObject/sqlobject/dbconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/dbconnection.py (original)
+++ trunk/SQLObject/sqlobject/dbconnection.py Tue Feb 10 20:13:17 2004
@@ -56,24 +56,30 @@
raise NotImplemented
connectionFromURI = classmethod(connectionFromURI)
- def _parseURI(uri, expectHost=True):
+ def _parseURI(uri):
schema, rest = uri.split(':', 1)
- rest = rest.strip('/')
- if expectHost:
+ assert rest.startswith('/'), "URIs must start with scheme:/ -- you did not include a / (in %r)" % rest
+ if rest.startswith('/') and not rest.startswith('//'):
+ host = None
+ rest = rest[1:]
+ elif rest.startswith('///'):
+ host = None
+ rest = rest[3:]
+ else:
+ rest = rest[2:]
if rest.find('/') == -1:
- host, rest = rest, ''
+ host = rest
+ rest = ''
else:
host, rest = rest.split('/', 1)
- if host.find('@') != -1:
- user, host = host.split('@', 1)
- if user.find(':') != -1:
- user, password = user.split(':', 1)
- else:
- password = None
+ if host and host.find('@') != -1:
+ user, host = host.split('@', 1)
+ if user.find(':') != -1:
+ user, password = user.split(':', 1)
else:
- user = password = None
+ password = None
else:
- host = user = password = None
+ user = password = None
path = '/' + rest
return user, password, host, path
_parseURI = staticmethod(_parseURI)
@@ -462,18 +468,18 @@
class ConnectionURIOpener(object):
def __init__(self):
- self.allClasses = []
- self.classSchemes = {}
+ self.schemeBuilders = {}
+ self.schemeSupported = {}
self.instanceNames = {}
+ self.cachedURIs = {}
- def registerConnectionClass(self, cls):
- if cls not in self.allClasses:
- self.allClasses.append(cls)
- for uriScheme in cls.schemes:
- assert not self.classSchemes.has_key(uriScheme) \
- or self.classSchemes[uriScheme] is cls, \
- "A class has already been registered for the URI scheme %s" % uriScheme
- self.classSchemes[uriScheme] = cls
+ def registerConnection(self, schemes, builder, isSupported):
+ for uriScheme in schemes:
+ assert not self.schemeBuilders.has_key(uriScheme) \
+ or self.schemeBuilders[uriScheme] is builder, \
+ "A driver has already been registered for the URI scheme %s" % uriScheme
+ self.schemeBuilders[uriScheme] = builder
+ self.schemeSupported = isSupported
def registerConnectionInstance(self, inst):
if inst.name:
@@ -483,20 +489,25 @@
assert inst.name.find(':') == -1, "You cannot include ':' in your class names (%r)" % cls.name
self.instanceNames[inst.name] = inst
- def openURI(self, uri):
+ def connectionForURI(self, uri):
+ if self.cachedURIs.has_key(uri):
+ return self.cachedURIs[uri]
if uri.find(':') != -1:
scheme, rest = uri.split(':', 1)
- assert self.classSchemes.has_key(scheme), \
+ assert self.schemeBuilders.has_key(scheme), \
"No SQLObject driver exists for %s" % scheme
- return self.classSchemes[scheme].connectionFromURI(uri)
+ conn = self.schemeBuilders[scheme]().connectionFromURI(uri)
else:
# We just have a name, not a URI
assert self.instanceNames.has_key(uri), \
"No SQLObject driver exists under the name %s" % uri
- return self.instanceNames[uri]
+ conn = self.instanceNames[uri]
+ # @@: Do we care if we clobber another connection?
+ self.cachedURIs[uri] = conn
+ return conn
TheURIOpener = ConnectionURIOpener()
-registerConnectionClass = TheURIOpener.registerConnectionClass
+registerConnection = TheURIOpener.registerConnection
registerConnectionInstance = TheURIOpener.registerConnectionInstance
-openURI = TheURIOpener.openURI
+connectionForURI = TheURIOpener.connectionForURI
Modified: trunk/SQLObject/sqlobject/dbm/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/dbm/__init__.py (original)
+++ trunk/SQLObject/sqlobject/dbm/__init__.py Tue Feb 10 20:13:17 2004
@@ -1,5 +1,14 @@
-from sqlobject import dbconnection
-from dbmconnection import DBMConnection
+from sqlobject.dbconnection import registerConnection
-dbconnection.registerConnectionClass(
- DBMConnection)
+def builder():
+ import dbmconnection
+ return dbmconnection.DBMConnection
+
+def isSupported():
+ try:
+ import anydbm
+ except ImportError:
+ return False
+ return True
+
+registerConnection(['dbm'], builder, isSupported)
Modified: trunk/SQLObject/sqlobject/dbm/dbmconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/dbm/dbmconnection.py (original)
+++ trunk/SQLObject/sqlobject/dbm/dbmconnection.py Tue Feb 10 20:13:17 2004
@@ -105,7 +105,6 @@
supportTransactions = False
dbName = 'dbm'
- schemes = [dbName]
def __init__(self, path, **kw):
global anydbm, pickle
@@ -136,16 +135,6 @@
return cls(path)
connectionFromURI = classmethod(connectionFromURI)
- def isSupported(cls):
- global anydbm
- if anydbm is None:
- try:
- import anydbm
- except ImportError:
- return False
- return True
- isSupported = classmethod(isSupported)
-
def _newID(self, table):
id = int(self._meta["%s.id" % table]) + 1
self._meta["%s.id" % table] = str(id)
Modified: trunk/SQLObject/sqlobject/firebird/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/firebird/__init__.py (original)
+++ trunk/SQLObject/sqlobject/firebird/__init__.py Tue Feb 10 20:13:17 2004
@@ -1,5 +1,13 @@
-from sqlobject import dbconnection
-from firebirdconnection import FirebirdConnection
+from sqlobject.dbconnection import registerConnection
-dbconnection.registerConnectionClass(
- FirebirdConnection)
+def builder():
+ import firebirdconnection
+ return firebirdconnection.FirebirdConnection
+
+def isSupported():
+ try:
+ import kinterbasdb
+ except ImportError:
+ return False
+
+registerConnection(['firebird', 'interbase'], builder, isSupported)
Modified: trunk/SQLObject/sqlobject/firebird/firebirdconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/firebird/firebirdconnection.py (original)
+++ trunk/SQLObject/sqlobject/firebird/firebirdconnection.py Tue Feb 10 20:13:17 2004
@@ -1,5 +1,6 @@
from sqlobject.dbconnection import DBAPI
kinterbasdb = None
+import re
class FirebirdConnection(DBAPI):
@@ -27,23 +28,13 @@
DBAPI.__init__(self, **kw)
def connectionFromURI(cls, uri):
- auth, password, host, path = self._parseURI(url)
+ auth, password, host, path = cls._parseURI(uri)
if not password:
password = 'masterkey'
if not auth:
auth='sysdba'
- return cls(host, db=path, user=user, passwd=password)
+ return cls(host, db=path, user=auth, passwd=password)
connectionFromURI = classmethod(connectionFromURI)
-
- def isSupported(cls):
- global kinterbasdb
- if kinterbasdb is None:
- try:
- import kinterbasdb
- except ImportError:
- return False
- return True
- isSupported = classmethod(isSupported)
def _runWithConnection(self, meth, *args):
conn = self.getConnection()
Modified: trunk/SQLObject/sqlobject/main.py
==============================================================================
--- trunk/SQLObject/sqlobject/main.py (original)
+++ trunk/SQLObject/sqlobject/main.py Tue Feb 10 20:13:17 2004
@@ -242,7 +242,7 @@
def findClass(name, registry=None):
#assert classRegistry.get(registry, {}).has_key(name), "No class by the name %s found (I have %s)" % (repr(name), ', '.join(map(str, classRegistry.keys())))
- return classRegistry[registry][name]
+ return classregistry.registry(registry).getClass(name)
def findDependencies(name, registry=None):
depends = []
@@ -652,7 +652,6 @@
def syncUpdate(self):
if not self._SO_createValues:
return
- print 'UP:', self._SO_createValues
self._SO_writeLock.acquire()
try:
if self._SO_columnDict:
@@ -1035,7 +1034,7 @@
def setConnection(cls, value):
if isinstance(value, (str, unicode)):
- value = dbconnection.openURI(value)
+ value = dbconnection.connectionForURI(value)
cls._connection = value
setConnection = classmethod(setConnection)
Modified: trunk/SQLObject/sqlobject/mysql/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/mysql/__init__.py (original)
+++ trunk/SQLObject/sqlobject/mysql/__init__.py Tue Feb 10 20:13:17 2004
@@ -1,5 +1,14 @@
-from sqlobject import dbconnection
-from mysqlconnection import MySQLConnection
+from sqlobject.dbconnection import registerConnection
-dbconnection.registerConnectionClass(
- MySQLConnection)
+def builder():
+ import mysqlconnection
+ return mysqlconnection.MySQLConnection
+
+def isSupported():
+ try:
+ import MySQLdb
+ except ImportError:
+ return False
+ return True
+
+registerConnection(['mysql'], builder, isSupported)
Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/mysql/mysqlconnection.py (original)
+++ trunk/SQLObject/sqlobject/mysql/mysqlconnection.py Tue Feb 10 20:13:17 2004
@@ -1,4 +1,5 @@
from sqlobject.dbconnection import DBAPI
+from sqlobject import col
MySQLdb = None
class MySQLConnection(DBAPI):
@@ -23,16 +24,6 @@
host=host or 'localhost')
connectionFromURI = classmethod(connectionFromURI)
- def isSupported(cls):
- global MySQLdb
- if MySQLdb is None:
- try:
- import MySQLdb
- except ImportError:
- return False
- return True
- isSupported = classmethod(isSupported)
-
def makeConnection(self):
return MySQLdb.connect(host=self.host, db=self.db,
user=self.user, passwd=self.passwd)
Modified: trunk/SQLObject/sqlobject/postgres/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/postgres/__init__.py (original)
+++ trunk/SQLObject/sqlobject/postgres/__init__.py Tue Feb 10 20:13:17 2004
@@ -1,5 +1,15 @@
-from sqlobject import dbconnection
-from pgconnection import PostgresConnection
+from sqlobject.dbconnection import registerConnection
-dbconnection.registerConnectionClass(
- PostgresConnection)
+def builder():
+ import pgconnection
+ return pgconnection.PostgresConnection
+
+def isSupported():
+ try:
+ import psycopg
+ except ImportError:
+ return False
+ return False
+
+registerConnection(['postgres', 'postgresql', 'psycopg'],
+ builder, isSupported)
Modified: trunk/SQLObject/sqlobject/postgres/pgconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/postgres/pgconnection.py (original)
+++ trunk/SQLObject/sqlobject/postgres/pgconnection.py Tue Feb 10 20:13:17 2004
@@ -1,4 +1,7 @@
from sqlobject.dbconnection import DBAPI
+import re
+from sqlobject import col
+from sqlobject import sqlbuilder
psycopg = None
pgdb = None
@@ -42,24 +45,16 @@
DBAPI.__init__(self, **kw)
def connectionFromURI(cls, uri):
- user, password, host, path = self._parseURI(uri)
+ user, password, host, path = cls._parseURI(uri)
+ path = path.strip('/')
return cls(host=host, db=path, user=user, passwd=password)
connectionFromURI = classmethod(connectionFromURI)
- def isSupported(cls):
- global psycopg
- if psycopg is None:
- try:
- import psycopg
- except ImportError:
- return False
- return False
- isSupported = classmethod(isSupported)
-
def _setAutoCommit(self, conn, auto):
conn.autocommit(auto)
def makeConnection(self):
+ print 'DSN', self.dsn
conn = self.pgmodule.connect(self.dsn)
if self.autoCommit:
conn.autocommit(1)
Modified: trunk/SQLObject/sqlobject/sqlite/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/sqlite/__init__.py (original)
+++ trunk/SQLObject/sqlobject/sqlite/__init__.py Tue Feb 10 20:13:17 2004
@@ -1,5 +1,14 @@
-from sqlobject import dbconnection
-from sqliteconnection import SQLiteConnection
+from sqlobject.dbconnection import registerConnection
-dbconnection.registerConnectionClass(
- SQLiteConnection)
+def builder():
+ import sqliteconnection
+ return sqliteconnection.SQLiteConnection
+
+def isSupported():
+ try:
+ import sqlite
+ except ImportError:
+ return False
+ return True
+
+registerConnection(['sqlite'], builder, isSupported)
Modified: trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py (original)
+++ trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py Tue Feb 10 20:13:17 2004
@@ -21,22 +21,12 @@
DBAPI.__init__(self, **kw)
def connectionFromURI(cls, uri):
- user, password, host, path = cls._parseURI(uri, expectHost=False)
- assert host is None
+ user, password, host, path = cls._parseURI(uri)
+ assert host is None, "SQLite can only be used locally (with a URI like sqlite:///file or sql:/file, not %r)" % uri
assert user is None and password is None, "You may not provide usernames or passwords for SQLite databases"
return cls(filename='/' + path)
connectionFromURI = classmethod(connectionFromURI)
- def isSupported(cls):
- global sqlite
- if sqlite is None:
- try:
- import sqlite
- except ImportError:
- return False
- return True
- isSupported = classmethod(isSupported)
-
def _setAutoCommit(self, conn, auto):
conn.autocommit = auto
Modified: trunk/SQLObject/sqlobject/sybase/__init__.py
==============================================================================
--- trunk/SQLObject/sqlobject/sybase/__init__.py (original)
+++ trunk/SQLObject/sqlobject/sybase/__init__.py Tue Feb 10 20:13:17 2004
@@ -1,5 +1,14 @@
-from sqlobject import dbconnection
-from sybaseconnection import SybaseConnection
+from sqlobject.dbconnection import registerConnection
-dbconnection.registerConnectionClass(
- SybaseConnection)
+def builder():
+ import sybaseconnection
+ return sybaseconnection.SybaseConnection
+
+def isSupported(cls):
+ try:
+ import Sybase
+ except ImportError:
+ return False
+ return True
+
+registerConnection(['sybase'], builder, isSupported)
Modified: trunk/SQLObject/sqlobject/sybase/sybaseconnection.py
==============================================================================
--- trunk/SQLObject/sqlobject/sybase/sybaseconnection.py (original)
+++ trunk/SQLObject/sqlobject/sybase/sybaseconnection.py Tue Feb 10 20:13:17 2004
@@ -31,16 +31,6 @@
db=path)
connectionFromURI = classmethod(connectionFromURI)
- def isSupported(cls):
- global Sybase
- if Sybase is None:
- try:
- import Sybase
- except ImportError:
- return False
- return True
- isSupported = classmethod(isSupported)
-
def insert_id(self, conn):
"""
Sybase adapter/cursor does not support the
Modified: trunk/SQLObject/tests/SQLObjectTest.py
==============================================================================
--- trunk/SQLObject/tests/SQLObjectTest.py (original)
+++ trunk/SQLObject/tests/SQLObjectTest.py Tue Feb 10 20:13:17 2004
@@ -1,6 +1,5 @@
import unittest
from sqlobject import *
-from sqlobject.dbconnection import openURI
import os
True, False = 1==1, 0==1
@@ -27,7 +26,7 @@
SQLObjectTest.supportAuto = True
SQLObjectTest.supportRestrictedEnum = True
SQLObjectTest.supportTransactions = True
- return 'postgres://localhost/test'
+ return 'postgres:///test'
def pygresConnection():
SQLObjectTest.supportDynamic = True
@@ -89,7 +88,7 @@
def setUp(self):
global __connection__
if isinstance(__connection__, str):
- __connection__ = openURI(__connection__)
+ __connection__ = connectionForURI(__connection__)
if self.debugSQL:
print
print '#' * 70
|