Author: phd
Date: Mon Feb 21 11:48:40 2011
New Revision: 4335
Log:
DB URI parser was changed to use urllib.split*() and unquote().
Added:
SQLObject/trunk/scripts/sqlobject-convertURI (contents, props changed)
SQLObject/trunk/sqlobject/tests/test_parse_uri.py (contents, props changed)
- copied, changed from r4332, SQLObject/trunk/sqlobject/tests/test_parse.py
Deleted:
SQLObject/trunk/sqlobject/tests/test_parse.py
Modified:
SQLObject/trunk/docs/News.txt
SQLObject/trunk/docs/SQLObject.txt
SQLObject/trunk/docs/TODO.txt
SQLObject/trunk/setup.py
SQLObject/trunk/sqlobject/dbconnection.py
SQLObject/trunk/sqlobject/firebird/firebirdconnection.py
SQLObject/trunk/sqlobject/maxdb/maxdbconnection.py
SQLObject/trunk/sqlobject/mssql/mssqlconnection.py
SQLObject/trunk/sqlobject/mysql/mysqlconnection.py
SQLObject/trunk/sqlobject/postgres/pgconnection.py
SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py
SQLObject/trunk/sqlobject/sybase/sybaseconnection.py
Modified: SQLObject/trunk/docs/News.txt
==============================================================================
--- SQLObject/trunk/docs/News.txt Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/docs/News.txt Mon Feb 21 11:48:40 2011 (r4335)
@@ -13,6 +13,11 @@
Features & Interface
--------------------
+* Major API change: DB URI parser was changed to use urllib.split*() and
+ unquote(). This means any username/password/path are allowed in DB
+ URIs if they are properly %-encoded, and DB URIs are automatically
+ unquoted.
+
* In SQLite, id columns are made AUTOINCREMENT.
* Parameter ``backend`` in DB URI is no longer supported, use parameter
Modified: SQLObject/trunk/docs/SQLObject.txt
==============================================================================
--- SQLObject/trunk/docs/SQLObject.txt Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/docs/SQLObject.txt Mon Feb 21 11:48:40 2011 (r4335)
@@ -152,13 +152,9 @@
postgres:///full/path/to/socket/database
postgres://host:5432/database
sqlite:///full/path/to/database
- sqlite:/C|/full/path/to/database
+ sqlite:/C:/full/path/to/database
sqlite:/:memory:
-Note that there is a special syntax to specify full path on w32 systems
-(useable for sqlite and firebird); also sqlite can manipulate special
-in-memory databases.
-
Parameters are: ``debug`` (default: False), ``debugOutput`` (default: False),
``cache`` (default: True), ``autoCommit`` (default: True),
``debugThreading`` (default: False),
Modified: SQLObject/trunk/docs/TODO.txt
==============================================================================
--- SQLObject/trunk/docs/TODO.txt Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/docs/TODO.txt Mon Feb 21 11:48:40 2011 (r4335)
@@ -1,8 +1,6 @@
TODO
----
-* Proper %-encoded URI.
-
* __version__.py
* Release 1.0.
@@ -80,6 +78,8 @@
* Better joins - automatic joins in .select()
based on ForeignKey/MultipleJoin/RelatedJoin.
+* Deprecate, then remove connectionForOldURI.
+
* Python 3.0+.
* Switch from setuptools to distribute.
Added: SQLObject/trunk/scripts/sqlobject-convertURI
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ SQLObject/trunk/scripts/sqlobject-convertURI Mon Feb 21 11:48:40 2011 (r4335)
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import sys
+
+try:
+ uri = sys.argv[1]
+except IndexError:
+ sys.exit("Usage: %s old-style-URI" % sys.argv[0])
+
+try:
+ import pkg_resources
+ pkg_resources.require('SQLObject>=1.0.0a1')
+except (ImportError, pkg_resources.DistributionNotFound):
+ pass
+
+from sqlobject.dbconnection import dbConnectionForScheme
+
+scheme = uri.split(':')[0]
+connCls = dbConnectionForScheme(scheme)
+conn = connCls.connectionFromOldURI(uri)
+print conn.uri()
Modified: SQLObject/trunk/setup.py
==============================================================================
--- SQLObject/trunk/setup.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/setup.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -52,7 +52,7 @@
download_url="http://cheeseshop.python.org/pypi/SQLObject/1.0",
license="LGPL",
packages=["sqlobject"] + ['sqlobject.%s' % package for package in subpackages],
- scripts=["scripts/sqlobject-admin"],
+ scripts=["scripts/sqlobject-admin", "scripts/sqlobject-convertURI"],
install_requires=["FormEncode>=1.1.1"],
extras_require={
'mysql': ['MySQLdb'],
Modified: SQLObject/trunk/sqlobject/dbconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/dbconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/dbconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -1,6 +1,8 @@
import atexit
+from cgi import parse_qsl
import inspect
import new
+import os
import sys
import threading
import types
@@ -87,7 +89,7 @@
atexit.register(_closeConnection, weakref.ref(self))
def uri(self):
- auth = getattr(self, 'user', '')
+ auth = getattr(self, 'user', '') or ''
if auth:
if self.password:
auth = auth + ':' + self.password
@@ -98,17 +100,23 @@
uri = '%s://%s' % (self.dbName, auth)
if self.host:
uri += self.host
+ if self.port:
+ uri += ':%d' % self.port
uri += '/'
db = self.db
if db.startswith('/'):
db = path[1:]
return uri + db
+ def connectionFromOldURI(cls, uri):
+ return cls._connectionFromParams(*cls._parseOldURI(uri))
+ connectionFromOldURI = classmethod(connectionFromOldURI)
+
def connectionFromURI(cls, uri):
- raise NotImplemented
+ return cls._connectionFromParams(*cls._parseURI(uri))
connectionFromURI = classmethod(connectionFromURI)
- def _parseURI(uri):
+ def _parseOldURI(uri):
schema, rest = uri.split(':', 1)
assert rest.startswith('/'), "URIs must start with scheme:/ -- you did not include a / (in %r)" % rest
if rest.startswith('/') and not rest.startswith('//'):
@@ -156,6 +164,42 @@
argvalue = urllib.unquote(argvalue)
args[argname] = argvalue
return user, password, host, port, path, args
+ _parseOldURI = staticmethod(_parseOldURI)
+
+ def _parseURI(uri):
+ protocol, request = urllib.splittype(uri)
+ user, password, port = None, None, None
+ host, path = urllib.splithost(request)
+
+ if host:
+ user, host = urllib.splituser(host)
+ if user:
+ user, password = urllib.splitpasswd(user)
+ host, port = urllib.splitport(host)
+ if port: port = int(port)
+ elif host == '':
+ host = None
+
+ # hash-tag is splitted but ignored
+ path, tag = urllib.splittag(path)
+ path, query = urllib.splitquery(path)
+
+ path = urllib.unquote(path)
+ if (os.name == 'nt') and (len(path) > 2):
+ # Preserve backward compatibility with URIs like /C|/path;
+ # replace '|' by ':'
+ if path[2] == '|':
+ path = "%s:%s" % (path[0:2], path[3:])
+ # Remove leading slash
+ if (path[0] == '/') and (path[2] == ':'):
+ path = path[1:]
+
+ args = {}
+ if query:
+ for name, value in parse_qsl(query):
+ args[name] = value
+
+ return user, password, host, port, path, args
_parseURI = staticmethod(_parseURI)
def soClassAdded(self, soClass):
Modified: SQLObject/trunk/sqlobject/firebird/firebirdconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/firebird/firebirdconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/firebird/firebirdconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -11,13 +11,14 @@
limit_re = re.compile('^\s*(select )(.*)', re.IGNORECASE)
- def __init__(self, host, db, user='sysdba',
+ def __init__(self, host, port, db, user='sysdba',
password='masterkey', autoCommit=1,
dialect=None, role=None, charset=None, **kw):
import kinterbasdb
self.module = kinterbasdb
self.host = host
+ self.port = port
self.db = db
self.user = user
self.password = password
@@ -30,8 +31,7 @@
DBAPI.__init__(self, **kw)
- def connectionFromURI(cls, uri):
- auth, password, host, port, path, args = cls._parseURI(uri)
+ def _connectionFromParams(cls, auth, password, host, port, path, args):
if not password:
password = 'masterkey'
if not auth:
@@ -40,8 +40,8 @@
if (path[0] == '/') and path[-3:].lower() not in ('fdb', 'gdb'):
path = path[1:]
path = path.replace('/', os.sep)
- return cls(host, db=path, user=auth, password=password, **args)
- connectionFromURI = classmethod(connectionFromURI)
+ return cls(host, port, db=path, user=auth, password=password, **args)
+ _connectionFromParams = classmethod(_connectionFromParams)
def _runWithConnection(self, meth, *args):
if not self.autoCommit:
Modified: SQLObject/trunk/sqlobject/maxdb/maxdbconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/maxdb/maxdbconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/maxdb/maxdbconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -59,27 +59,28 @@
dbName = 'maxdb'
schemes = [dbName]
- def __init__ (self, user, password, database,
- host='', autoCommit=1, sqlmode='internal',
+ def __init__ (self, host='', port=None, user=None, password=None,
+ database=None, autoCommit=1, sqlmode='internal',
isolation=None, timeout=None, **kw):
from sapdb import dbapi
self.module = dbapi
- self.autoCommit = autoCommit
+ self.host = host
+ self.port = port
self.user = user
self.password = password
self.database = database
- self.host = host
+ self.autoCommit = autoCommit
self.sqlmode = sqlmode
self.isolation = isolation
self.timeout = timeout
DBAPI.__init__(self, **kw)
- def connectionFromURI(cls, uri):
- auth, password, host, port, path, args = cls._parseURI(uri)
+ def _connectionFromParams(cls, auth, password, host, port, path, args):
path = path.replace('/', os.path.sep)
- return cls(host, db=path, user=auth, password=password, **args)
- connectionFromURI = classmethod(connectionFromURI)
+ return cls(host, port, user=auth, password=password,
+ database=path, **args)
+ _connectionFromParams = classmethod(_connectionFromParams)
def _getConfigParams(self,sqlmode,auto):
autocommit='off'
Modified: SQLObject/trunk/sqlobject/mssql/mssqlconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/mssql/mssqlconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/mssql/mssqlconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -8,7 +8,7 @@
dbName = 'mssql'
schemes = [dbName]
- def __init__(self, db, user, password='', host='localhost',
+ def __init__(self, db, user, password='', host='localhost', port=None,
autoCommit=0, **kw):
drivers = kw.pop('driver', None) or 'adodb,pymssql'
for driver in drivers.split(','):
@@ -64,6 +64,7 @@
self.autoCommit=int(autoCommit)
self.host = host
+ self.port = port
self.db = db
self.user = user
self.password = password
@@ -72,12 +73,11 @@
self._can_use_max_types = None
DBAPI.__init__(self, **kw)
- def connectionFromURI(cls, uri):
- user, password, host, port, path, args = cls._parseURI(uri)
+ def _connectionFromParams(cls, user, password, host, port, path, args):
path = path.strip('/')
- return cls(user=user, password=password, host=host or 'localhost',
- db=path, **args)
- connectionFromURI = classmethod(connectionFromURI)
+ return cls(user=user, password=password,
+ host=host or 'localhost', port=port, db=path, **args)
+ _connectionFromParams = classmethod(_connectionFromParams)
def insert_id(self, conn):
"""
Modified: SQLObject/trunk/sqlobject/mysql/mysqlconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/mysql/mysqlconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/mysql/mysqlconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -50,11 +50,10 @@
DBAPI.__init__(self, **kw)
- def connectionFromURI(cls, uri):
- user, password, host, port, path, args = cls._parseURI(uri)
+ def _connectionFromParams(cls, user, password, host, port, path, args):
return cls(db=path.strip('/'), user=user or '', password=password or '',
host=host or 'localhost', port=port or 0, **args)
- connectionFromURI = classmethod(connectionFromURI)
+ _connectionFromParams = classmethod(_connectionFromParams)
def makeConnection(self):
dbEncoding = self.dbEncoding
Modified: SQLObject/trunk/sqlobject/postgres/pgconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/postgres/pgconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/postgres/pgconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -106,15 +106,14 @@
self.dbEncoding = kw.pop("charset", None)
DBAPI.__init__(self, **kw)
- def connectionFromURI(cls, uri):
- user, password, host, port, path, args = cls._parseURI(uri)
+ def _connectionFromParams(cls, user, password, host, port, path, args):
path = path.strip('/')
if (host is None) and path.count('/'): # Non-default unix socket
path_parts = path.split('/')
host = '/' + '/'.join(path_parts[:-1])
path = path_parts[-1]
return cls(host=host, port=port, db=path, user=user, password=password, **args)
- connectionFromURI = classmethod(connectionFromURI)
+ _connectionFromParams = classmethod(_connectionFromParams)
def _setAutoCommit(self, conn, auto):
# psycopg2 does not have an autocommit method.
Modified: SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/sqlite/sqliteconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -95,25 +95,27 @@
self._memoryConn = sqlite.connect(
self.filename, **self._connOptions)
- def connectionFromURI(cls, uri):
- user, password, host, port, path, args = cls._parseURI(uri)
- assert host is None, (
+ def _connectionFromParams(cls, user, password, host, port, path, args):
+ assert host is None and port is None, (
"SQLite can only be used locally (with a URI like "
- "sqlite:///file or sqlite:/file, not %r)" % uri)
+ "sqlite:/file or sqlite:///file, not sqlite://%s%s)" %
+ (host, ':%r' % port if port else ''))
assert user is None and password is None, (
"You may not provide usernames or passwords for SQLite "
"databases")
if path == "/:memory:":
path = ":memory:"
return cls(filename=path, **args)
- connectionFromURI = classmethod(connectionFromURI)
+ _connectionFromParams = classmethod(_connectionFromParams)
def uri(self):
path = self.filename
if path == ":memory:":
path = "/:memory:"
- else:
+ elif path.startswith('/'):
path = "//" + path
+ else:
+ path = "///" + path
return 'sqlite:%s' % path
def getConnection(self):
Modified: SQLObject/trunk/sqlobject/sybase/sybaseconnection.py
==============================================================================
--- SQLObject/trunk/sqlobject/sybase/sybaseconnection.py Mon Feb 21 11:46:11 2011 (r4334)
+++ SQLObject/trunk/sqlobject/sybase/sybaseconnection.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -8,7 +8,7 @@
schemes = [dbName]
NumericType = None
- def __init__(self, db, user, password='', host='localhost',
+ def __init__(self, db, user, password='', host='localhost', port=None,
locking=1, **kw):
db = db.strip('/')
import Sybase
@@ -21,6 +21,7 @@
self.module = Sybase
self.locking = int(locking)
self.host = host
+ self.port = port
self.db = db
self.user = user
self.password = password
@@ -32,11 +33,10 @@
kw['autoCommit'] = autoCommit
DBAPI.__init__(self, **kw)
- def connectionFromURI(cls, uri):
- user, password, host, port, path, args = cls._parseURI(uri)
- return cls(user=user, password=password, host=host or 'localhost',
- db=path, **args)
- connectionFromURI = classmethod(connectionFromURI)
+ def _connectionFromParams(cls, user, password, host, port, path, args):
+ return cls(user=user, password=password,
+ host=host or 'localhost', port=port, db=path, **args)
+ _connectionFromParams = classmethod(_connectionFromParams)
def insert_id(self, conn):
"""
Copied and modified: SQLObject/trunk/sqlobject/tests/test_parse_uri.py (from r4332, SQLObject/trunk/sqlobject/tests/test_parse.py)
==============================================================================
--- SQLObject/trunk/sqlobject/tests/test_parse.py Fri Feb 11 02:44:20 2011 (r4332, copy source)
+++ SQLObject/trunk/sqlobject/tests/test_parse_uri.py Mon Feb 21 11:48:40 2011 (r4335)
@@ -18,7 +18,7 @@
user, password, host, port, path, args = _parseURI("mysql://user:pass%20word@host/database?unix_socket=/var/mysql/socket")
assert user == "user"
- assert password == "pass%20word"
+ assert password == "pass word"
assert host == "host"
assert port is None
assert path == "/database"
@@ -41,9 +41,9 @@
assert args == {}
user, password, host, port, path, args = _parseURI("postgres:///full/path/to/socket/database")
- assert user == None
+ assert user is None
assert password is None
- assert host == None
+ assert host is None
assert port is None
assert path == "/full/path/to/socket/database"
assert args == {}
@@ -78,5 +78,13 @@
assert password is None
assert host is None
assert port is None
- assert path == "C:/full/path/to/database"
+ assert path == "/C:/full/path/to/database"
+ assert args == {}
+
+ user, password, host, port, path, args = _parseURI("sqlite:///C:/full/path/to/database")
+ assert user is None
+ assert password is None
+ assert host is None
+ assert port is None
+ assert path == "/C:/full/path/to/database"
assert args == {}
|