# -*- 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.
#-----------------------------------------------------------------------------
"""
Adaptor
CVS information
$Id: Adaptor.py 932 2004-07-20 06:21:57Z sbigaret $
"""
__version__='$Revision: 932 $'[11:-2]
from inspect import isclass
from delegation import DelegateWrapper
# Interfaces
from Modeling.interfaces.Adaptor import IAdaptor
# Framework
from utils import staticmethod,getModule
## Do not access these variables directly -- they are private (MT-safety)
## This holds the adaptors' classes
__adaptorsClasses={} # adaptorName: adaptorClass
## This is for expressionClass
__expressionsClasses={} # adaptorClass: expressionClass
## default delegate
__defaultDelegate=None
# Global reentrant lock
from threading import RLock
__adaptorClass_lock=RLock()
lock=__adaptorClass_lock.acquire
unlock=__adaptorClass_lock.release
# Notifications
AdaptorNeededForNameNotification='AdaptorNeededForNameNotification'
#Exceptions
class AdaptorImportError(ImportError):
pass
#
class GeneralAdaptorException(RuntimeError):
pass
class CannotSerializeException(GeneralAdaptorException):
pass
# module functions / Adaptor ``static'' methods
def adaptorWithModel(aModel):
"""
Returns the adaptor associated to the supplied model: it extracts the name
of the adaptor to be used with the model then calls adaptorWithModel()
Raises ValueError is parameter 'aModel' is None or if its adaptorName
is empty.
Raises AdaptorImportError if the adaptor cannot be loaded.
"""
lock()
try:
if not aModel or not aModel.adaptorName():
raise ValueError, "Supplied model is null or its adaptor name is empty"
adaptorName=aModel.adaptorName()
adaptor=adaptorWithName(adaptorName)
adaptor.setConnectionDictionary(aModel.connectionDictionary())
return adaptor
finally:
unlock()
def adaptorWithName(aName):
"""
Returns the adaptor.
Raises AdaptorImportError if the adaptor cannot be loaded.
"""
if not aName:
raise ValueError, "Parameter 'aName' is null or empty"
lock()
try:
global __adaptorsClasses
if __adaptorsClasses.get(aName, None):
return __adaptorsClasses[aName](aName)
from Modeling import DatabaseAdaptors
# try to import the module
adaptorModuleName=aName+'AdaptorLayer'
try:
path=getattr(DatabaseAdaptors,'__path__', None)
_module=getModule(adaptorModuleName, path)
except:
import traceback, cStringIO
exc=cStringIO.StringIO()
traceback.print_exc(file=exc)
raise AdaptorImportError, "Unable to import adaptor for name '%s': "\
"module %s couldn't be imported\nReason:%s"%(aName,
adaptorModuleName,
exc.getvalue())
if not hasattr(_module, 'adaptorFactory'):
raise AdaptorImportError, "Imported module %s does not define the "\
"method 'adaptorFactory()'"%(adaptorModuleName,)
_factory=getattr(_module, 'adaptorFactory')
_adaptorClass=None
if callable(_factory): _adaptorClass=_factory()
else: _adaptorClass=_factory
__adaptorsClasses[aName]=_adaptorClass
return _adaptorClass(aName)
finally:
unlock()
def defaultDelegate():
"""
Returns the default delegate.
See also: setDefaultDelegate()
"""
lock()
try:
return __defaultDelegate
finally:
unlock()
def expressionClassForAdaptor(adaptor):
"""
Returns the expression class to be used by adaptor, or None if none was
previously set with 'setExpressionClassForAdaptor()'.
"""
lock()
try:
global __expressionsClasses
if not isclass(adaptor):
adaptor=adaptor.__class__
return __expressionsClasses.get(adaptor, None)
finally:
unlock()
def setDefaultDelegate(delegate):
"""
Sets the default delegate which is assigned to Adaptor instances when they
are initialized.
"""
lock()
try:
global __defaultDelegate
__defaultDelegate=delegate
finally:
unlock()
def setExpressionClassForAdaptor(sqlExpression, adaptor):
"""
Sets the SQLExpression to use in conjonction with a particular adaptor.
Parameters 'sqlExpression' and 'adaptor' can be either instances or classes.
"""
lock()
try:
global __expressionsClasses
if not isclass(sqlExpression):
sqlExpression=sqlExpression.__class__
if not isclass(adaptor):
adaptor=adaptor.__class__
__expressionsClasses[adaptor]=sqlExpression
finally:
unlock()
class Adaptor:
"See interfaces.Adaptor for details"
__implements__ = (IAdaptor,)
__raise_exception__='AbstractMethod'
__raise_msg__ ='Left intentionally unimplemented in this class, '\
'subclass should override this method'
## Attention: il y en a qui vont tre compltement rcrites et d'autres qui
## doivent faire appel l'implmentation de cette classe, donc bon.
def __init__(self, name):
"See interfaces.Adaptor for details"
self._name=name
self._connectionDictionary={}
self._contexts=[]
#self._delegate=DelegateWrapper(, defaultDelegate())
# Static methods
adaptorWithModel=staticmethod(adaptorWithModel)
adaptorWithName=staticmethod(adaptorWithName)
defaultDelegate=staticmethod(defaultDelegate)
expressionClassForAdaptor=staticmethod(expressionClassForAdaptor)
setDefaultDelegate=staticmethod(setDefaultDelegate)
setExpressionClassForAdaptor=staticmethod(setExpressionClassForAdaptor)
# Instance methods
def administrativeConnectionDictionaryForAdaptor(self, anAdaptor):
"See interfaces.Adaptor for details"
def assertConnectionDictionaryIsValid(self):
"See interfaces.Adaptor for details"
def assignExternalInfoForAttribute(self, anAttribute):
"See interfaces.Adaptor for details"
def assignExternalInfoForEntireModel(self, aModel):
"See interfaces.Adaptor for details"
def assignExternalInfoForEntity(self, anEntity):
"See interfaces.Adaptor for details"
def assignExternalTypeForAttribute(self, anAttribute):
"See interfaces.Adaptor for details"
def canServiceModel(self, aModel):
"""
Compares 'aModel' 's connectionDictionary with self's and returns the
result
"""
return aModel.connectionDictionary()!=self._connectionDictionary
def connectionDictionary(self):
"""
Returns the connectionDictionary
See also: setConnectionDictionary()
"""
return self._connectionDictionary
def contexts(self):
"""
Returns all AdaptorContexts created and registered with this Adaptor
See also: createAdaptorContext()
"""
return tuple(self._contexts)
def createAdaptorContext(self):
"""
Instanciates a new AdaptorContext, registers it in the list of contexts()
then returns it.
abstract
Subclasses should override this method to add the appropriate
AdaptorContext to the private attribute 'self._contexts' and return it,
without calling this method.
"""
raise __raise_exception__, __raise_msg__
def createDatabaseWithAdministrativeConnectionDictionary(self, administrativeConnectionDictionary):
"""
Connects to the database with the supplied connection dictionary and
creates the database specified by the Adaptor's connectionDictionary()
Adaptor's implementation does nothing.
Subclasses should implement this method without calling this.
"""
return
def defaultExpressionClass(self):
"""
Returns the default expression class to be used along with this adaptor.
See 'expressionClass()' for an explanation on how the expression class
is actually determined for an Adaptor.
Subclasses should override this method without calling Adaptor's.
"""
__abstract__()
def delegate(self):
"""
Returns the delegate for that Adaptor ; when initialized the delegate is
the one returned by defaultDelegate()
"""
return self._delegate
def dropDatabaseWithAdministrativeConnectionDictionary(self, administrativeConnectionDictionary):
"Adaptor's implementation does nothing"
return
def expressionClass(self):
"""
Returns the expression class to be used with the Adaptor.
The mechanism used needs a little explanation. First, a concrete Adaptor
comes with (or reuse) a concrete expressionClass (SQLExpression) -- the
method 'defaultExpressionClass()' returns that concrete expression class.
This is also the default value returned by this method.
However, you may need to substitute that default expression class with one
of your own (for example, you may want to subclass that expr.class to
change part of its behaviour). This is what the static method
'setExpressionClassForAdaptor()' does: when called, it registers your
custom expression class for use with a given adaptor. Any subsequent call
to this method --'expressionClass()'-- then returns the registered
expr. class instead of the default one.
To summarize it:
- if 'expressionClassForAdaptor(self)' returns None, the method returns
'self.defaultExpressionClass()'
- otherwise, it returns 'expressionClassForAdaptor(self)'
Subclasses should not override this method.
"""
# First, check if it was overriden with
# (static) setExpressionClassForAdaptor
exprClass=Adaptor.expressionClassForAdaptor(self)
if exprClass is not None:
return exprClass
return self.defaultExpressionClass()
def expressionFactory(self):
"See interfaces.Adaptor for details"
def externalTypesWithModel(self, aModel):
"See interfaces.Adaptor for details"
def fetchedValueForDataValue(self, value, anAttribute):
"""
Adaptor's implementation returns 'value' without any modification
"""
return value
def fetchedValueForDateValue(self, value, anAttribute):
"""
Adaptor's implementation returns 'value' without any modification
"""
return value
def fetchedValueForNumberValue(self, value, anAttribute):
"""
Adaptor's implementation returns 'value', possibly modified if
'anAttribute' corresponds to a SQL 'NUMERIC' field with a scale lower
than the number of digits in the fractional part.
For example, the float '4.675' will be converted to '4.68' if
'anAttribute' has a scale of 2.
"""
if anAttribute.externalType()=='NUMERIC':
return round(value, anAttribute.scale())
def fetchedValueForStringValue(self, value, anAttribute):
"""
Adaptor's implementation returns 'value' without modification
"""
def fetchedValueForValue(self, value, anAttribute):
"""
This method examine 'anAttribute' and, according to its externalType,
forwards the message to one of the methods fetchedValueForDataValue(),
fetchedValueForDateValue(), fetchedValueForNumberValue() or
fetchedValueForStringValue().
The purpose here is to return a value, derived from 'value', which
reflects the value as it is actually stored by a database-backend. This is
used by the snapshotting mechanisms in the framework (optimistic locking).
See these methods for a description of their actions.
"""
def handleDroppedConnection(self):
"Unimplemented yet"
self.__unimplemented__()
def hasOpenChannels(self):
"""
Forwards the messages to all contexts() and returns 1 (true value) if at
least one of these returned a true value, return 0 (false value)
otherwise.
See: setConnectionDictionary()
"""
for context in self.contexts():
if context.hasOpenChannels():
return 1
return 0
def internalTypeForExternalType(self, extType, aModel):
"See interfaces.Adaptor for details"
def isDroppedConnectionException(self, exception):
"See interfaces.Adaptor for details"
def isValidQualifierType(self, typeName, aModel):
"See interfaces.Adaptor for details"
def name(self):
"""
Returns the name that was used when the concrete Adaptor was instanciated.
See also: adaptorWithName(), adaptorWithModel()
"""
return self._name
def prototypeAttributes(self):
"See interfaces.Adaptor for details"
self.__unimplemented__()
def setConnectionDictionary(self, connectionDictionary):
"""
Sets the connectionDictionary.
Raises ValueError if hasOpenChannels() returns true --in this case the
connectionDictionary is not set.
"""
if self.hasOpenChannels():
raise ValueError, "Connection dictionary cannot be changed while some"\
"AdaptorChannels are opened"
self._connectionDictionary=connectionDictionary
def setDelegate(self, aDelegate):
"""
Sets the delegate (not used yet)
"""
self._delegate=aDelegate
#def synchronizationFactory(self):
# "See interfaces.Adaptor for details"
# self.__unimplemented__()
def schemaGenerationFactory(self):
"See interfaces.Adaptor for details"
__abstract__()
def __unimplemented__(self):
raise 'Unimplemented', 'Yet unimplemented'
def __abstract__():
raise 'AbstractMethod', 'Left intentionally unimplemented in this class, '\
'subclass should override this method'
|