From 3500b3d1914241bfbc060954b55f05a8bb8e9d40 Mon Sep 17 00:00:00 2001 From: adustman Date: Sun, 23 Jun 2002 20:25:55 +0000 Subject: [PATCH] Some errorhandler cleanups --- MySQLdb/CHANGELOG | 4 ++ MySQLdb/MySQLdb/connections.py | 15 ++-- MySQLdb/MySQLdb/cursors.py | 123 +++++++++++++++++++++------------ 3 files changed, 90 insertions(+), 52 deletions(-) diff --git a/MySQLdb/CHANGELOG b/MySQLdb/CHANGELOG index 968d380..2249016 100644 --- a/MySQLdb/CHANGELOG +++ b/MySQLdb/CHANGELOG @@ -1,3 +1,7 @@ +0.9.2c2 + + * errorhandler cleanups (Rob Steele) + 0.9.2c1 * If using Python 2.2 or newer, the _mysql connection object is diff --git a/MySQLdb/MySQLdb/connections.py b/MySQLdb/MySQLdb/connections.py index fe76aef..33deef6 100644 --- a/MySQLdb/MySQLdb/connections.py +++ b/MySQLdb/MySQLdb/connections.py @@ -16,18 +16,20 @@ import types, _mysql def defaulterrorhandler(connection, cursor, errorclass, errorvalue): """ - If cursor is not None, errorvalue is appended to cursor.messages; - otherwise it is appended to connection.messages. Then errorclass - is raised with errorvalue as the value. + If cursor is not None, (errorclass, errorvalue) is appended to + cursor.messages; otherwise it is appended to + connection.messages. Then errorclass is raised with errorvalue as + the value. You can override this with your own error handler by assigning it to the instance. """ + error = errorclass, errorvalue if cursor: - cursor.messages.append(errorvalue) + cursor.messages.append(error) else: - connection.messages.append(errorvalue) + connection.messages.append(error) raise errorclass, errorvalue @@ -132,7 +134,8 @@ class Connection(ConnectionBase): if self._transactional: self.query("ROLLBACK") else: - raise NotSupportedError, "Not supported by server" + self.errorhandler(None, + NotSupportedError, "Not supported by server") def cursor(self, cursorclass=None): """ diff --git a/MySQLdb/MySQLdb/cursors.py b/MySQLdb/MySQLdb/cursors.py index 4e7ba47..47bafe0 100644 --- a/MySQLdb/MySQLdb/cursors.py +++ b/MySQLdb/MySQLdb/cursors.py @@ -17,7 +17,6 @@ if hasattr(exceptions, "StopIteration"): else: _EndOfData = exceptions.IndexError - class BaseCursor: """A base for Cursor classes. Useful attributes: @@ -40,24 +39,36 @@ class BaseCursor: self.lastrowid = None self.messages = [] self.errorhandler = connection.errorhandler - + self._result = None + def __del__(self): self.close() def close(self): """Close the cursor. No further queries will be possible.""" + if not self.connection: return + del self.messages[:] + self.nextset() self.connection = None self.errorhandler = None + self._result = None def _check_executed(self): if not self._executed: - self.errorhandler(self.connection, self, - ProgrammingError, "execute() first") + self.errorhandler(self, ProgrammingError, "execute() first") def nextset(self): - """Advance to the next result set. Returns None if there are - no more result sets. Note that MySQL does not support multiple - result sets at this time.""" + """Advance to the next result set. + + Returns None if there are no more result sets. + + Note that MySQL does not support multiple result sets at this + time. + + """ + del self.messages[:] + if self._executed: + self.fetchall() return None def setinputsizes(self, *args): @@ -68,32 +79,37 @@ class BaseCursor: def _get_db(self): if not self.connection: - self.errorhandler(self.connection, self, - ProgrammingError, "cursor closed") + self.errorhandler(self, ProgrammingError, "cursor closed") return self.connection - def execute(self, query, args=None): + def execute(self, query, args=()): """Execute a query. query -- string, query to execute on server args -- optional sequence or mapping, parameters to use with query. - returns long integer rows affected, if any""" + Note: If args is a sequence, then %s must be used as the + parameter placeholder in the query. If a mapping is used, + %(key)s must be used as the placeholder. + + Returns long integer rows affected, if any + + """ from types import ListType, TupleType - if args is None: - r = self._query(query) - elif type(args) is ListType and type(args[0]) is TupleType: - r = self.executemany(query, args) # deprecated - else: - try: - r = self._query(query % self.connection.literal(args)) - except TypeError, m: - if m.args[0] in ("not enough arguments for format string", - "not all arguments converted"): - self.errorhandler(self, ProgrammingError, m.args[0]) - else: - raise + from sys import exc_info + del self.messages[:] + try: + r = self._query(query % self.connection.literal(args)) + except TypeError, m: + if m.args[0] in ("not enough arguments for format string", + "not all arguments converted"): + self.errorhandler(self, ProgrammingError, m.args[0]) + else: + self.errorhandler(self, TypeError, m) + except: + e = exc_info() + self.errorhandler(self, e[0], e[1]) self._executed = query return r @@ -102,15 +118,22 @@ class BaseCursor: """Execute a multi-row query. query -- string, query to execute on server - args -- sequence of sequences or mappings, parameters to use with - query. The query must contain the clause "values ( ... )". - The parenthetical portion will be repeated once for each - item in the sequence. - returns long integer rows affected, if any - - This method performs multiple-row inserts and similar queries.""" + args + + Sequence of sequences or mappings, parameters to use with + query. + + Returns long integer rows affected, if any. + + This method improves performance on multiple-row INSERT and + REPLACE. Otherwise it is equivalent to looping over args with + execute(). + + """ from string import join + from sys import exc_info + del self.messages[:] if not args: return m = insert_values.search(query) if not m: @@ -127,10 +150,12 @@ class BaseCursor: except TypeError, msg: if msg.args[0] in ("not enough arguments for format string", "not all arguments converted"): - self.errorhandler(self.connection, self, - ProgrammingError, msg.args[0]) + self.errorhandler(self, ProgrammingError, msg.args[0]) else: - raise + self.errorhandler(self, TypeError, msg) + except: + e = exc_info() + self.errorhandler(self, e[0], e[1]) r = self._query(join(q,',\n')) self._executed = query return r @@ -145,8 +170,6 @@ class BaseCursor: self.rownumber = 0 self.description = self._result and self._result.describe() or None self.lastrowid = db.insert_id() - message = db.info() - if message: self.messages.append(message) self._check_for_warnings() return self.rowcount @@ -159,7 +182,7 @@ class BaseCursor: DEPRECATED: Use messages attribute""" self._check_executed() if self.messages: - return self.messages[0] + return self.messages[-1] else: return '' @@ -170,6 +193,8 @@ class BaseCursor: return self.lastrowid def _fetch_row(self, size=1): + if not self._result: + return () return self._result.fetch_row(size, self._fetch_type) def next(self): @@ -203,10 +228,12 @@ class CursorWarningMixIn: def _check_for_warnings(self): from string import atoi, split - if self.messages: - warnings = atoi(split(self.messages[0])[-1]) - if warnings: - raise Warning, self.messages[0] + info = self._get_db().info() + if info is None: + return + warnings = atoi(split(info)[-1]) + if warnings: + raise Warning, info class CursorStoreResultMixIn: @@ -225,12 +252,13 @@ class CursorStoreResultMixIn: def _query(self, q): rowcount = self._BaseCursor__do_query(q) - self._rows = self._result and self._fetch_row(0) or () - del self._result + self._rows = self._fetch_row(0) + self._result = None return rowcount def fetchone(self): - """Fetches a single row from the cursor.""" + """Fetches a single row from the cursor. None indicates that + no more rows are available.""" self._check_executed() if self.rownumber >= len(self._rows): return None result = self._rows[self.rownumber] @@ -284,9 +312,10 @@ class CursorStoreResultMixIn: elif mode == 'absolute': r = value else: - raise ProgrammingError, "unknown scroll mode %s" % `mode` + self.errorhandler(self, ProgrammingError, + "unknown scroll mode %s" % `mode`) if r < 0 or r >= len(self._rows): - raise IndexError, "out of range" + self.errorhandler(self, IndexError, "out of range") self.rownumber = r @@ -300,6 +329,8 @@ class CursorUseResultMixIn: def close(self): """Close the cursor. No further queries can be executed.""" + del self.messages[:] + self.nextset() self._result = None BaseCursor.close(self)