Version 0.9.2a1. Unicode + DB-API extensions.

This commit is contained in:
adustman
2001-12-24 01:23:55 +00:00
parent a18138516e
commit 7214eaa31e
6 changed files with 135 additions and 26 deletions

View File

@ -1 +1,6 @@
dist
build
regress.py
test.py
PKG-INFO
*.pyc *.pyc

View File

@ -1,4 +1,10 @@
post-0.9.1 0.9.1a1
* Added a number of DB-API extensions.
* Unicode instances can now be used as parameters to cursor.execute().
It attempts to use whatever character set MySQL is using. If that
can't be determined, then latin1 is used.
* Mac OS X configuration linkage fix (Dan Grassi) * Mac OS X configuration linkage fix (Dan Grassi)

View File

@ -7,7 +7,16 @@ override Connection.default_cursor with a non-standard Cursor class.
""" """
import cursors import cursors
from _mysql_exceptions import NotSupportedError, ProgrammingError from _mysql_exceptions import Warning, Error, InterfaceError, DataError, \
DatabaseError, OperationalError, IntegrityError, InternalError, \
NotSupportedError, ProgrammingError
def defaulterrorhandler(connection, cursor, errorclass, errorvalue):
if cursor:
cursor.messages.append(errorvalue)
else:
connection.messages.append(errorvalue)
raise errorclass, errorvalue
class Connection: class Connection:
@ -57,8 +66,11 @@ class Connection:
self.cursorclass = self.default_cursor self.cursorclass = self.default_cursor
self._db = apply(connect, args, kwargs2) self._db = apply(connect, args, kwargs2)
self._db.converter[types.StringType] = self._db.string_literal self._db.converter[types.StringType] = self._db.string_literal
if hasattr(types, 'UnicodeType'):
self._db.converter[types.UnicodeType] = self.unicode_literal
self._transactional = self._db.server_capabilities & CLIENT.TRANSACTIONS self._transactional = self._db.server_capabilities & CLIENT.TRANSACTIONS
self.messages = []
def __del__(self): def __del__(self):
if hasattr(self, '_db'): self.close() if hasattr(self, '_db'): self.close()
@ -102,6 +114,14 @@ class Connection:
import _mysql import _mysql
return _mysql.escape(o, self._db.converter) return _mysql.escape(o, self._db.converter)
def unicode_literal(self, u, dummy=None):
"""Convert a unicode object u to a string using the current
character set as the encoding. If that's not available,
use latin1."""
try: charset = self.character_set_name()
except: charset = 'latin1'
return self.literal(u.encode(charset))
def affected_rows(self): return self._db.affected_rows() def affected_rows(self): return self._db.affected_rows()
def dump_debug_info(self): return self._db.dump_debug_info() def dump_debug_info(self): return self._db.dump_debug_info()
def escape_string(self, s): return self._db.escape_string(s) def escape_string(self, s): return self._db.escape_string(s)
@ -124,8 +144,24 @@ class Connection:
try: try:
return apply(getattr(self._db, feature), args, kwargs) return apply(getattr(self._db, feature), args, kwargs)
except AttributeError: except AttributeError:
raise NotSupportedError, "not supported by MySQL library" self.errorhandler(self, None, NotSupportedError,
"not supported by MySQL library")
def character_set_name(self): def character_set_name(self):
return self._try_feature('character_set_name') return self._try_feature('character_set_name')
def change_user(self, *args, **kwargs): def change_user(self, *args, **kwargs):
return apply(self._try_feature, ('change_user',)+args, kwargs) return apply(self._try_feature, ('change_user',)+args, kwargs)
Warning = Warning
Error = Error
InterfaceError = InterfaceError
DatabaseError = DatabaseError
DataError = DataError
OperationalError = OperationalError
IntegrityError = IntegrityError
InternalError = InternalError
ProgrammingError = ProgrammingError
NotSupportedError = NotSupportedError
errorhandler = defaulterrorhandler

View File

@ -7,7 +7,16 @@ default, MySQLdb uses the Cursor class.
import re import re
insert_values = re.compile(r'values\s*(\(.+\))', re.IGNORECASE) insert_values = re.compile(r'values\s*(\(.+\))', re.IGNORECASE)
from _mysql import escape, ProgrammingError, Warning from _mysql_exceptions import Warning, Error, InterfaceError, DataError, \
DatabaseError, OperationalError, IntegrityError, InternalError, \
NotSupportedError, ProgrammingError
import exceptions
if hasattr(exceptions, "StopIteration"):
_EndOfData = exceptions.StopIteration
else:
_EndOfData = exceptions.IndexError
class BaseCursor: class BaseCursor:
@ -18,10 +27,9 @@ class BaseCursor:
See the MySQL docs for more information.""" See the MySQL docs for more information."""
from _mysql import MySQLError, Warning, Error, InterfaceError, \ from _mysql_exceptions import MySQLError, Warning, Error, InterfaceError, \
DatabaseError, DataError, OperationalError, \ DatabaseError, DataError, OperationalError, IntegrityError, \
IntegrityError, InternalError, ProgrammingError, \ InternalError, ProgrammingError, NotSupportedError
NotSupportedError
def __init__(self, connection): def __init__(self, connection):
self.connection = connection self.connection = connection
@ -30,6 +38,8 @@ class BaseCursor:
self.arraysize = 100 self.arraysize = 100
self._executed = None self._executed = None
self.lastrowid = None self.lastrowid = None
self.messages = []
self.errorhandler = connection.errorhandler
def __del__(self): def __del__(self):
self.close() self.close()
@ -41,7 +51,8 @@ class BaseCursor:
def _check_executed(self): def _check_executed(self):
if not self._executed: if not self._executed:
raise ProgrammingError, "execute() first" self.errorhandler(self.connection, self,
ProgrammingError, "execute() first")
def setinputsizes(self, *args): def setinputsizes(self, *args):
"""Does nothing, required by DB API.""" """Does nothing, required by DB API."""
@ -51,7 +62,8 @@ class BaseCursor:
def _get_db(self): def _get_db(self):
if not self.connection: if not self.connection:
raise ProgrammingError, "cursor closed" self.errorhandler(self.connection, self,
ProgrammingError, "cursor closed")
return self.connection._db return self.connection._db
def execute(self, query, args=None): def execute(self, query, args=None):
@ -73,7 +85,8 @@ class BaseCursor:
except TypeError, m: except TypeError, m:
if m.args[0] in ("not enough arguments for format string", if m.args[0] in ("not enough arguments for format string",
"not all arguments converted"): "not all arguments converted"):
raise ProgrammingError, m.args[0] self.errorhandler(self.connection, self,
ProgrammingError, m.args[0])
else: else:
raise raise
self._executed = query self._executed = query
@ -104,7 +117,8 @@ class BaseCursor:
except TypeError, msg: except TypeError, msg:
if msg.args[0] in ("not enough arguments for format string", if msg.args[0] in ("not enough arguments for format string",
"not all arguments converted"): "not all arguments converted"):
raise ProgrammingError, msg.args[0] self.errorhandler(self.connection, self,
ProgrammingError, msg.args[0])
else: else:
raise raise
r = self._query(join(q,',\n')) r = self._query(join(q,',\n'))
@ -118,9 +132,11 @@ class BaseCursor:
db.query(q) db.query(q)
self._result = self._get_result() self._result = self._get_result()
self.rowcount = db.affected_rows() self.rowcount = db.affected_rows()
self.rownumber = 0
self.description = self._result and self._result.describe() or None self.description = self._result and self._result.describe() or None
self.lastrowid = db.insert_id() self.lastrowid = db.insert_id()
self._info = db.info() message = db.info()
if message: self.messages.append(message)
self._check_for_warnings() self._check_for_warnings()
return self.rowcount return self.rowcount
@ -129,18 +145,45 @@ class BaseCursor:
_query = __do_query _query = __do_query
def info(self): def info(self):
"""Return some information about the last query (db.info())""" """Return some information about the last query (db.info())
DEPRECATED: Use messages attribute"""
self._check_executed() self._check_executed()
return self._info if self.messages:
return self.messages[0]
else:
return ''
def insert_id(self): def insert_id(self):
"""Return the last inserted ID on an AUTO_INCREMENT columns.""" """Return the last inserted ID on an AUTO_INCREMENT columns.
DEPRECATED: use lastrowid attribute"""
self._check_executed() self._check_executed()
return self.lastrowid return self.lastrowid
def _fetch_row(self, size=1): def _fetch_row(self, size=1):
return self._result.fetch_row(size, self._fetch_type) return self._result.fetch_row(size, self._fetch_type)
def next(self):
"""Fetches the next row. If using Python 2.1 or newer,
StopIteration is raised when there are no more rows.
Otherwise, IndexError is raised."""
result = self.fetchone()
if result is None:
raise _EndOfData
return result
def __iter__(self): return self # XXX
Warning = Warning
Error = Error
InterfaceError = InterfaceError
DatabaseError = DatabaseError
DataError = DataError
OperationalError = OperationalError
IntegrityError = IntegrityError
InternalError = InternalError
ProgrammingError = ProgrammingError
NotSupportedError = NotSupportedError
class CursorWarningMixIn: class CursorWarningMixIn:
@ -150,10 +193,10 @@ class CursorWarningMixIn:
def _check_for_warnings(self): def _check_for_warnings(self):
from string import atoi, split from string import atoi, split
if self._info: if self.messages:
warnings = atoi(split(self._info)[-1]) warnings = atoi(split(self.messages[0])[-1])
if warnings: if warnings:
raise Warning, self._info raise Warning, self.messages[0]
class CursorStoreResultMixIn: class CursorStoreResultMixIn:
@ -173,7 +216,6 @@ class CursorStoreResultMixIn:
def _query(self, q): def _query(self, q):
rowcount = self._BaseCursor__do_query(q) rowcount = self._BaseCursor__do_query(q)
self._rows = self._result and self._fetch_row(0) or () self._rows = self._result and self._fetch_row(0) or ()
self.rownumber = 0
del self._result del self._result
return rowcount return rowcount
@ -203,7 +245,7 @@ class CursorStoreResultMixIn:
def seek(self, row, whence=0): def seek(self, row, whence=0):
"""seek to a given row of the result set analogously to file.seek(). """seek to a given row of the result set analogously to file.seek().
This is non-standard extension.""" This is non-standard extension. DEPRECATED: Use scroll method"""
self._check_executed() self._check_executed()
if whence == 0: if whence == 0:
self.rownumber = row self.rownumber = row
@ -214,10 +256,29 @@ class CursorStoreResultMixIn:
def tell(self): def tell(self):
"""Return the current position in the result set analogously to """Return the current position in the result set analogously to
file.tell(). This is a non-standard extension.""" file.tell(). This is a non-standard extension. DEPRECATED:
use rownumber attribute"""
self._check_executed() self._check_executed()
return self.rownumber return self.rownumber
def scroll(self, value, mode='relative'):
"""Scroll the cursor in the result set to a new position according
to mode.
If mode is 'relative' (default), value is taken as offset to
the current position in the result set, if set to 'absolute',
value states an absolute target position."""
self._check_executed()
if mode == 'relative':
r = self.rownumber + value
elif mode == 'absolute':
r = value
else:
raise ProgrammingError, "unknown scroll mode %s" % `mode`
if r < 0 or r >= len(self._rows):
raise IndexError, "out of range"
self.rownumber = r
class CursorUseResultMixIn: class CursorUseResultMixIn:

View File

@ -1 +1,2 @@
*.tex
*.html *.html

View File

@ -56,7 +56,7 @@ elif sys.platform == "win32": # Ugh
libraries = [mysqlclient, 'zlib', 'msvcrt', 'libcmt', libraries = [mysqlclient, 'zlib', 'msvcrt', 'libcmt',
'wsock32', 'advapi32'] 'wsock32', 'advapi32']
extra_objects = [r'c:\mysql\lib\opt\mysqlclient.lib'] extra_objects = [r'c:\mysql\lib\opt\mysqlclient.lib']
elif sys.platform == "darwin1": # Mac OS X elif sys.platform[:6] == "darwin": # Mac OS X
include_dirs = ['/usr/local/mysql/include/mysql'] include_dirs = ['/usr/local/mysql/include/mysql']
library_dirs = ['/usr/local/mysql/lib/mysql'] library_dirs = ['/usr/local/mysql/lib/mysql']
extra_link_args = ['-flat_namespace'] extra_link_args = ['-flat_namespace']
@ -88,7 +88,7 @@ MySQLdb. MySQLdb is free software.
setup (# Distribution meta-data setup (# Distribution meta-data
name = "MySQL-python", name = "MySQL-python",
version = "0.9.1", version = "0.9.2",
description = "An interface to MySQL", description = "An interface to MySQL",
long_description=long_description, long_description=long_description,
author = "Andy Dustman", author = "Andy Dustman",