[Modeling-cvs] ProjectModeling/Modeling ChangeLog,1.1,1.2 delegation.py,1.4,1.5
Status: Abandoned
Brought to you by:
sbigaret
From: Sebastien B. <sbi...@us...> - 2004-08-02 20:54:57
|
Update of /cvsroot/modeling/ProjectModeling/Modeling In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1181/Modeling Modified Files: ChangeLog delegation.py Log Message: * delegation.py: private methods and fields now begins w/ a simple underscore, not two of them (+doc. updated) * tests/test_delegation.py: added Index: delegation.py =================================================================== RCS file: /cvsroot/modeling/ProjectModeling/Modeling/delegation.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** delegation.py 14 Feb 2004 18:27:04 -0000 1.4 --- delegation.py 2 Aug 2004 20:54:47 -0000 1.5 *************** *** 1,93 **** # -*- coding: iso-8859-1 -*- ! """ ! delegation module ! The underlying philosophy ! ! Suppose you are about to develop a reusable component offering a secure ! login service. This component might need two different sorts of features: ! say, a mandatory procedure to check login and password, and some optional ! others, e.g. a special procedure to be called when the maximal number of ! login attempts is reached. Your component implements a default procedure ! to check credentials against, say, the '/etc/password' file, and does not ! have any limit on the number of retries after failure. ! However and since this is a reusable component you wish that the procedure ! that checks credentials, the maximum number of failures and the action to ! be taken can be conveniently changed at runtime. ! One approach consists in making these parameters settable in your ! component ; this will work well, but this may not be really convenient: ! - for you, as the developper of the reusable component, because this ! make the API of the component all the more cumbersome since the number ! of possible external ``tune-ups'' grows ! - for the users of your component, for which it may be handier to be ! able to provide a single object gathering its own mechanisms to tune ! the reusable component [1]. A good reason for this is when the some ! processings involved depends on some other objects the reusable ! component has no way to know about, and/or if answers to some questions ! cannot be determined but at runtime. ! The delegation offers you a general mechanism to solve these two issues. ! The DelegateWrapper class allows the following features: ! - it makes it simple to have a single place to specify and document what ! can be changed along with specific documentation: this is the ! delegate's interface. ! - The users of your component do not have to implement *all* ! methods of the delegate's API, they only provide whatever methods is ! adequate for their own use [2]. ! - It can also be used to notify a delegate when some actions are about ! to be done, have been processed, etc., thus making it possible to ! keep an external object in sync. with the reusable component's ! internal state, without violating encapsulation. ! ..[1] of course, you may run into cases where such a design is inaccurate. ! All this is obviously a general discussion which might be inadequate ! given a specific architecture or design philosophy! ! ..[2] except if you enforce your own rules, such as ``if the delegates ! provides methodOne it should provide methodTwo as well'', but this an ! other story (see DelegateWrapper.respondsTo()) ! How to use ! We will stick on the example exposed above to explain how delegation is ! achieved with this module. ! First, define a general ``interface'': a class, which defines and ! documents the methods a delegate for your component might implement:: ! class LoginComponentDelegate: ! 'Delegate for the reusable LoginComponent' ! def checkCredentials(self, login, password): ! ''' ! Implement this method if you want to replace the default checking ! mechanism by yours. This method should raise InvalidCredentials ! if parameters 'login' and 'password' do not match an valid user. ! Parameters: both 'login' and 'password' are strings. ! ''' ! def maximumNumberOfFailures(self): ! ''' ! Implement this method to ... ! A return value equal to '-1' means no limit. ! ''' ! def numberOfFailuresHasReachedItsMaximum(self): ! ''' ! ... ! ''' ! The 'delegation' module supplies one class, 'DelegateWrapper', to handle ! delegate. Its role is to inspect a delegate's interface... ! To use it, your reusable component's class will look like:: class LoginComponent: --- 1,98 ---- # -*- coding: iso-8859-1 -*- ! """delegation module. ! The underlying philosophy ! ------------------------- ! Suppose you are about to develop a reusable component offering a secure ! login service. This component might need two different sorts of features: ! say, a mandatory procedure to check login and password, and some optional ! others, e.g. a special procedure to be called when the maximal number of ! login attempts is reached. Your component implements a default procedure ! to check credentials against, say, the ``/etc/password`` file, and does not ! have any limit on the number of retries after failure. ! However and since this is a reusable component you wish that the procedure ! that checks credentials, the maximum number of failures and the action to ! be taken can be conveniently changed at runtime. ! One approach consists in making these parameters settable in your ! component ; this will work well, but this may not be really convenient: ! - for you, as the developper of the reusable component, because this ! make the API of the component all the more cumbersome since the number ! of possible external "tune-ups" grows ! - for the users of your component, for which it may be handier to be ! able to provide a single object gathering its own mechanisms to tune ! the reusable component [1]_. A good reason for this is when the some ! processings involved depends on some other objects the reusable ! component has no way to know about, and/or if answers to some questions ! cannot be determined but at runtime. ! The delegation offers you a general mechanism to solve these two issues. ! The DelegateWrapper class allows the following features: ! - it makes it simple to have a single place to specify and document what ! can be changed along with specific documentation: this is the ! delegate's interface. ! - The users of your component do not have to implement *all* ! methods of the delegate's API, they only provide whatever methods is ! adequate for their own use [2]_. ! - It can also be used to notify a delegate when some actions are about ! to be done, have been processed, etc., thus making it possible to ! keep an external object in sync. with the reusable component's ! internal state, without violating encapsulation. ! .. [1] of course, you may run into cases where such a design is inaccurate. ! All this is obviously a general discussion which might be inadequate given ! a specific architecture or design philosophy! ! ! .. [2] except if you enforce your own rules, such as "if the delegates ! provides methodOne it should provide methodTwo as well", but this an other ! story (see DelegateWrapper.respondsTo()) ! How to use ! ---------- ! We will stick on the example exposed above to explain how delegation is ! achieved with this module. ! First, define a general "interface": a class, which defines and ! documents the methods a delegate for your component might implement: ! .. code-block:: Python ! class LoginComponentDelegate: ! 'Delegate for the reusable LoginComponent' ! def checkCredentials(self, login, password): ! ''' ! Implement this method if you want to replace the default checking ! mechanism by yours. This method should raise InvalidCredentials ! if parameters 'login' and 'password' do not match an valid user. ! Parameters: both 'login' and 'password' are strings. ! ''' ! def maximumNumberOfFailures(self): ! ''' ! Implement this method to ... ! A return value equal to '-1' means no limit. ! ''' ! def numberOfFailuresHasReachedItsMaximum(self): ! ''' ! ... ! ''' ! The `delegation` module supplies one class, `DelegateWrapper`, to handle ! delegate. Its role is to inspect a delegate's interface... ! ! To use it, your reusable component's class will look like: ! ! .. code-block:: Python class LoginComponent: *************** *** 131,234 **** [...] ! The delegate itself, as it will be provided by the users of the reusable ! component, can be an instance of any class. You should have noticed in the ! sample code above that the DelegateWrapper acts as a proxy : it ! automatically forwards any function call it cannot service (see Gotchas, ! below) to the wrapped object --the real delegate). Note, however, that the ! DelegateWrapper is **not** an ``absolute'' proxy: the forwarded messages ! are __only__ those which are declared in the delegate's interface. ! Gotchas ! There are several points which you should be aware of when using this ! class, some of which should be indicated in your own documentation: ! - the DelegateWrapper works by inspecting the interface-class and ! registering the signatures of its methods. By signature we mean: name, ! parameters *and* their default values, if any. These signatures are then ! compared to those of the delegate object when it is supplied. Thus: ! - if a delegate object does implement a function whose name matches ! one of the delegate's API but which uses different number of ! parameters, the same number of parameter with different names, or ! the same number of parameters and the same names but with different ! default values, this function will not be considered as an valid ! delegate method (hence 'respondsTo(thatMethodName)' will be false). ! - The DelegateWrapper determines the mapping between the delegate's ! API and the delegate object when this object is supplied ! ('setDelegateObject()') ; this mapping determines the answer for ! message 'respondsTo' as well as whether that message should be ! forwarded to the delegate by the DelegateWrapper. ! Thus, if the delegate object can change at runtime, e.g. not ! responding to a message at some point of runtime and being able to ! service the same message later, that message will never be ! considered available. In that case, the provider for the delegate ! object should take care to call the component's 'setDelegate()' ! method each time the delegate object changes --this ensures that the ! mapping between the delegate's API and the object's capabilities is ! correctly recomputed. ! - The DelegateWrapper itself declares some methods: 'respondsTo', ! 'delegateObject', 'setDelegateObject', 'reevaluateDelegateObject'. If ! the delegate's interface uses at least one of these methods, the ! automatic forwarding of method calls is automatically disabled. In that ! case, a warnings is issued (warning level: RuntimeWarning) when the ! delegate object is set. You can still send messages to the delegate ! object by sending the messages to the ! 'DelegateWrapper.delegateObject()'. ! CVS information ! ! $Id$ ! ! Licence:: ! This software is Copyright (c) 2001, 2002 inQual S.A.S. and ! Contributors. ! All rights reserved. Redistribution and use in source and binary forms, with or without ! modification, are permitted provided that the following conditions ! are met: ! ! 1. Redistributions in source code must retain the above copyright ! notice, this list of conditions, and the following disclaimer. ! ! 2. Redistributions in binary form must reproduce the above copyright ! notice, this list of conditions, and the following disclaimer in ! the documentation and/or other materials provided with the ! distribution. ! ! 3. Neither the name of the inQual S.A.S. nor the names of its ! contributors may be used to endorse or promote products derived ! from this software without specific prior written permission. ! ! 4. If any files are modified, you must cause the modified files to ! carry prominent notices stating that you changed the files and ! the date of any change. ! Disclaimer ! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND ! CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, ! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ! MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS ! BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ! EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ! TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ! DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ! ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ! TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF ! THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! SUCH DAMAGE. """ ! __version__='Revision: 1.3 '[10:] # original revision ! __authors__=('Sebastien Bigaret <Seb...@in...>', ) from warnings import warn --- 136,235 ---- [...] ! The delegate itself, as it will be provided by the users of the reusable ! component, can be an instance of any class. You should have noticed in the ! sample code above that the `DelegateWrapper` acts as a proxy : it ! automatically forwards any function call it cannot service (see Gotchas, ! below) to the wrapped object --the real delegate). Note, however, that the ! `DelegateWrapper` is **not** an "absolute" proxy: the forwarded messages ! are __only__ those which are declared in the delegate's interface. ! Gotchas ! ------- ! There are several points which you should be aware of when using this ! class, some of which should be indicated in your own documentation: ! - the DelegateWrapper works by inspecting the interface-class and ! registering the signatures of its methods. By signature we mean: name, ! parameters *and* their default values, if any. These signatures are then ! compared to those of the delegate object when it is supplied. Thus: ! - if a delegate object does implement a function whose name matches ! one of the delegate's API but which uses different number of ! parameters, the same number of parameter with different names, or ! the same number of parameters and the same names but with different ! default values, this function will not be considered as an valid ! delegate method (hence 'respondsTo(thatMethodName)' will be false). ! - The DelegateWrapper determines the mapping between the delegate's ! API and the delegate object when this object is supplied ! ('setDelegateObject()') ; this mapping determines the answer for ! message 'respondsTo' as well as whether that message should be ! forwarded to the delegate by the DelegateWrapper. ! Thus, if the delegate object can change at runtime, e.g. not ! responding to a message at some point of runtime and being able to ! service the same message later, that message will never be ! considered available. In that case, the provider for the delegate ! object should take care to call the component's 'setDelegate()' ! method each time the delegate object changes --this ensures that the ! mapping between the delegate's API and the object's capabilities is ! correctly recomputed. ! - The `DelegateWrapper` itself declares some methods: ! `DelegateWrapper.respondsTo`, `DelegateWrapper.delegateObject`, ! `DelegateWrapper.setDelegateObject`, ! `DelegateWrapper.reevaluateDelegateObject`. If the delegate's interface uses ! at least one of these methods, the automatic forwarding of method calls is ! automatically disabled. In that case, a warnings is issued (warning level: ! ``RuntimeWarning``) when the delegate object is set. You can still send ! messages to the delegate object by sending the messages to the ! `DelegateWrapper.delegateObject()`. ! Licence ! ------- ! :: ! ! Copyright (c) 2001-2004, Sebastien Bigaret All rights reserved. Redistribution and use in source and binary forms, with or without ! modification, are permitted provided that the following conditions are ! met: ! * Redistributions of source code must retain the above copyright ! notice, this list of conditions and the following disclaimer. ! ! * Redistributions in binary form must reproduce the above copyright ! notice, this list of conditions and the following disclaimer in the ! documentation and/or other materials provided with the distribution. ! ! * Neither the name of the software's copyright holder, Sebastien ! Bigaret, nor the names of its contributors may be used to endorse or ! promote products derived from this software without specific prior ! written permission. ! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ! IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ! THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ! PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ! CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ! EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ! PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ! LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ! NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ! SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + CVS information + --------------- + + $Id$ + """ ! __version__='$Revision$'[11:-3] # original revision ! __authors__=('Sebastien Bigaret <sbi...@us...>', ) from warnings import warn *************** *** 239,263 **** The DelegateWrapper: """ ! __delegate=None def __init__(self, interface, object=None): """ Initializes the DelegateWrapper ! Parameters ! ! interface -- a class object declaring the methods a delegate may choose ! to implement ! object -- optional delegate object. The delegate object can be set ! or changed later with 'setDelegateObject()' """ if not inspect.isclass(interface): raise ValueError, 'Invalid parameter interface: should be a class' ! self.__interfaceSpec={} ! self.__delegateCapabilities={} ! self.__proxyDelegate=1 ! self.__interface=interface ! self.__compileInterface() if object: self.setDelegateObject(object) --- 240,265 ---- The DelegateWrapper: """ ! _delegate=None ! def __init__(self, interface, object=None): """ Initializes the DelegateWrapper ! :Parameters: ! - `interface`: a class object declaring the methods a delegate may ! choose to implement ! - `object`: optional delegate object. The delegate object can be set or ! changed later with `setDelegateObject()` ! """ if not inspect.isclass(interface): raise ValueError, 'Invalid parameter interface: should be a class' ! self._interfaceSpec={} ! self._delegateCapabilities={} ! self._proxyDelegate=1 ! self._interface=interface ! self._compileInterface() if object: self.setDelegateObject(object) *************** *** 267,280 **** Returns the delegate object """ ! return self.__delegate def respondsTo(self, interfaceMethodName): """ Tells whether the wrapped delegate object can responds to the method ! 'methodName' as defined in the delegate's interface. ! Returned value is None for 'false', '1' (integer) for true """ ! return self.__delegateCapabilities.get(interfaceMethodName) def reevaluateDelegateObject(self): --- 269,282 ---- Returns the delegate object """ ! return self._delegate def respondsTo(self, interfaceMethodName): """ Tells whether the wrapped delegate object can responds to the method ! `interfaceMethodName` as defined in the delegate's interface. ! Returned value is ``None`` for false, ``1`` (integer) for true """ ! return self._delegateCapabilities.get(interfaceMethodName) def reevaluateDelegateObject(self): *************** *** 282,293 **** """ ! if not self.__delegate: return ! self.__delegateCapabilities={} ! functions=self.__getClassMethodsSignatures(self.__delegate.__class__) for nameSignature in functions: name, signature=nameSignature ! if signature==self.__interfaceSpec.get(name): ! self.__delegateCapabilities[name]=1 def setDelegateObject(self, object): --- 284,295 ---- """ ! if not self._delegate: return ! self._delegateCapabilities={} ! functions=self._getClassMethodsSignatures(self._delegate.__class__) for nameSignature in functions: name, signature=nameSignature ! if signature==self._interfaceSpec.get(name): ! self._delegateCapabilities[name]=1 def setDelegateObject(self, object): *************** *** 298,328 **** raise ValueError, 'Invalid parameter object: it should be an instance' ! self.__delegate=object self.reevaluateDelegateObject() def __getattr__(self, name): # proxy object's method only ! if not self.__proxyDelegate: raise AttributeError, 'Automatic forward of method calls is disabled. ' ! if self.__delegateCapabilities.get(name): ! return getattr(self.__delegate, name) raise AttributeError # Private methods ! def __compileInterface(self): """ Inspect the delegate's interface and extracts the necessary methods' names and signatures """ ! functions=self.__getClassMethodsSignatures(self.__interface) dict={} pbFuncNames=[] ! self.__proxyDelegate=1 for nameSignature in functions: name, signature=nameSignature dict[name]=signature if name in ('delegate', 'respondsTo', 'reevaluateDelegateObject', 'setDelegate'): ! self.__proxyDelegate=0 pbFuncNames.append(name) ! if not self.__proxyDelegate: warn('DelegateWrapper will not be able to proxy calls to the delegate '\ "object: the interface declares the methods %s which are also "\ --- 300,330 ---- raise ValueError, 'Invalid parameter object: it should be an instance' ! self._delegate=object self.reevaluateDelegateObject() def __getattr__(self, name): # proxy object's method only ! if not self._proxyDelegate: raise AttributeError, 'Automatic forward of method calls is disabled. ' ! if self._delegateCapabilities.get(name): ! return getattr(self._delegate, name) raise AttributeError # Private methods ! def _compileInterface(self): """ Inspect the delegate's interface and extracts the necessary methods' names and signatures """ ! functions=self._getClassMethodsSignatures(self._interface) dict={} pbFuncNames=[] ! self._proxyDelegate=1 for nameSignature in functions: name, signature=nameSignature dict[name]=signature if name in ('delegate', 'respondsTo', 'reevaluateDelegateObject', 'setDelegate'): ! self._proxyDelegate=0 pbFuncNames.append(name) ! if not self._proxyDelegate: warn('DelegateWrapper will not be able to proxy calls to the delegate '\ "object: the interface declares the methods %s which are also "\ *************** *** 330,347 **** "(see documentation for details)"%str(pbFuncNames), RuntimeWarning) ! self.__interfaceSpec=dict return ! def __getClassMethodsSignatures(self, aClass): """ Generic function returning a sequence made of couples (<functionName>, <functionSignature>), each couple representing one of ! 'aClass' 's methods. ! An example of such a couple is (from tests/test_delegation.py):: ('canTheWorldBeChanged', "(['self', 'isElvisAlive'], None, None, (1,))") ! See also: inspect.getargspec() """ isfunction=inspect.isfunction --- 332,349 ---- "(see documentation for details)"%str(pbFuncNames), RuntimeWarning) ! self._interfaceSpec=dict return ! def _getClassMethodsSignatures(self, aClass): """ Generic function returning a sequence made of couples (<functionName>, <functionSignature>), each couple representing one of ! `aClass` 's methods. ! An example of such a couple is (from ``tests/test_delegation.py``):: ('canTheWorldBeChanged', "(['self', 'isElvisAlive'], None, None, (1,))") ! See also: `inspect.getargspec()` """ isfunction=inspect.isfunction Index: ChangeLog =================================================================== RCS file: /cvsroot/modeling/ProjectModeling/Modeling/ChangeLog,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** ChangeLog 2 Aug 2004 20:11:39 -0000 1.1 --- ChangeLog 2 Aug 2004 20:54:47 -0000 1.2 *************** *** 1,4 **** --- 1,8 ---- 2004-08-02 Sebastien Bigaret <sbi...@us...> + * delegation.py: private methods and fields now begins w/ a + simple underscore, not two of them (+doc. updated) + * tests/test_delegation.py: added + * DatabaseAdaptors/AbstractDBAPI2AdaptorLayer/AbstractDBAPI2AdaptorContext.py (AbstractDBAPI2AdaptorContext.__openConnectionIfNecessary__): |