# -*- coding: iso-8859-1 -*-
#-----------------------------------------------------------------------------
# Modeling Framework: an Object-Relational Bridge for python
#
# Copyright (c) 2001-2004 Sbastien Bigaret <sbigaret@users.sourceforge.net>
# All rights reserved.
#
# This file is part of the Modeling Framework.
#
# This code is distributed under a "3-clause BSD"-style license;
# see the LICENSE file for details.
#-----------------------------------------------------------------------------
"""
AbstractDBAPI2AdaptorContext
This module contains the AbstractDBAPI2AdaptorContext class to be inherited
from when designing an concrete AdaptorContext based on a python
database-adaptor complying to the python DB-API v2.0.
CVS information
$Id: AbstractDBAPI2AdaptorContext.py 934 2004-08-02 20:11:39Z sbigaret $
"""
__version__='$Revision: 934 $'[11:-2]
from Modeling.logging import db_info,db_trace,db_debug,db_fatal
# Framework
from Modeling.AdaptorContext import AdaptorContext
from Modeling.Adaptor import GeneralAdaptorException
# Interfaces
from Modeling.interfaces.AdaptorContext import IAdaptorContext
import os
class AbstractDBAPI2AdaptorContext(AdaptorContext):
"""
Your inheriting concrete Adaptor class **must** override:
- createAdaptorChannel()
You normally do not need to override other methods. In particular,
dbAPI_cursorForAdaptorChannel() does not need to be overridden.
"""
__implements__ = (IAdaptorContext,)
def __init__(self, anAdaptor):
"""
Initialization. You do normally not need to override this method. If you
do, you **must** explicitly call the superclass' implementation.
"""
AdaptorContext.__init__.im_func(self, anAdaptor)
self._cnx=None
def adaptorChannelDidClose(self, aChannel):
"""
Informs the AdaptorContext that one of its AdaptorChannel has been closed.
Never invoke this method yourself, it is automatically called by the
AdaptorChannel.
Default behaviour leaves the connection to the database open, even when
the AdaptorContext has no opened channels ; setting the environment
variable 'MDL_TRANSIENT_DB_CONNECTION' to any true value closes the
database connection the AdaptorContext has no opened channels anymore.
You do not need to override this method when designing a concrete
AdaptorContext. If you do, however, you must call the superclass
implementation, and you must keep in mind that the underlying database
connection may have been closed as well.
Parameter 'aChannel' is currently unused.
See also: AbstractDBAPI2AdaptorChannel.closeChannel()
Modeling.AdaptorChannel.closeChannel()
"""
db_debug('Adaptor channel %s did close'%repr(aChannel))
if not self.hasOpenChannels():
## Close the connexion
db_debug('Channels are now all closed')
if self._cnx and os.environ.get('MDL_TRANSIENT_DB_CONNECTION'):
db_info('Closing the connection to the database')
self._cnx.close()
self._cnx=None
def beginTransaction(self):
"""
Starts a new transaction.
Raises GeneralAdaptorException if an transaction has already begun.
See also: Modeling.interfaces.AdaptorContext for details
"""
db_info('Transaction: BEGIN')
if self.hasOpenTransaction():
raise GeneralAdaptorException, 'Another transaction has already begun'
# pgdb automatically begins a transaction
self._openConnectionIfNecessary()
self.transactionDidBegin()
def commitTransaction(self):
"""
Commits the currently opened transaction.
Raises GeneralAdaptorException if no transaction has begun.
See also: Modeling.interfaces.AdaptorContext for details
"""
db_info('Transaction: COMMIT')
if not self.hasOpenTransaction():
raise GeneralAdaptorException, 'No transaction begun'
self._cnx.commit()
self.transactionDidCommit()
def createAdaptorChannel(self):
"""
Returns a brand new instance of the adequate concrete AdaptorChannel.
Implementation hint: say your adaptor's name is 'Dummy', then you'll
probably use the following code::
from DummyAdaptorChannel import DummyAdaptorChannel
db_trace('Creating DummyAdaptorChannel %s'%repr(channel))
channel=DummyAdaptorChannel(self)
self.__addChannel__(channel)
return channel
--> It is very important that you do not forget to append the new
adaptor channel to the list of held channels, using the
__addChannel__ method.
Subclasses should override this method without calling the superclass'
implementation.
See also: Modeling.interfaces.AdaptorContext for details
"""
__abstract__()
#def hasOpenTransaction(self):
# "See Modeling.interfaces.AdaptorContext for details"
# db_trace('-')
# raise 'Unimplemented'
# #return self._hasOpenTransaction
def rollbackTransaction(self):
"""
Rollbacks the currently opened transaction.
Raises GeneralAdaptorException if no transaction has begun.
See also:
Modeling.interfaces.AdaptorContext for details
"""
db_info('Transaction: ROLLBACK')
if not self.hasOpenTransaction():
raise GeneralAdaptorException, 'No transaction begun'
self._cnx.rollback()
self.transactionDidRollback()
#def transactionDidBegin(self):
# "See Modeling.interfaces.AdaptorContext for details"
# AdaptorContext.transactionDidBegin.im_func(self)
#
#def transactionDidCommit(self):
# "See Modeling.interfaces.AdaptorContext for details"
# AdaptorContext.transactionDidCommit.im_func(self)
#
#def transactionDidRollback(self):
# "See Modeling.interfaces.AdaptorContext for details"
# AdaptorContext.transactionDidRollback.im_func(self)
## private & specific AbstractDBAPI2AdaptorLayer method
def dbAPI_cursorForAdaptorChannel(self):
"""
Returns a 'cursor' for use by an AbstractDBAPI2AdaptorChannel when it is
about to open the channel. Never invoke this method, this is automatically
invoked by the AbstractDBAPI2AdaptorChannel.
See also: AbstractDBAPI2AdaptorChannel.openChannel()
"""
db_trace('Called')
self._openConnectionIfNecessary()
return self._cnx.cursor()
def _openConnectionIfNecessary(self):
"""
Internally used to open a connection to the underlying database, if
necessary.
Raises GeneralAdaptorException if the connection cannot be opened --in
this case the reason why it failed is attached to the exception.
"""
if not self._cnx:
cnxDict=self._adaptor.connectionDictionary()
dict_for_info=cnxDict.copy() ; dict_for_info['password']='xxxx'
db_info('Opening connection to the DB with conn.Dict: %s'%str(dict_for_info))
try:
self._cnx=apply(self.adaptor().underlying_py_adaptor_module().connect,
(),
self._adaptor.dbAPI_connectionDictionaryForConnect(cnxDict))
except:
import StringIO, traceback
exc=StringIO.StringIO()
traceback.print_exc(file=exc)
reason=exc.getvalue()
del exc
db_fatal('Unable to open connexion w/ connectionDictionary=%s\nReason:\n%s'%(dict_for_info, reason))
raise GeneralAdaptorException, 'Unable to open connexion w/ connectionDictionary=%s\nReason:\n%s'%(dict_for_info, reason)
def __abstract__():
raise 'Unimplemented', 'abstract method, should be implemented in subclasses'
|