<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1660699321100518149</id><updated>2024-09-04T18:49:48.776-07:00</updated><category term="DBFSpy"/><category term="DataConnectors"/><category term="abstract class"/><category term="design"/><category term="TypedContainers"/><category term="list"/><category term="strongly typed"/><category term="BaseDataObject"/><category term="MiniAppServer"/><category term="TDD"/><category term="TypedList"/><category term="concrete class"/><category term="test-driven development"/><category term="BaseDataConnection"/><category term="BaseDatabaseConnection"/><category term="BaseFilesystemItemCache"/><category term="IFilesystem"/><category term="IFilesystemItem"/><category term="ResultSet"/><category term="UML"/><category term="asp.net"/><category term="code conventions"/><category term="configuration"/><category term="content management"/><category term="interface"/><category term="unit-testing"/><category term="web-applications"/><category term="CMS"/><category term="Drupal"/><category term="Enumeration"/><category term="HasConfiguration"/><category term="IFilesystemOperators"/><category term="IsConfigurable"/><category term="IsDataConnector"/><category term="IsQuery"/><category term="IsResultSet"/><category term="MySQLConnector"/><category term="MySQLDatasource"/><category term="MySQLFileItem"/><category term="MySQLFilesystem"/><category term="PicaCMS"/><category term="Query"/><category term="Record"/><category term="Tridion"/><category term="UnitTestUtilities"/><category term="Wordpress"/><category term="annotations"/><category term="class"/><category term="class diagram"/><category term="code coverage"/><category term="coldfusion"/><category term="container"/><category term="database connection"/><category term="decorators"/><category term="dictionary"/><category term="documentation"/><category term="function"/><category term="goals"/><category term="integration testing"/><category term="introduction"/><category term="local applications"/><category term="meta-data"/><category term="mod-python"/><category term="mothball"/><category term="perl"/><category term="purpose"/><category term="requirements"/><category term="template file"/><category term="tuple"/><category term="unittest"/><category term="utilities"/><title type='text'>My Brain on Python</title><subtitle type='html'>Captures from the stream of consciousness as I ponder (or thrash) my way through some Python projects...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>39</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-4948016619282600040</id><published>2012-01-14T14:24:00.000-08:00</published><updated>2012-01-14T14:24:48.428-08:00</updated><title type='text'>Slower than I&#39;d hoped, and ...</title><content type='html'>Well, I&#39;ve had a few more days to ponder on things, and my work situation, while *very* nice/neat/cool, just isn&#39;t going to allow me to continue at anywhere near the pace I&#39;d been maintaining before the change in jobs happened. In addition, I&#39;m actually writing Python code at work, and though I&#39;m doing a lot of really neat things with it, I&#39;m not sure whether I can share any of it at all (I certainly can&#39;t without approval from my boss).&lt;br /&gt;
&lt;br /&gt;
I&#39;ll try to ask around and see if any of what I&#39;m doing is OK to share here (I hope so, since I&#39;ve written a couple thousand lines of code so far), but until I know for sure, I&#39;m gonna err on the side of caution, and not post anything unless I *know* it&#39;s mine, unencumbered.</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/4948016619282600040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/slower-than-id-hoped-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4948016619282600040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4948016619282600040'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/slower-than-id-hoped-and.html' title='Slower than I&#39;d hoped, and ...'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-7392074397293049939</id><published>2012-01-09T04:30:00.001-08:00</published><updated>2012-01-09T04:30:01.998-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Enumeration"/><category scheme="http://www.blogger.com/atom/ns#" term="TypedContainers"/><title type='text'>Interlude: Enumerations and TypedContainers</title><content type='html'>&lt;p&gt;So, while I ponder my next step(s), it occurred to me that I have some old 
code lying around that could stand to be polished and/or brought up to my 
current standards. One item from that mix is an attempt to implement an 
&lt;a href=&quot;http://en.wikipedia.org/wiki/Enumerated_type&quot;&gt;Enumerated Type&lt;/a&gt;, 
much like the types provided in Java and C#. Although it isn&#39;t, technically, a 
strongly-typed container like &lt;code&gt;TypedDictionary&lt;/code&gt;, &lt;code&gt;TypedList&lt;/code&gt; 
or &lt;code&gt;TypedTuple&lt;/code&gt; are, it has enough similar characteristics that 
dropping the &lt;code&gt;Enumeration&lt;/code&gt; class into the &lt;code&gt;TypedContainers&lt;/code&gt; 
module felt reasonable. Consider:
    &lt;ul&gt;
        &lt;li&gt;An enumeration is a collection of values, like dictionaries, lists 
            and tuples;&lt;/li&gt;
        &lt;li&gt;Also like dictionaries, lists, and tuples, enumerations are (or at 
            least can be considered as a type);&lt;/li&gt;
        &lt;li&gt;Value-membership is also a shared characteristic. That is, all of 
            the established types &lt;em&gt;and&lt;/em&gt; enumerations both should allow a 
            developer to determine whether some arbitrary value is a member of 
            the instance.&lt;/li&gt;
        &lt;li&gt;Unlike those types, however, an enumeration has a fixed, immutable 
            set of values.&lt;/li&gt;
    &lt;/ul&gt;
Whether the member-values of an enumeration need to be of the same type or not 
is an open question, to my mind. If they are, so be it. But, ultimately, the 
purpose that an enumeration serves (mainly, that I&#39;m aware of) is to collect 
some fixed set of values, which &lt;em&gt;feels&lt;/em&gt; like a type, regardless.&lt;/p&gt;

&lt;p&gt;So that (I hope) justifies the decision to put &lt;code&gt;Enumeration&lt;/code&gt; into 
&lt;code&gt;TypedContainers&lt;/code&gt; (shared at &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/TypedContainers.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/TypedContainers.py&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;By preference, I&#39;d like to be able to use code structures like:
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;SomeStuff = Enumeration(
    &#39;This is an enumeration of values for some purpose&#39;, 
    Good=1,
    Bad=-1,
    Indifferent=0
    )

# Dot-notation should be an option:
goodValue = SomeStuff.Good

# For comparison/membership-detection, &quot;in&quot; should be used:
if( goodValue in SomeStuff ):
    # do something
&lt;/pre&gt;

The creation of an enumeration (lines 1-6) should allow for the generation of an 
instance doc-string (though, to be truthful, I&#39;m not sure that it&#39;ll ever be of 
much use). At present, this doc-string won&#39;t appear anywhere, but I&#39;ll eventually 
get my head together on the documentation side of things to work out a way to 
retrieve it for &lt;a href=&quot;http://my-brain-on-python.blogspot.com/search/label/APpyDoc&quot;&gt;API 
documentation&lt;/a&gt;, so it might as well be available now, rather than having to 
come back and implement it later. The generation of member-names and values should 
be as simple as providing them suring the construction of the instance, and they 
should not be limited in number or type.
&lt;/p&gt;

&lt;p&gt;Use of dot-notation for specific member-names/-values should be allowed (line 9), 
and detection of whether some arbitrary value is present within the Enumeration 
should be as simple as possible (I prefer the &lt;code&gt;in&lt;/code&gt; keyword, lines 12-13).&lt;/p&gt;

&lt;p&gt;With all of these requirements/preferences in mind, here&#39;s the (updated) code 
that I generated to meet them:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class Enumeration( object ):
    &quot;&quot;&quot;Provides a &quot;formal&quot; enumeration-type, allowing a single class-instance to be
used to provide dot-notation enumeration values.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    __isLocked = False
    _iterableItems = []
    _key = 0

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetMembers( self ):
        &quot;&quot;&quot;Gets the members of the Enumeration as a Dictionary value.&quot;&quot;&quot;
        return self.__members

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetMemberNames( self ):
        &quot;&quot;&quot;Gets the names of the Enumeration&#39;s members.&quot;&quot;&quot;
        return self.__members.keys()

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetMemberValues( self ):
        &quot;&quot;&quot;Gets the values of the Enumeration&#39;s members.&quot;&quot;&quot;
        return self.__members.values()

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    Members = property( _GetMembers, None, None, _GetMembers.__doc__ )
    MemberNames = property( _GetMemberNames, None, None, _GetMemberNames.__doc__ )
    MemberValues = property( _GetMemberValues, None, None, _GetMemberValues.__doc__ )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;docString&#39;, None, &#39;The documentation-string for the Enumeration.&#39; )
    @DocumentArgument( &#39;keyword&#39;, &#39;members&#39;, None, &#39;The members of the Enumeration, in the form &quot;MemberName=MemberValue&quot;.&#39; )
    @DocumentException( NotImplementedError, &#39;used as a base class&#39; )
    @DocumentException( TypeError, &#39;not supplied with a keyword-argument set&#39; )
    @DocumentException( KeyError, &#39;an enumeration member-name is specified that is used internally by the class (see it\&#39;s properties)&#39; )
    def __init__( self, docString, **members ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != Enumeration:
            raise NotImplementedError( &#39;Enumeration is (nominally) a final class, and is not intended to be derived from.&#39; )
        if type( members ) != types.DictType:
            raise TypeError( &quot;Enumeration expected a keyword-list of member names and values.&quot; )
        self.__isLocked = False
        self._key = 0
        self.__members = {}
        forbiddenKeys = dir( self )
        for theMember in members:
            if theMember in forbiddenKeys:
                raise KeyError( &quot;An Enumeration cannot override the %s key-name: It&#39;s reserved by the object.&quot; % ( theMember ) )
            self.__dict__[ theMember ] = members[ theMember ]
            self.__members[ theMember ] = members[ theMember ]
        if docString != None:
            self.__doc__ = docString + (&#39; %s&#39; % ( self.__members.keys() ) ).replace( &quot;&#39;&quot;, &#39;&#39; )
        self._iterableItems = self.__members.values()
        self.__isLocked = True

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __setattr__( self, name, value ):
        &quot;&quot;&quot;Intercepts attempts to set attribute-values, and prevents them from being set if the instance has been locked.&quot;&quot;&quot;
        # Allow changes to self._key for iteration purposes:
        if not self.__isLocked or name == &#39;_key&#39;:
            return object.__setattr__( self, name, value )
        raise AttributeError( &#39;Enumerations are immutable after instantiation.&#39; )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __iter__( self ):
        &quot;&quot;&quot;Standard iterator handle.&quot;&quot;&quot;
        self._key = 0
        return copy.copy( self )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentException( StopIteration, &#39;the end of an iteration against an instance is reached (per specifications for an iterable object)&#39; )
    def next( self ):
        &quot;&quot;&quot;Standard iteration mechanism.&quot;&quot;&quot;
        try:
            thisKey = self._key
            self._key += 1
            return self._iterableItems[ thisKey ]
        except IndexError:
            self._key = 0
            raise StopIteration

__all__ += [ &#39;Enumeration&#39; ]&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Commentary&lt;/strong&gt;&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;9-11&lt;/dt&gt;
    &lt;dd&gt;Provides a default value for the &lt;code&gt;__isLocked&lt;/code&gt; attribute of 
        the instance. Without this, the &lt;code&gt;__setattr__&lt;/code&gt; call made 
        implicitly during object-construction to set it&#39;s value to &lt;code&gt;False&lt;/code&gt;
        fails, since no attribute exists to check. &lt;code&gt;_key&lt;/code&gt; is similarly 
        set, to avoid it being similarly constrained.&lt;/dd&gt;

    &lt;dt&gt;17-30, 44-46&lt;/dt&gt;
    &lt;dd&gt;My typical property-getter and property-declaration structure, for 
        read-only properties.&lt;/dd&gt;

    &lt;dt&gt;52-77&lt;/dt&gt;
    &lt;dd&gt;Object constructor:
        &lt;dl&gt;

            &lt;dt&gt;63-64&lt;/dt&gt;
            &lt;dd&gt;Checks that the &lt;code&gt;members&lt;/code&gt; argument is a dictionary, 
                which is the default type for a keyword argument list. This may 
                not be strictly necessary, but given that I anticipate the use 
                of constructor-calls along the lines of &lt;code&gt;MyEnumeration = 
                Enumeration( **someDictionary )&lt;/code&gt;, it felt safer to leave 
                it in place than to remove it.&lt;/dd&gt;

            &lt;dt&gt;65-68&lt;/dt&gt;
            &lt;dd&gt;Sets the default values for various properties, then gets every 
                member-name of the instance (with &lt;code&gt;dir()&lt;/code&gt;) in order 
                to have a list of forbidden member-names for later checking/comparison.&lt;/dd&gt;

            &lt;dt&gt;69-73&lt;/dt&gt;
            &lt;dd&gt;Iterates through the provided member names/values dictionary, 
                checks each key-name therein against the forbidden keys (raising 
                a &lt;code&gt;KeyError&lt;/code&gt; if there&#39;s a collision), and if everything&#39;s 
                OK, it attaches the member name/value to the instance&#39;s 
                &lt;code&gt;__dict__&lt;/code&gt; to make it available in a dot-notation 
                syntax later. This is also where (line 72) the internal 
                &lt;code&gt;__members&lt;/code&gt; dictionary gets populated so that the 
                various &lt;code&gt;Member...&lt;/code&gt; properties and getters defined 
                previously have something to work with.&lt;/dd&gt;

            &lt;dt&gt;74-75&lt;/dt&gt;
            &lt;dd&gt;Sets the &lt;code&gt;__doc__&lt;/code&gt; of the instance, making sure to 
                include the provided member-names.&lt;/dd&gt;

            &lt;dt&gt;76&lt;/dt&gt;
            &lt;dd&gt;Sets the &lt;code&gt;_iterableItems&lt;/code&gt; array up, so that it&#39;s 
                available for use by the iteration methods later.&lt;/dd&gt;

            &lt;dt&gt;77&lt;/dt&gt;
            &lt;dd&gt;Locks the object so that any future use of &lt;code&gt;__setattr__&lt;/code&gt; 
                will know that the object is locked, and should not be modified.&lt;/dd&gt;

        &lt;/dl&gt;
    &lt;/dd&gt;

    &lt;dt&gt;88-93&lt;/dt&gt;
    &lt;dd&gt;Leverages the built-in &lt;code&gt;__setattr__&lt;/code&gt; method to prevent the 
        addition of new attributes or modification of existing ones once the 
        instance is locked.&lt;/dd&gt;

    &lt;dt&gt;96-99, 101-111&lt;/dt&gt;
    &lt;dd&gt;The &lt;code&gt;__iter__&lt;/code&gt; method is Python&#39;s standard mechanism for 
        iterating over &lt;a href=&quot;http://docs.python.org/library/stdtypes.html#iterator-types&quot;&gt;iterator-types&lt;/a&gt; - and 
        since I wanted &lt;code&gt;Enumeration&lt;/code&gt; to support the &lt;code&gt;in&lt;/code&gt; 
        keyword to determine membership, it needs to be iterable. In this 
        particular implementation, all it does is return a shallow copy of the 
        original instance to be iterated over with &lt;code&gt;next&lt;/code&gt;. Similarly, 
        the &lt;code&gt;next&lt;/code&gt; method is a standard iteration-mechanism, which 
        returns the next item from the container/collection.&lt;/dd&gt;

&lt;/dl&gt;

&lt;p&gt;There are some new/unusual/interesting items in the unit-tests, I think, so 
I&#39;ll share those as well.&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class EnumerationDerived( Enumeration ):
        def __init__( self, docString, **members ):
            Enumeration.__init__( self, docString, **members )
    
    class testEnumeration( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the Enumeration class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass

        def getGoodValues( self ):
            return [
                {&#39;name1&#39;:&#39;value1&#39;, &#39;name2&#39;:&#39;value2&#39;},
                {&#39;name1&#39;:True, &#39;name2&#39;:False},
                {&#39;name1&#39;:-1, &#39;name2&#39;:0, &#39;name3&#39;:1},
                ]
        
        def testFinal( self ):
            &quot;&quot;&quot;Testing final nature of the Enumeration class.&quot;&quot;&quot;
            try:
                testObject = EnumerationDerived( &#39;docstring&#39;, Name=&#39;value&#39; )
                self.fail( &#39;Enumeration is nominally a final class, and should not be extensible.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to instantiate a class derived from Enumeration should raise NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the Enumeration class.&quot;&quot;&quot;
            testValues = self.getGoodValues()
            for testValue in testValues:
                testObject = Enumeration( &#39;docstring&#39;, **testValue )
                self.assertEquals( testValue.keys(), testObject.MemberNames, &#39;An Enumeration instance created with %s as members should have %s as member names, but %s was returned instead.&#39; % ( testValue, testValue.keys(), testObject.MemberNames ) )
                self.assertEquals( testValue.values(), testObject.MemberValues, &#39;An Enumeration instance created with %s as members should have %s as member values, but %s was returned instead.&#39; % ( testValue, testValue.values(), testObject.MemberValues ) )
                self.assertEquals( testValue, testObject.Members, &#39;An Enumeration instance created with %s as members should have %s as member values, but %s was returned instead.&#39; % ( testValue, testValue, testObject.Members ) )
            # Construction should fail with bad docstrings...
            badDocstrings = [ True, 1, object() ]
            for testValue in badDocstrings:
                try:
                    testObject = Enumeration( testValue, One=1, Two=2 )
                    self.fail( &#39;%s should not be a valid docstring for an Enumeration&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Passing %s as a docstring should raise TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the Enumeration class.&quot;&quot;&quot;
            items = getMemberNames( Enumeration )[0]
            actual = len( items )
            expected = 3
            self.assertEquals( expected, actual, &#39;Enumeration is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the Enumeration class.&quot;&quot;&quot;
            items = getMemberNames( Enumeration )[1]
            actual = len( items )
            expected = 1
            self.assertEquals( expected, actual, &#39;Enumeration is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test properties

        def testMemberNames( self ):
            &quot;&quot;&quot;Tests the MemberNames property of the Enumeration class.&quot;&quot;&quot;
            #Tested in construction
            pass

        def testMemberValues( self ):
            &quot;&quot;&quot;Tests the MemberValues property of the Enumeration class.&quot;&quot;&quot;
            #Tested in construction
            pass

        def testMembers( self ):
            &quot;&quot;&quot;Tests the Members property of the Enumeration class.&quot;&quot;&quot;
            #Tested in construction
            pass

        # Test methods

        def testnext( self ):
            &quot;&quot;&quot;Tests the next method of the Enumeration class.&quot;&quot;&quot;
            #Tested in iterability tests
            pass

        # Test iterabilility
        def testIterability( self ):
            &quot;&quot;&quot;Tests the various iteration-dependent functions of the Enumeration class.&quot;&quot;&quot;
            testValues = self.getGoodValues()
            for testValue in testValues:
                testObject = Enumeration( &#39;docstring&#39;, **testValue )
                for member in testValue:
                    memberValue = testValue[ member ]
                    self.assertTrue( memberValue in testObject, &#39;Test of %s being in the Enumeration\&#39;s values should return true.&#39; % ( memberValue ) )
            testObject = Enumeration( &#39;docstring&#39;, Zero=0, One=1, Two=2, Three=3 )
            testValue = 0
            for value in testObject:
                self.assertEquals( testValue, value, &#39;The iterated value should match&#39; )
                testValue += 1

        # Test immutability after instantiation
        def testImmutability( self ):
            &quot;&quot;&quot;Tests the immutability of the members/values of an Enumeration after it&#39;s been instantiated.&quot;&quot;&quot;
            testObject = Enumeration( None, Good=1, Bad=-1, Indifferent=0 )
            try:
                testObject.Ugly = 2
                self.fail( &#39;Once instantiated, the members of an Enumeration should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Once instantiated, modification of the members of an Enumeration should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__ ) )
            try:
                testObject.Good = 2
                self.fail( &#39;Once instantiated, the members of an Enumeration should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Once instantiated, modification of the members of an Enumeration should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__ ) )

        # Test dot-notation access
        def testDotAccess( self ):
            &quot;&quot;&quot;Tests dot-notation access to member values by name.&quot;&quot;&quot;
            testObject = Enumeration( None, Good=1, Bad=-1, Indifferent=0 )
            self.assertTrue( hasattr( testObject, &#39;Good&#39; ), &#39;Member names should be accessible as attributes of the Enumeration instance.&#39; )
            self.assertTrue( hasattr( testObject, &#39;Bad&#39; ), &#39;Member names should be accessible as attributes of the Enumeration instance.&#39; )
            self.assertTrue( hasattr( testObject, &#39;Indifferent&#39; ), &#39;Member names should be accessible as attributes of the Enumeration instance.&#39; )
            self.assertEquals( testObject.Good, 1, &#39;Member values accessed through dot-notation should match the values supplied at creation.&#39; )
            self.assertEquals( testObject.Bad, -1, &#39;Member values accessed through dot-notation should match the values supplied at creation.&#39; )
            self.assertEquals( testObject.Indifferent, 0, &#39;Member values accessed through dot-notation should match the values supplied at creation.&#39; )

        # Test reserved-name creation - all should raise errors
        def testReservedNames( self ):
            &quot;&quot;&quot;Tests that the reserved names (existing attributes) aren&#39;t allowed as member-names.&quot;&quot;&quot;
            names = [ &#39;_key&#39;, &#39;Members&#39;, &#39;MemberNames&#39;, &#39;MemberValues&#39; ]
            for name in names:
                members = { name:True }
                try:
                    testObject = Enumeration( &#39;docstring&#39;, **members )
                    self.fail( &#39;Creation of an Enumeration with a member-name of %s should raise KeyError&#39; % ( name ) )
                except KeyError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creation of an Enumeration with a member-name of %s should raise KeyError but %s was raised instead:\n  %s&#39; % ( name, error.__class__.__name__, error ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testEnumeration ) )&lt;/pre&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;14-19&lt;/dt&gt;
    &lt;dd&gt;While poking around at some proof-of-concept PHP code at work over the 
        last few weeks, it occurred to me that a test-case class could be extended 
        upon to provide at lease some of the typical functionality I find myself 
        using. One of those typical items is the generation of lists of good and 
        bad property-values for various cases. In this case, rather than repeating 
        the &quot;good&quot; cases at least twice (see lines 33, 99), which could lead to 
        the values being tested for those methods getting out of sync and leading 
        to inconsistent testing, I&#39;m calling one method to return &lt;em&gt;all&lt;/em&gt; of 
        the good values.&lt;/dd&gt;
    &lt;dd&gt;I may very well refactor the &lt;code&gt;UnitTestUtilities&lt;/code&gt; module to make 
        use of this concept (and while I&#39;m there, maybe attach the oft-repeated 
        &lt;code&gt;testMethodCountAndTests&lt;/code&gt; and &lt;code&gt;testPropertyCountAndTests&lt;/code&gt; 
        methods so that I&#39;ll have a unit-test class that just does that automatically.&lt;/dd&gt;

    &lt;dt&gt;31-48&lt;/dt&gt;
    &lt;dd&gt;Apart from the fact that it makes use of the good-values method noted 
        above, this is pretty typical for construction-testing. Note that the 
        test-method tests both good and bad values.&lt;/dd&gt;

    &lt;dt&gt;70-83, 87-90&lt;/dt&gt;
    &lt;dd&gt;These test-methods probably look a bit odd, since they are required, but 
        they have no useful implementation. The &lt;em&gt;useful&lt;/em&gt; tests all exist 
        elsewhere, so the required test-methods simply note that this is the 
        case as a comment, and pass.&lt;/dd&gt;

    &lt;dt&gt;93-105&lt;/dt&gt;
    &lt;dd&gt;This marks the first test-method of this set that tests an overall 
        &lt;em&gt;characteristic&lt;/em&gt; of the class - in this case, it&#39;s iterability. 
        The method tests both whether the &lt;code&gt;in&lt;/code&gt; keyword works as 
        expected/desired (line 100) for each member, and the &quot;overall&quot; iterability 
        of instances (102-105).&lt;/dd&gt;

    &lt;dt&gt;108-124&lt;/dt&gt;
    &lt;dd&gt;This method tests that instances are immutable after creation (another 
        &quot;characteristic&quot;-based test-method).&lt;/dd&gt;

    &lt;dt&gt;127-135&lt;/dt&gt;
    &lt;dd&gt;This method tests that member-names specified at construction are 
        available as attributes of the instance by name, and that the values 
        behind those attribute-names match what was supplied.&lt;/dd&gt;

    &lt;dt&gt;138-149&lt;/dt&gt;
    &lt;dd&gt;This test-method was almost missed... I realized just before I was first 
        scheduling this for publication on the blog that I hadn&#39;t tested whether 
        using any of the &quot;reserved&quot; names at construction would raise the errors 
        I wanted. I could&#39;ve gone back and added these tests to the constructor 
        tests instead of making a new method specifically for them, but since 
        it&#39;s kinda critical that class members not get destroyed, it seemed 
        apropos to call the tests out in their own method.&lt;/dd&gt;

&lt;/dl&gt;

&lt;p&gt;And that wraps up the &lt;code&gt;Enumeration&lt;/code&gt;...&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/7392074397293049939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/interlude-enumerations-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/7392074397293049939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/7392074397293049939'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/interlude-enumerations-and.html' title='Interlude: Enumerations and TypedContainers'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-3661999924336126235</id><published>2012-01-06T04:30:00.000-08:00</published><updated>2012-01-06T04:30:01.858-08:00</updated><title type='text'>Where to go from here?</title><content type='html'>&lt;p&gt;With &lt;code&gt;MySQLConnector&lt;/code&gt; (and the supporting development that led up 
to it) complete, I&#39;m at something of a loss as to where to go from here. My daily 
routine hasn&#39;t settled down yet, and though I&#39;d hoped to be able to get some 
serious code-blogging time in over the week off from work between Christmas and 
New Year&#39;s, that hasn&#39;t happened. One complete computer rebuild, which pointed 
out a critical flaw in my home-system backup-plan, correcting that and trying 
like hell to back things up before the main drive on the old machine died 
completely. The random chaos of the holiday season. The demands of family while 
I&#39;m home and they&#39;re awake. The list goes on and on. I &lt;em&gt;did&lt;/em&gt; at least 
manage to get the data-connectors module (up through &lt;code&gt;MySQLConnector&lt;/code&gt;) 
completed, which I&#39;d promised myself I&#39;d get done.&lt;/p&gt;

&lt;p&gt;I have &lt;a href=&quot;/p/project-ideas.html&quot;&gt;a list of possibles to pick from&lt;/a&gt;, 
some of which are dependent on other items on that list.&lt;/p&gt;

&lt;p&gt;But the simple fact of the matter is that I&#39;m kinda stuck here. If any readers 
have a preference what I should tackle next, please comment and let me know. 
Otherwise, I&#39;ll poke around a bit and see what, if anything, strikes my fancy 
(though I have to say that I&#39;m leaning towards the &lt;a href=&quot;http://my-brain-on-python.blogspot.com/search/label/MiniAppServer&quot;&gt;MiniAppServer&lt;/a&gt; 
idea at present).&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/3661999924336126235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/where-to-go-from-here.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3661999924336126235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3661999924336126235'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/where-to-go-from-here.html' title='Where to go from here?'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-257962238826725868</id><published>2012-01-04T04:30:00.000-08:00</published><updated>2012-01-04T04:30:00.851-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BaseDataConnection"/><category scheme="http://www.blogger.com/atom/ns#" term="configuration"/><category scheme="http://www.blogger.com/atom/ns#" term="integration testing"/><category scheme="http://www.blogger.com/atom/ns#" term="MySQLConnector"/><title type='text'>The MySQLConnector module</title><content type='html'>&lt;p&gt;Happy New Year, readers! The bulk of today&#39;s code is shared at 
&lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/MySQLConnector.py&quot;&gt;dropbox.com/u/1917253/site-packages/MySQLConnector.py&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So the last leg of our journey into Python-database connectivity is (finally) 
the &lt;code&gt;MySQLConnector&lt;/code&gt; class. There&#39;s been a &lt;em&gt;lot&lt;/em&gt; of what I&#39;d 
call &quot;support&quot; development to get to this point:
    &lt;ul&gt;
        &lt;li&gt;Three interfaces (&lt;code&gt;IsDataConnector&lt;/code&gt;, &lt;code&gt;IsQuery&lt;/code&gt;, 
            and &lt;code&gt;IsResultSet&lt;/code&gt;);&lt;/li&gt;
        &lt;li&gt;One abstract class (&lt;code&gt;BaseDataConnector&lt;/code&gt;); and &lt;/li&gt;
        &lt;li&gt;Three concrete classes (&lt;code&gt;Query&lt;/code&gt;, &lt;code&gt;ResultSet&lt;/code&gt; 
            and &lt;code&gt;Record&lt;/code&gt;).&lt;/li&gt;
    &lt;/ul&gt;
These took considerably longer to write about than I&#39;d expected, though I&#39;m glad 
that they provided enough material for the number of posts that they generated. 
There&#39;s also a fair amount of code that had to be written: With unit-tests, the 
&lt;code&gt;DataConnectors&lt;/code&gt; module is just &lt;em&gt;barely&lt;/em&gt; closer to 2,400 lines 
of code than 2,300, in about a 40:60 split between &quot;real&quot; code and test-code.&lt;/p&gt;

&lt;p&gt;One (hopefully significant) advantage of this approach is that the creation 
of the &lt;code&gt;MySQLConnector&lt;/code&gt; class is going to be crazy-simple. As a 
derivative of both &lt;code&gt;BaseDataConnector&lt;/code&gt; and &lt;code&gt;IsConfigurable&lt;/code&gt;, 
there&#39;s really not a whole lot of code that&#39;s specific to the &lt;code&gt;MySQLConnector&lt;/code&gt; 
to be written, and other similar classes (say, a hypothetical &lt;code&gt;PostGreSQLConnector&lt;/code&gt;) 
would require a similarly small level of effort. Consider that &lt;code&gt;MySQLConnector&lt;/code&gt; 
has the following properties and methods:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;The &lt;code&gt;Configure&lt;/code&gt; method, from &lt;code&gt;IsConfigurable&lt;/code&gt;, which 
        &lt;em&gt;may&lt;/em&gt; need specific implementation, but that was implemented at a 
        basic level in &lt;code&gt;BaseDataConnector&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;Connect&lt;/code&gt; method, required by &lt;code&gt;IsDataConnector&lt;/code&gt;, 
        which &lt;em&gt;will&lt;/em&gt; need implementation.&lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;Database&lt;/code&gt; property, from &lt;code&gt;BaseDataConnector&lt;/code&gt;, 
        that is complete and tested until or unless we need to make 
        implementation-specific changes.&lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;Execute&lt;/code&gt; method, required by &lt;code&gt;IsDataConnector&lt;/code&gt;, 
        which &lt;em&gt;will&lt;/em&gt; need implementation.&lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;Host&lt;/code&gt; property, from &lt;code&gt;BaseDataConnector&lt;/code&gt;, also 
        complete and tested, barring implementation-specific changes.&lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;Password&lt;/code&gt; property, from &lt;code&gt;BaseDataConnector&lt;/code&gt;, also 
        complete and tested, barring implementation-specific changes.&lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;User&lt;/code&gt; property, from &lt;code&gt;BaseDataConnector&lt;/code&gt;, also 
        complete and tested, barring implementation-specific changes.&lt;/li&gt;
    &lt;li&gt;And, of course, unit-tests for the new class. Ideally, there should also 
        be some system tests to test/prove out functionality against an actual 
        database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I was working on &lt;code&gt;MySQLConnector&lt;/code&gt;, I discovered that I&#39;d never 
finished figuring out how to unit-test the &lt;code&gt;Configure&lt;/code&gt; method of it, 
and that I was missing a &lt;code&gt;Configuration&lt;/code&gt; class:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Configuration&lt;/b&gt; (from the Configuration module):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class Configuration( ConfigParser.ConfigParser, object ):
    &quot;&quot;&quot;Represents configuration data derived from a text-file.&quot;&quot;&quot;

    #################################
    # Class Attributes              #
    #################################

    #################################
    # Class Property-Getter Methods #
    #################################

    def _GetConfigFile( self ):
        &quot;&quot;&quot;Gets the config-file-path specified at object creation.&quot;&quot;&quot;
        return self._configFile

    def _GetSections( self ):
        &quot;&quot;&quot;Gets the available configuration-sections.&quot;&quot;&quot;
        if self._configSections == None:
            try:
                self.read( self._configFile )
            except Exception, error:
                raise AttributeError( &#39;%s.Sections: Could not read configuration from %s. Original error:\n\t%s&#39; % ( self.__class__.__name__, self._configFile, error ) )
            self._configSections = {}
            for theSection in self.sections():
                self._configSections[ theSection ] = {}
                for theItem in self.items( theSection, 1 ):
                    itemName = theItem[0]
                    itemValue = theItem[1]
                    if itemValue[0] in [ &#39;[&#39;, &#39;{&#39;, &#39;(&#39; ]:
                        # Evaluate the value instead... Ugly, but viable...
                        itemValue = eval( itemValue )
                    self._configSections[ theSection ][ itemName ] = itemValue
        return self._configSections

    #################################
    # Class Property-Setter Methods #
    #################################

    #################################
    # Class Properties              #
    #################################

    ConfigFile = property( _GetConfigFile, None, None, _GetConfigFile.__doc__ )
    Sections = property( _GetSections, None, None, _GetSections.__doc__ )

    #################################
    # Object Constructor            #
    #################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;filePath&#39;, None, &#39;(String) The path to the configuration-file.&#39; )
    def __init__( self, filePath ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        ConfigParser.ConfigParser.__init__( self )
        self._configSections = None
        self._configFile = filePath

    #################################
    # Object Destructor             #
    #################################

    #################################
    # Class Methods                 #
    #################################

__all__ += [ &#39;Configuration&#39; ]&lt;/pre&gt;

&lt;p&gt;This will likely need to be refactored at some point, since there are other 
configuration mechanisms than the &lt;code&gt;ConfigParser&lt;/code&gt; base here. For now, though, 
I&#39;ll leave it be. &lt;code&gt;Configuration&lt;/code&gt; instances are basically just wrappers 
around &lt;code&gt;ConfigParser&lt;/code&gt; instances, that provide &lt;code&gt;ConfigFile&lt;/code&gt; 
and &lt;code&gt;Sections&lt;/code&gt; properties. The &lt;code&gt;ConfigFile&lt;/code&gt; is probably 
pretty straightforward - it&#39;s a pointer to the configuration-file that the 
instance uses. &lt;code&gt;Sections&lt;/code&gt; might be a little harder to understand, 
though - I wanted to have a simple access mechanism for the configuration-sections 
of a given configuration-file, in order to be able to retrieve section-items 
without having to make a function-call every time.&lt;/p&gt;

&lt;p&gt;With that in place, I could finish the unit-tests for &lt;code&gt;BaseDataConnector:&lt;/code&gt;&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;        def testConfigure( self ):
            &quot;&quot;&quot;Unit-tests the Configure method of the BaseDataConnector abstract class.&quot;&quot;&quot;
            # First, we need to build out a configuration-file
            hostName = &#39;hostName&#39;
            databaseName = &#39;databaseName&#39;
            userName = &#39;userName&#39;
            userPassword = &#39;userPassword&#39;
            configString = &quot;&quot;&quot;[Default Section]
SectionName1: SectionValue1
SectionName2: SectionValue2

[Data Access]
host: %s
database: %s
user: %s
password: %s
&quot;&quot;&quot; % ( hostName, databaseName, userName, userPassword )
            fp = open( &#39;test.cfg&#39;, &#39;w&#39; )
            fp.write( configString )
            fp.close()
            # Now we need to create the configuration object and read in the configuration values
            configParser = Configuration( &#39;test.cfg&#39; )
            # Now, finally, we can test the configuration
            testObject = BaseDataConnectorDerived()
            testObject.Configure( configParser, &#39;Data Access&#39; )
            self.assertEquals( testObject.Host, hostName, &#39;The host name should be retrievable from configuration&#39; )
            self.assertEquals( testObject.Database, databaseName, &#39;The database name should be retrievable from configuration&#39; )
            self.assertEquals( testObject.User, userName, &#39;The user name should be retrievable from configuration&#39; )
            self.assertEquals( testObject.Password, userPassword, &#39;The user password should be retrievable from configuration&#39; )
            os.unlink( &#39;test.cfg&#39; )&lt;/pre&gt;

&lt;p&gt;All this is doing is creating a hard-coded (and very simple) configuration-file, 
with a &lt;code&gt;Data Access&lt;/code&gt; section that contains the host, database and user 
names for the connector, and the password that it should use. Looking at lines 22 and
24-25, creation of a &lt;code&gt;BaseDataConnector&lt;/code&gt;-derived instance &lt;em&gt;should&lt;/em&gt; 
always be feasible with this sort of code-structure: Create an instance with no 
arguments, create a configuration instance, then call the &lt;code&gt;Configure&lt;/code&gt; 
method of the data-connector instance.&lt;/p&gt;

&lt;p&gt;It may bear noting that &lt;code&gt;MySQLConnector&lt;/code&gt; doesn&#39;t have any sort of 
direct support for cursors - that may be something that I&#39;ll add in to 
&lt;code&gt;BaseDataConnector&lt;/code&gt; at some point in the future, &lt;em&gt;if&lt;/em&gt; there&#39;s 
support for it across MySQL (yes), PostgreSQL (not sure), and ODBC (also not sure), 
which are the three data-connection types I expect this to support in the long 
run.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;MySQLConnector&lt;/b&gt; (Nominal final class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;#####################################
# Convenience imports               #
#####################################

# See To-do list on MySQLConnector.__init__

#####################################
# Classes defined in the module     #
#####################################

class MySQLConnector( BaseDataConnector, IsConfigurable, object ):
    &quot;&quot;&quot;Class doc-string.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetConnection( self ):
        &quot;&quot;&quot;Gets the connection to the specified MySQL database.&quot;&quot;&quot;
        if self._connection == None:
            self.Connect()
        return self._connection

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The name of the database the connection will be made to.&#39; )
    def _SetDatabase( self, value ):
        if self._connection != None:
            raise AttributeError( &#39;%s.Database cannot be reset once a connection is established&#39; % ( self.__class__.__name__ ) )
        BaseDataConnector._SetDatabase( self, value )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The name or IP address of the host that the database being connected to lives on.&#39; )
    def _SetHost( self, value ):
        if self._connection != None:
            raise AttributeError( &#39;%s.Host cannot be reset once a connection is established&#39; % ( self.__class__.__name__ ) )
        BaseDataConnector._SetHost( self, value )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The password that will be used to connect to the database.&#39; )
    def _SetPassword( self, value ):
        if self._connection != None:
            raise AttributeError( &#39;%s.Password cannot be reset once a connection is established&#39; % ( self.__class__.__name__ ) )
        BaseDataConnector._SetPassword( self, value )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The user-name that will be used to connect to the database.&#39; )
    def _SetUser( self, value ):
        if self._connection != None:
            raise AttributeError( &#39;%s.User cannot be reset once a connection is established&#39; % ( self.__class__.__name__ ) )
        BaseDataConnector._SetUser( self, value )

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelConnection( self ):
        self._connection = None

    ##################################
    # Class Properties               #
    ##################################

    Connection = property( _GetConnection, None, None, &#39;Gets the connection to the specified MySQL database.&#39; )
    Database = property( BaseDataConnector._GetDatabase, _SetDatabase, BaseDataConnector._DelDatabase, BaseDataConnector._GetDatabase.__doc__ )
    Host = property( BaseDataConnector._GetHost, _SetHost, BaseDataConnector._DelHost, BaseDataConnector._GetHost.__doc__ )
    Password = property( BaseDataConnector._GetPassword, _SetPassword, BaseDataConnector._DelPassword, BaseDataConnector._GetPassword.__doc__ )
    User = property( BaseDataConnector._GetUser, _SetUser, BaseDataConnector._DelUser, BaseDataConnector._GetUser.__doc__ )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;host&#39;, &#39;The name or IP-address of the host that the database resides upon.&#39; )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;database&#39;, &#39;The name of the database that the connection will be made to.&#39; )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;user&#39;, &#39;The user-name used to connect to the database.&#39; )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;password&#39;, &#39;The password used to connect to the database.&#39; )
    @ToDo( &#39;Figure out what items from DataConnectors should be imported for convenience in using the MySQLConnector class.&#39; )
    def __init__( self, **parameters ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != MySQLConnector:
            raise NotImplementedError( &#39;MySQLConnector is (nominally) a final class, and is not intended to be derived from.&#39; )
        self._connection = None
        BaseDataConnector.__init__( self, **parameters )

    ##################################
    # Object Destructor              #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __del__( self ):
        self._Close()

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _Close( self ):
        if self._connection:
            self._connection.close()

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def Connect( self ):
        &quot;&quot;&quot;Connects to the database specified by Database, residing on Host, using User and Password to connect.&quot;&quot;&quot;
        self._connection = MySQLdb.connect( 
            host=self._host, 
            db=self._database, 
            user=self._user, 
            passwd=self._password
            )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;query&#39;, None, &#39;The IsQuery object that will be executed against the object\&#39;s Connection.&#39; )
    def Execute( self, query ):
        &quot;&quot;&quot;Executes the provided IsQuery object&#39;s query against the object&#39;s connection, returning one or more results.&quot;&quot;&quot;
        if not isinstance( query, IsQuery ):
            raise TypeError( &#39;%s.Execute expects an instance of IsQuery as it\&#39;s query argument.&#39; % ( self.__class__.__name__ ) )
        self.Connection.query( query.Sql )
        resultSets = []
        resultSet = []
        results = self.Connection.store_result()
        while results:
            row = list( results.fetch_row( 1, 1 ) )
            while row:
                resultSet += row
                row = results.fetch_row( 1, 1 )
            if self.Connection.next_result() == 0:
                resultSets.append( resultSet )
                resultSet = []
                results = self.Connection.store_result()
            else:
                break
        resultSets.append( resultSet )
        return resultSets

__all__ += [ &#39;MySQLConnector&#39; ]&lt;/pre&gt;

&lt;dl&gt;

    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;5&lt;/dt&gt;
    &lt;dd&gt;This is a fine-tuning item that will almost certainly evolve in an obvious 
        way as I start writing code that actually &lt;em&gt;uses&lt;/em&gt; &lt;code&gt;MySQLConnector&lt;/code&gt;: 
        There will almost certainly be some (small?) set of imports from the base 
        &lt;code&gt;DataConnectors&lt;/code&gt; module that will be needed frequently enough 
        to warrant importing them as part of the &lt;code&gt;MySQLConnector&lt;/code&gt; 
        import. Off the top of my head, I&#39;d expect at least &lt;code&gt;Query&lt;/code&gt; 
        and maybe &lt;code&gt;ResultSet&lt;/code&gt; to be needed, but I&#39;ll wait to see what 
        actually crops up as I start using the module...&lt;/dd&gt;

    &lt;dt&gt;23-27&lt;/dt&gt;
    &lt;dd&gt;An almost-typical property getter method, the main difference being that 
        the underlying MySQLdb &lt;code&gt;Connection&lt;/code&gt; object isn&#39;t instantiated 
        until it&#39;s needed.&lt;/dd&gt;

    &lt;dt&gt;33-59&lt;/dt&gt;
    &lt;dd&gt;Overridden property-setter methods. The only additional functionality 
        that they provide is to prevent the modification of any connection-control 
        property (the host, database- and user-name, and password used for the 
        connection) once the connection has been instantiated. The rationale for 
        this is, basically, that once a connection has been established, changes 
        to that connection should not be allowed. Chances are good that an error 
        of some type would be thrown anyway, but since I&#39;d rather raise errors 
        as close to their &quot;real&quot; source as possible, I&#39;m explicitly causing that 
        to happen here.&lt;/dd&gt;

    &lt;dt&gt;73-77&lt;/dt&gt;
    &lt;dd&gt;The actual property declarations. Since most of these are a mix of the 
        functionality from &lt;code&gt;BaseDataConnector&lt;/code&gt; (for the &lt;code&gt;_Get...&lt;/code&gt; 
        methods) and local implementations (&lt;code&gt;_Set...&lt;/code&gt; methods), there&#39;s 
        a mix of complete external method-references and &quot;normal&quot; local references 
        to accommodate.&lt;/dd&gt;

    &lt;dt&gt;101-103, 109-112&lt;/dt&gt;
    &lt;dd&gt;I&#39;m not sure that a formal object-destructor&#39;s actually &lt;em&gt;needed&lt;/em&gt;, 
        since the underlying MySQLdb &lt;code&gt;Connection&lt;/code&gt; object &lt;em&gt;probably&lt;/em&gt; 
        closes the database connection all on it&#39;s own, but to be safe, I&#39;m providing 
        one that calls to the &lt;code&gt;_Close&lt;/code&gt; method to make &lt;em&gt;sure&lt;/em&gt; that 
        the connection is closed cleanly when the object is destroyed.&lt;/dd&gt;
    &lt;dd&gt;The &lt;code&gt;_Close&lt;/code&gt; method is necessary because when the destructor 
        fires, it doesn&#39;t recognize object properties. I think I mentioned this 
        same pattern somewhere before, but I cannot recall where, exactly.&lt;/dd&gt;

    &lt;dt&gt;114-122&lt;/dt&gt;
    &lt;dd&gt;A public method to open the database-connection with the instance&#39;s parameters.&lt;/dd&gt;

    &lt;dt&gt;124-144&lt;/dt&gt;
    &lt;dd&gt;The method that actually queries against the database, returning a list of 
        lists of dictionaries, ready to be passed to &lt;code&gt;ResultSet&lt;/code&gt; 
        constructors.
        &lt;dl&gt;

            &lt;dt&gt;128-129&lt;/dt&gt;
            &lt;dd&gt;Typical type-checking of the supplied &lt;code&gt;query&lt;/code&gt; argument.&lt;/dd&gt;

            &lt;dt&gt;130&lt;/dt&gt;
            &lt;dd&gt;Run the provided Query&#39;s SQL against the database, readying the 
                results for formatting/digestion.&lt;/dd&gt;

            &lt;dt&gt;131, 132&lt;/dt&gt;
            &lt;dd&gt;Prep the lists of all results, and the first list of rows for use.&lt;/dd&gt;

            &lt;dt&gt;133&lt;/dt&gt;
            &lt;dd&gt;Get (and store) the first result-set returned.&lt;/dd&gt;
            &lt;dd&gt;This uses &lt;code&gt;store_result&lt;/code&gt; instead of &lt;code&gt;use_result&lt;/code&gt;, 
                in the belief that a well-designed application (and database) 
                should rarely return so many records that storing them wouldn&#39;t 
                provide faster throughput than keeping the database-connection 
                open and retrieving result-sets one at a time.&lt;/dd&gt;

            &lt;dt&gt;134&lt;/dt&gt;
            &lt;dd&gt;For so long as there are results:
                &lt;dl&gt;

                    &lt;dt&gt;135&lt;/dt&gt;
                    &lt;dd&gt;Get the first row of the current result-set&lt;/dd&gt;

                    &lt;dt&gt;136-137&lt;/dt&gt;
                    &lt;dd&gt;For so long as there&#39;s a valid row, add the row to the 
                        current result-set, and get the next row.&lt;/dd&gt;

                    &lt;dt&gt;139-142&lt;/dt&gt;
                    &lt;dd&gt;Once the current result&#39;s row-set has been processed, 
                        if there is another result-set after the current one, 
                        append the current results to the list to be returned, 
                        reset the row-list, and get/store the next result-set.&lt;/dd&gt;

                    &lt;dt&gt;143-144&lt;/dt&gt;
                    &lt;dd&gt;If there are no other result-sets, then exit the loop.&lt;/dd&gt;

                &lt;/dl&gt;
            &lt;/dd&gt;

            &lt;dt&gt;145&lt;/dt&gt;
            &lt;dd&gt;Make &lt;em&gt;sure&lt;/em&gt; that the last set of rows gets attached to the 
                results to be returned before returning them.&lt;/dd&gt;

        &lt;/dl&gt;
    &lt;/dd&gt;

&lt;/dl&gt;

&lt;p&gt;Unit-tests&lt;/p&gt;

&lt;p&gt;Some of the unit-tests (the ones that are very basic system-tests, all in the 
testExecute method) require some actual tables on the test_database database:&lt;/p&gt;

&lt;pre&gt;CREATE TABLE `test_database`.`test_table_1` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT &#39;The record-id&#39;,
  `first_name` VARCHAR(30)  NOT NULL COMMENT &#39;A first name&#39;,
  `last_name` varchar(30)  NOT NULL COMMENT &#39;A last-name&#39;,
  PRIMARY KEY (`id`)
)
ENGINE = MyISAM
COMMENT = &#39;A test-table&#39;;

INSERT INTO test_table_1 ( first_name, last_name )
VALUES ( &#39;Brian&#39;, &#39;Allbee&#39; ), ( &#39;Joe&#39;, &#39;Smith&#39; );

CREATE TABLE `test_database`.`test_table_2` (
  `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT &#39;Record-ID&#39;,
  `name_id` int  NOT NULL 
      COMMENT &#39;ID of the record from test_table_1 that relates to this record&#39;,
  `email` varchar(60)  NOT NULL COMMENT &#39;An email address&#39;,
  PRIMARY KEY (`id`)
)
ENGINE = MyISAM
COMMENT = &#39;Another test table&#39;;

INSERT INTO test_table_2 ( name_id, email )
VALUES ( 1, &#39;brian.allbee@somedomain.com&#39; ), ( 2, &#39;joe.smith@somedomain.com&#39; );
&lt;/pre&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class MySQLConnectorDerived( MySQLConnector ):
        def __init__( self, **parameters ):
            MySQLConnector.__init__( self, **parameters )
        def __del__( self ):
            try:
                MySQLConnector.__del__( self )
            except:
                pass
    
    class testMySQLConnector( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the MySQLConnector class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass

        def getLiveConnection( self ):
            &quot;&quot;&quot;Returns a MySQLConnector instance pointing to a common local test-database.&quot;&quot;&quot;
            return MySQLConnector( host=&#39;localhost&#39;, database=&#39;test_database&#39;, user=&#39;test_user&#39;, password=&#39;test_password&#39; )
        
        def testFinal( self ):
            &quot;&quot;&quot;Testing final nature of the MySQLConnector class.&quot;&quot;&quot;
            try:
                testObject = MySQLConnectorDerived()
                self.fail( &#39;MySQLConnector is nominally a final class, and should not be extendable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Instantiating a derivation of MySQLConnection should raise NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the MySQLConnector class.&quot;&quot;&quot;
            testObject = MySQLConnector()
            self.assertTrue( isinstance( testObject, MySQLConnector ), &#39;Instances of MySQLConnector should be instances of MySQLConnector.&#39; )
            self.assertEquals( testObject.Host, None, &#39;With no host supplied at construction, it\&#39;s property value should be None&#39; )
            self.assertEquals( testObject.Database, None, &#39;With no database supplied at construction, it\&#39;s property value should be None&#39; )
            self.assertEquals( testObject.User, None, &#39;With no user supplied at construction, it\&#39;s property value should be None&#39; )
            self.assertEquals( testObject.Password, None, &#39;With no password supplied at construction, it\&#39;s property value should be None&#39; )
            testHost = &#39;testHost&#39;
            testDatabase = &#39;database&#39;
            testUser = &#39;testUser&#39;
            testPassword = &#39;testPassword&#39;
            testObject = MySQLConnector( host=testHost, database=testDatabase, user=testUser, password=testPassword )
            self.assertEquals( testObject.Host, testHost, &#39;The host supplied at construction should be present in the object\&#39;s properties&#39; )
            self.assertEquals( testObject.Database, testDatabase, &#39;The database supplied at construction should be present in the object\&#39;s properties&#39; )
            self.assertEquals( testObject.User, testUser, &#39;The user supplied at construction should be present in the object\&#39;s properties&#39; )
            self.assertEquals( testObject.Password, testPassword, &#39;The password supplied at construction should be present in the object\&#39;s properties&#39; )
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the MySQLConnector class.&quot;&quot;&quot;
            items = getMemberNames( MySQLConnector )[0]
            actual = len( items )
            expected = 5
            self.assertEquals( expected, actual, &#39;MySQLConnector is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the MySQLConnector class.&quot;&quot;&quot;
            items = getMemberNames( MySQLConnector )[1]
            actual = len( items )
            expected = 3
            self.assertEquals( expected, actual, &#39;MySQLConnector is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Unit-test properties

        def testConnection( self ):
            &quot;&quot;&quot;Unit-tests the Connection property of the MySQLConnector class.&quot;&quot;&quot;
            testObject = MySQLConnector( host=&#39;localhost&#39;, database=&#39;no_such_database&#39;, user=&#39;test_user&#39;, password=&#39;test_password&#39; )
            try:
                testObject.Connect()
                self.fail( &#39;Trying to connect to a non-existant database should raise OperationalError.&#39; )
            except MySQLdb.OperationalError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to connect to a non-existant database should raise OperationalError, but %s was raised instead:\n  %s.&#39; % ( error.__class__.__name__, error ) )

        def testDatabase( self ):
            &quot;&quot;&quot;Unit-tests the Database property of the MySQLConnector class.&quot;&quot;&quot;
            testObject = self.getLiveConnection()
            oldDatabase = testObject.Database
            testObject.Database = &#39;ook&#39;
            self.assertEquals( testObject.Database, &#39;ook&#39;, &#39;Before a connection is made, the object should allow modification of the Database property.&#39; )
            testObject.Database = oldDatabase
            testObject.Connect()
            try:
                testObject.Database = &#39;ook&#39;
                self.fail( &#39;Once a connection is established, the Database property should be immutable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Modifying the Database property after connection is establilshed should raise AttributeError, bu %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testHost( self ):
            &quot;&quot;&quot;Unit-tests the Host property of the MySQLConnector class.&quot;&quot;&quot;
            testObject = self.getLiveConnection()
            oldHost = testObject.Host
            testObject.Host = &#39;ook&#39;
            self.assertEquals( testObject.Host, &#39;ook&#39;, &#39;Before a connection is made, the object should allow modification of the Host property.&#39; )
            testObject.Host = oldHost
            testObject.Connect()
            try:
                testObject.Host = &#39;ook&#39;
                self.fail( &#39;Once a connection is established, the Host property should be immutable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Modifying the Host property after connection is establilshed should raise AttributeError, bu %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testPassword( self ):
            &quot;&quot;&quot;Unit-tests the Password property of the MySQLConnector class.&quot;&quot;&quot;
            testObject = self.getLiveConnection()
            oldPassword = testObject.Password
            testObject.Password = &#39;ook&#39;
            self.assertEquals( testObject.Password, &#39;ook&#39;, &#39;Before a connection is made, the object should allow modification of the Password property.&#39; )
            testObject.Password = oldPassword
            testObject.Connect()
            try:
                testObject.Password = &#39;ook&#39;
                self.fail( &#39;Once a connection is established, the Password property should be immutable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Modifying the Password property after connection is establilshed should raise AttributeError, bu %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

#        def testQueue( self ):
#            &quot;&quot;&quot;Unit-tests the Queue property of the MySQLConnector class.&quot;&quot;&quot;
#            pass

        def testUser( self ):
            &quot;&quot;&quot;Unit-tests the User property of the MySQLConnector class.&quot;&quot;&quot;
            testObject = self.getLiveConnection()
            oldUser = testObject.User
            testObject.User = &#39;ook&#39;
            self.assertEquals( testObject.User, &#39;ook&#39;, &#39;Before a connection is made, the object should allow modification of the User property.&#39; )
            testObject.User = oldUser
            testObject.Connect()
            try:
                testObject.User = &#39;ook&#39;
                self.fail( &#39;Once a connection is established, the User property should be immutable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Modifying the User property after connection is establilshed should raise AttributeError, bu %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        # Unit-test methods

        def testConfigure( self ):
            &quot;&quot;&quot;Unit-tests the Configure method of the MySQLConnector class&quot;&quot;&quot;
        def testConfigure( self ):
            &quot;&quot;&quot;Unit-tests the Configure method of the BaseDataConnector abstract class.&quot;&quot;&quot;
            # First, we need to build out a configuration-file
            hostName = &#39;localhost&#39;
            databaseName = &#39;test_database&#39;
            userName = &#39;test_user&#39;
            userPassword = &#39;test_password&#39;
            configString = &quot;&quot;&quot;[Default Section]
SectionName1: SectionValue1
SectionName2: SectionValue2

[Data Access]
host: %s
database: %s
user: %s
password: %s
&quot;&quot;&quot; % ( hostName, databaseName, userName, userPassword )
            fp = open( &#39;test.cfg&#39;, &#39;w&#39; )
            fp.write( configString )
            fp.close()
            # Now we need to create the configuration object and read in the configuration values
            configParser = Configuration( &#39;test.cfg&#39; )
            # Now, finally, we can test the configuration
            testObject = MySQLConnector()
            testObject.Configure( configParser, &#39;Data Access&#39; )
            self.assertEquals( testObject.Host, hostName, &#39;The host name should be retrievable from configuration&#39; )
            self.assertEquals( testObject.Database, databaseName, &#39;The database name should be retrievable from configuration&#39; )
            self.assertEquals( testObject.User, userName, &#39;The user name should be retrievable from configuration&#39; )
            self.assertEquals( testObject.Password, userPassword, &#39;The user password should be retrievable from configuration&#39; )
            os.unlink( &#39;test.cfg&#39; )

        def testConnect( self ):
            &quot;&quot;&quot;Unit-tests the Connect method of the MySQLConnector class.&quot;&quot;&quot;
            pass # Tested in the various property-tests above...

        def testExecute( self ):
            &quot;&quot;&quot;Unit-tests the Execute method of the MySQLConnector class.&quot;&quot;&quot;
            testObject = self.getLiveConnection()
            # Single-table, single row, single field
            MyQuery = Query( testObject, &quot;SELECT first_name FROM test_table_1 LIMIT 1;&quot; )
            testResults = testObject.Execute( MyQuery )
            self.assertEquals( len( testResults ), 1, &#39;%s should return a result-set&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0] ), 1, &#39;%s should return a single row&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[0][0].keys(), [ &#39;first_name&#39; ], &#39;%s should return only the first_name field&#39; % ( MyQuery.Sql ) )
            # Single-table, single row, all fields
            MyQuery = Query( testObject, &quot;SELECT * FROM test_table_1 LIMIT 1;&quot; )
            testResults = testObject.Execute( MyQuery )
            self.assertEquals( len( testResults ), 1, &#39;%s should return a result-set&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0] ), 1, &#39;%s should return a single row&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0][0] ), 3, &#39;%s should return three fields&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[0][0].keys(), [ &#39;first_name&#39;, &#39;last_name&#39;, &#39;id&#39; ], &#39;%s should return first_name, last_name and id fields&#39; % ( MyQuery.Sql ) )
            # Single-table, all rows, one field
            MyQuery = Query( testObject, &quot;SELECT first_name FROM test_table_1;&quot; )
            testResults = testObject.Execute( MyQuery )
            self.assertEquals( len( testResults ), 1, &#39;%s should return a result-set&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0] ), 2 )
            self.assertEquals( testResults[0][0].keys(), [ &#39;first_name&#39; ] )
            self.assertEquals( testResults[0][0].keys(), testResults[0][1].keys(), &#39;Field-names in different rows of the result-set should be identical&#39; )
            # Single-table, all rows, all fields
            MyQuery = Query( testObject, &quot;SELECT * FROM test_table_1;&quot; )
            testResults = testObject.Execute( MyQuery )
            self.assertEquals( len( testResults ), 1, &#39;%s should return a result-set&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0] ), 2, &#39;%s should return two rows&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0][0] ), 3, &#39;%s should return three fields&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[0][0].keys(), [ &#39;first_name&#39;, &#39;last_name&#39;, &#39;id&#39; ], &#39;%s should return first_name, last_name and id fields&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[0][0].keys(), testResults[0][1].keys(), &#39;Field-names in different rows of the result-set should be identical&#39; )
            # Multiple tables (joined), all fields
            MyQuery = Query( testObject, &quot;SELECT t1.*, t2.* FROM test_table_1 t1 LEFT JOIN test_table_2 t2 ON t1.id=t2.name_id;&quot; )
            testResults = testObject.Execute( MyQuery )
            self.assertEquals( len( testResults ), 1, &#39;%s should return a result-set&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0] ), 2, &#39;%s should return two rows&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[0][0].keys(), [&#39;first_name&#39;, &#39;last_name&#39;, &#39;t2.id&#39;, &#39;email&#39;, &#39;name_id&#39;, &#39;id&#39;] )
            self.assertEquals( testResults[0][0].keys(), testResults[0][1].keys(), &#39;Field-names in different rows of the result-set should be identical&#39; )
            # Multiple tables, multiple result-sets
            MyQuery = Query( testObject, &quot;SELECT * FROM test_table_1;SELECT * FROM test_table_2;&quot; )
            testResults = testObject.Execute( MyQuery )
            self.assertEquals( len( testResults ), 2, &#39;%s should return two result-sets&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[0] ), 2, &#39;%s should return two rows in it\&#39;s first result-set&#39; % ( MyQuery.Sql ) )
            self.assertEquals( len( testResults[1] ), 2, &#39;%s should return two rows in it\&#39;s second result-set&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[0][0].keys(), [ &#39;first_name&#39;, &#39;last_name&#39;, &#39;id&#39; ], &#39;%s should return first_name, last_name and id fields&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[0][0].keys(), testResults[0][1].keys(), &#39;Field-names in different rows of the result-set should be identical&#39; )
            self.assertEquals( testResults[1][0].keys(), [&#39;name_id&#39;, &#39;id&#39;, &#39;email&#39;], &#39;%s should return name_id, id and email fields&#39; % ( MyQuery.Sql ) )
            self.assertEquals( testResults[1][0].keys(), testResults[1][1].keys(), &#39;Field-names in different rows of the result-set should be identical&#39; )
            # Query that returns no results
            MyQuery = Query( testObject, &quot;SELECT * FROM test_table_1 WHERE first_name=&#39;ook&#39;;&quot; )
            testResults = testObject.Execute( MyQuery )
            self.assertEquals( len( testResults ), 1 )
            self.assertEquals( len( testResults[0] ), 0 )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testMySQLConnector ) )&lt;/pre&gt;

&lt;dl&gt;

    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;1-8&lt;/dt&gt;
    &lt;dd&gt;An almost-typical derived-test class. The main difference here is the 
        inclusion of an explicit object-destructor that cannot throw errors.&lt;/dd&gt;
    &lt;dd&gt;Unit-testing the object-destructor in any sort of meaningful way has 
        eluded me still - I don&#39;t like not having such a test, but I&#39;m recdonciled 
        to having to take it on faith that the destructor is doing what it&#39;s 
        supposed to do...&lt;/dd&gt;

    &lt;dt&gt;19-21&lt;/dt&gt;
    &lt;dd&gt;A convenience method to return a valid &lt;code&gt;MySQLConnector&lt;/code&gt; 
        instance pointing to the test database.&lt;/dd&gt;

    &lt;dt&gt;23-31&lt;/dt&gt;
    &lt;dd&gt;Standard final-class testing.&lt;/dd&gt;

    &lt;dt&gt;33-49&lt;/dt&gt;
    &lt;dd&gt;Object-construction testing. Note that the properties being passed as 
        arguments to the constructor are being explicitly verified, though they 
        shouldn&#39;t have to be as long as &lt;code&gt;MySQLConnector&lt;/code&gt; remains true 
        to the interface of &lt;code&gt;BaseDataConnector&lt;/code&gt;. These are in place 
        as much to make sure that potential future breaking changes underneath 
        the &lt;code&gt;MySQLConnector&lt;/code&gt; class would be caught before introducing 
        bugs to a production system.&lt;/dd&gt;

&lt;/dl&gt;

&lt;p&gt;With the exception of the &lt;code&gt;testConfigure&lt;/code&gt; and &lt;code&gt;testExecute&lt;/code&gt; 
methods, most of the rest of the tests provided here are pretty typical. The 
&lt;code&gt;testConfigure&lt;/code&gt; method is almost identical to the same method in the 
test-suite for &lt;code&gt;BaseDataConnector&lt;/code&gt;, but is provided as a separate 
test-set in case of future modification to the &lt;code&gt;Configure&lt;/code&gt; method of 
either &lt;code&gt;BaseDataConnector&lt;/code&gt; or &lt;code&gt;MySQLConnector&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;testExecute&lt;/code&gt;, on the other hand, is an entirely different flavor 
of unicorn. This is really more of a system/integration test than a &quot;real&quot; 
unit-test, though I think it may straddle the line to some degree. It&#39;s necessary 
because the &lt;code&gt;Execute&lt;/code&gt; method of &lt;code&gt;MySQLConnector&lt;/code&gt; exists, 
but at the same time, there&#39;s no good way to &lt;em&gt;really&lt;/em&gt; test it without 
making a connection to the database, and running queries against that connection. 
I&#39;ll hammer out some additional (and more detailed) integration tests that will 
eventually be added to the test-suite for &lt;code&gt;MySQLConnector&lt;/code&gt;, but for now, 
this feels pretty good: Given the two tables of two rows present in the test 
database, I&#39;m pretty confident that I&#39;ve hit all of the meaningful variations 
of number of results, rows and fields that could come up against that data-set.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/257962238826725868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/mysqlconnector-module.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/257962238826725868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/257962238826725868'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2012/01/mysqlconnector-module.html' title='The MySQLConnector module'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-4295986771231233859</id><published>2011-12-23T04:30:00.000-08:00</published><updated>2011-12-23T04:30:00.382-08:00</updated><title type='text'>Taking a break...</title><content type='html'>&lt;p&gt;With the changes in my routine after my recent job-change, I&#39;m going to take a week off from posting, and try to get more backlog built up during the week off from work.&lt;/p&gt;

&lt;p&gt;Check back in on January 4th - I should have material enough written by then to resume my normal posting-schedule, I hope... And, at the very least, I&#39;ll have finished &lt;code&gt;MySQLConnector&lt;/code&gt;, and started in on some system/integration testing of it.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/4295986771231233859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/taking-break.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4295986771231233859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4295986771231233859'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/taking-break.html' title='Taking a break...'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-2852398645695633884</id><published>2011-12-21T04:30:00.000-08:00</published><updated>2011-12-21T04:30:04.019-08:00</updated><title type='text'>Defending Class Finality in Python</title><content type='html'>&lt;p&gt;Back when &lt;code&gt;Record&lt;/code&gt; was defined in &lt;code&gt;DataConnectors&lt;/code&gt; 
(part 4), I had an inkling that a nominally final class would be viewed as 
&quot;not Pythonic.&quot; I still struggle with what, exactly &quot;pythonic&quot; really &lt;em&gt;means&lt;/em&gt;, 
though there are several results that look promising for various searches on 
Google, including &lt;a href=&quot;http://www.google.com/search?q=pythonic+code&quot;&gt;pythonic 
code&lt;/a&gt;. The reason that the precise definition of &lt;abbr&gt;Pythonic&lt;/abbr&gt; is a 
sticking point for me (at least right now) is that when I was looking to see if 
there was a standard mechanism for making final (non-extendable) classes, I ran 
across several mail-list items, blog-posts, and other resources on the web that 
all seemed to present more or less the same reason for discouraging class finality 
in any way:

&lt;blockquote&gt;Who are &lt;em&gt;you&lt;/em&gt; to say that a class cannot be extended?&lt;/blockquote&gt;

Having given it some thought, I have a two-part response (not necessarily an 
&lt;em&gt;answer&lt;/em&gt;) to that question:

&lt;blockquote&gt;I am the person who created the class in the first place, who thus 
arguably knows (and at least &lt;em&gt;should&lt;/em&gt; know) what all of the design- and 
implementation-decisions were for it, and who is the most likely to know what is 
going to break if a haphazard extension of that class is made.&lt;/blockquote&gt;

&lt;blockquote&gt;Given the very nature of Python, and (at least for my part) the 
intention of releasing the &lt;em&gt;code&lt;/em&gt; rather than just the &lt;em&gt;executable&lt;/em&gt; 
in good open-source tradition, by making a class nominally final as I have in a 
few cases so far, I am not saying that the class &lt;em&gt;cannot&lt;/em&gt; be extended. 
Anyone who feels the need to extendit has access to the code, and can remove the 
nominally-final constraint I have put in place. What I &lt;em&gt;am&lt;/em&gt; saying is that 
the class wasn&#39;t &lt;em&gt;intended&lt;/em&gt; to be extended, and if you want to extend it, 
you&#39;d better at least &lt;em&gt;look&lt;/em&gt; at my code first.&lt;/blockquote&gt;

&lt;/p&gt;

&lt;p&gt;As to what &quot;Pythonic&quot; means...? Well, &lt;a href=&quot;http://chrisarndt.de/talks/rupy/2008/output/slides.html&quot;&gt;this&lt;/a&gt; 
seems to be a popular (and detailed) explanation. I&#39;m not very far into it just 
yet, but at the very least, it looks to be an enjoyable read...&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/2852398645695633884/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/defending-class-finality-in-python.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/2852398645695633884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/2852398645695633884'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/defending-class-finality-in-python.html' title='Defending Class Finality in Python'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-4249131024830351793</id><published>2011-12-19T04:30:00.001-08:00</published><updated>2011-12-19T04:30:00.475-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="concrete class"/><category scheme="http://www.blogger.com/atom/ns#" term="DataConnectors"/><category scheme="http://www.blogger.com/atom/ns#" term="Query"/><title type='text'>The DataConnectors module (part 6)</title><content type='html'>&lt;p&gt;So, the last generic-but-concrete class that I was working on, you may remember, 
was the &lt;code&gt;Query&lt;/code&gt; class. It was stalled for a time because I wasn&#39;t sure 
how I &lt;em&gt;really&lt;/em&gt; wanted the process of executing a query against a database 
to work. My primary concern was which object was going to be responsible for what 
part of the result-acquisition.&lt;/p&gt;

&lt;p&gt;In retrospect, this feels to me now like the entire question is tied to making 
a decision about where the line should be drawn for the logical encapsulation-points 
for the relevant classes, and to some extent, it&#39;s also partly a question of how 
tightly coupled the classes themselves are. Actually, there are several principles 
that I&#39;m trying to find a good balance between for this part of the object-stack, 
so I&#39;ll list them out, and examine different scenarios against them:
&lt;dl&gt;
    &lt;dt&gt;Encapsulate what varies&lt;/dt&gt;
    &lt;dd&gt;&quot;When designing software, look for the portions most likely to change and 
        prepare them for future expansion by shielding the rest of the program 
        from that change. Hide the potential variation behind an interface. Then, 
        when the implementation changes, software written to the interface doesn&#39;t 
        need to change. This is called encapsulating variation.&quot; (from 
        &lt;a href=&quot;http://blogs.msdn.com/b/steverowe/archive/2007/12/26/encapsulate-what-varies.aspx&quot;&gt;blogs.msdn.com/b/steverowe&lt;/a&gt;). 
        There may be other methods than hiding things behind an interface, but 
        that&#39;s likely the most common (and best?) practice.&lt;/dd&gt;

    &lt;dt&gt;Program to an interface, not to an implementation&lt;/dt&gt;
    &lt;dd&gt;Any two objects that conform to the same interface should be functionally 
        interchangeable, so that other objects that care only about that interface 
        then don&#39;t have to know or care about the implementation underneath it.&lt;/dd&gt;

    &lt;dt&gt;The Single Responsibility Principle&lt;/dt&gt;
    &lt;dd&gt;Any given object should have a single responsibility, that responsibility 
        should be entirely encapsulated within the class, and it&#39;s properties and 
        methods should be narrowly aligned with that responsibility.&lt;/dd&gt;
&lt;/dl&gt;
I&#39;ve listed these in the order of priority that I think makes the most sense for 
this particular problem, my argument being that of all of these, the Single 
Responsibility Principle is the most flexible and I&#39;m all but certain that encapsulation 
of what varies the least flexible. Programming to an interface rather than to an 
implementation will, I think, be implicit with any reasonable design for the 
problem, but should be kept in mind as a higher priority than the class&#39; 
responsibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: &lt;code&gt;Query&lt;/code&gt; handles everything (almost)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first scenario centers around the concept that the &lt;code&gt;Query&lt;/code&gt; is 
responsible for the storage of the SQL to be executed, the execution of that SQL 
against the data-source, and the handling and storage of results returned from 
that execution. In this scenario, the &lt;code&gt;Query&lt;/code&gt; must have (or be provided) 
a data-source object (derived from &lt;code&gt;BaseDataConnector&lt;/code&gt;) which must 
have a method to execute an arbitrary SQL string against the database it represents, 
and that returns results to the &lt;code&gt;Query&lt;/code&gt;. In keeping with the idea that 
the &lt;code&gt;Query&lt;/code&gt; holds as much relevant responsibility as possible, those 
results would probably &lt;em&gt;not&lt;/em&gt; be in &lt;code&gt;ResultSet&lt;/code&gt;/&lt;code&gt;Record&lt;/code&gt; 
instances.
&lt;dl&gt;
    &lt;dt&gt;Encapsulating what varies&lt;/dt&gt;
    &lt;dd&gt;Since the most likely point of variation is the specific mechanism that 
        executes a SQL string against a data-source, and what structure/format 
        those results come back in, encapsulating that in the 
        &lt;code&gt;BaseDataConnector&lt;/code&gt;-derived data-source feels pretty good.&lt;/dd&gt;

    &lt;dt&gt;Programming to an interface, not to an implementation&lt;/dt&gt;
    &lt;dd&gt;This scnario also feels pretty good in this respect, since &lt;code&gt;Query&lt;/code&gt; 
        doesn&#39;t need to know (or care) what &lt;em&gt;kind&lt;/em&gt; of 
        &lt;code&gt;BaseDataConnector&lt;/code&gt; it&#39;s data-source is, just that it &lt;em&gt;is&lt;/em&gt; 
        a &lt;code&gt;BaseDataConnector&lt;/code&gt;.&lt;/dd&gt;

    &lt;dt&gt;The Single Responsibility Principle&lt;/dt&gt;
    &lt;dd&gt;Assuming that the &lt;code&gt;BaseDataConnector&lt;/code&gt; returns the simplest 
        possible structure/format of it&#39;s results (most likely a list of 
        dictionary-instances), and that &lt;code&gt;Query&lt;/code&gt; is responsible for 
        taking that raw result-set and converting them into 
        &lt;code&gt;ResultSet&lt;/code&gt;/&lt;code&gt;Record&lt;/code&gt; structures, the 
        responsibilities for each class might be stated something like so:
        &lt;dl&gt;
            &lt;dt&gt;&lt;code&gt;BaseDataConnector&lt;/code&gt;&lt;/dt&gt;
            &lt;dd&gt;Manages internal and/or low-level connection to and interaction 
                with a back-end data-source.&lt;/dd&gt;

            &lt;dt&gt;&lt;code&gt;Query&lt;/code&gt;&lt;/dt&gt;
            &lt;dd&gt;Provides user/developer-level mechanisms to interact with a 
                back-end database.&lt;/dd&gt;
        &lt;/dl&gt;
    &lt;/dd&gt;

    &lt;dt&gt;Other Considerations&lt;/dt&gt;
    &lt;dd&gt;I&#39;m pretty sure that I noted, some few posts ago, that for performance 
        reasons it would be desirable to be able to queue up queries in order to 
        minimize the number of connections made against the back-end database. 
        This approach doesn&#39;t &lt;em&gt;prevent&lt;/em&gt; that sort of capability, but I 
        suspect that it would be more difficult to implement. It would almost 
        certainly require a change to the responsibility of the 
        &lt;code&gt;BaseDataConnector&lt;/code&gt;-derived classes, if only to provide some 
        mechanism to be able to associate an arbitary number of &lt;code&gt;ResultSet&lt;/code&gt; 
        instances with a particular &lt;code&gt;Query&lt;/code&gt; object, but since creation 
        of a &lt;code&gt;ResultSet&lt;/code&gt; and it&#39;s associated &lt;code&gt;Record&lt;/code&gt; 
        instances to indicate the beginning of a query-association can be done 
        in code, not as part of the &quot;real&quot; data-access, this may not be &lt;em&gt;too&lt;/em&gt; 
        onerous.&lt;/dd&gt;
&lt;/dl&gt;
An example of code using this structure would look something like this:
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;# Create the data-source, where DataConnector is derived from BaseDataConnector
dataSource = DataConnector( host=&#39;host_name&#39;, database=&#39;database_name&#39;, user=&#39;user_name&#39;, password=&#39;password&#39; )
# Create the Query instance, with SQL that returns two result-sets
sql = &quot;&quot;&quot;SELECT somefield 
    FROM sometable 
    ORDER BY somefield;
SELECT someotherfield
    FROM sometable
    ORDER BY someotherfield;&quot;&quot;&quot;
myQuery = Query( dataSource, sql )
someResults, someOtherResults = myQuery.Results
&lt;/pre&gt;
On reflection, the idea of queueing up multiple queries is, perhaps, not as critical 
as I&#39;d thought. Consider that this example returns multiple result-sets, and is 
managed in a single &lt;code&gt;Query&lt;/code&gt; instance. The performance concerns about 
multiple database-connections could be mitigated easily enough by taking this 
sort of approach, provided that the number of result-sets returned by any given 
chunk of SQL were known and didn&#39;t change based on input parameters. This approach 
is feeling better and better to me the more I think on it.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: &lt;code&gt;BaseDataConnector&lt;/code&gt; handles everything (almost)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This scenario&#39;s concept is diametrically opposed from the first: Here, the 
&lt;code&gt;BaseDataConnector&lt;/code&gt;-derived objects handle as much of the interaction 
as possible, while &lt;code&gt;Query&lt;/code&gt; becomes little more than a dumb data-object, 
containing the SQL to be executed, if it exists &lt;em&gt;at all&lt;/em&gt;.
&lt;dl&gt;
    &lt;dt&gt;Encapsulating what varies&lt;/dt&gt;
    &lt;dd&gt;The encapsulation of implementation-variations is not significantly 
        different here from the first scenario - it&#39;s still centered around the 
        potential differences between different database-engines, how their 
        connection and query-execution works, and what the results-structure looks 
        like.&lt;/dd&gt;

    &lt;dt&gt;Programming to an interface, not to an implementation&lt;/dt&gt;
    &lt;dd&gt;Again, not significantly different from the first scenario - the specifics 
        of what the interface (at the &lt;code&gt;BaseDataConnector&lt;/code&gt; level) 
        &lt;em&gt;provides&lt;/em&gt; change, but we&#39;re still keeping the implementation hidden 
        from the rest of the world.&lt;/dd&gt;

    &lt;dt&gt;The Single Responsibility Principle&lt;/dt&gt;
    &lt;dd&gt;This scenario &lt;em&gt;does&lt;/em&gt; make &lt;em&gt;drastic&lt;/em&gt; changes to the 
        responsibilities of the objects, though. Fundamentally, there&#39;s no real 
        need for any &lt;code&gt;Query&lt;/code&gt; objects at all, since the actual SQL could 
        just be passed to a &lt;code&gt;Query&lt;/code&gt; method in a data-source object, 
        and the results retrieved directly from there. If we assume that there&#39;s 
        no need to have a &lt;code&gt;Query&lt;/code&gt; object at all, the responsibilities 
        break down to:
        &lt;dl&gt;
            &lt;dt&gt;&lt;code&gt;BaseDataConnector&lt;/code&gt;&lt;/dt&gt;
            &lt;dd&gt;Manages all interaction with a back-end data-aource, including 
                execution of queries against it.&lt;/dd&gt;
        &lt;/dl&gt;
        &lt;/dd&gt;

    &lt;dt&gt;Other Considerations&lt;/dt&gt;
    &lt;dd&gt;This scenario, assuming that there is no &lt;code&gt;Query&lt;/code&gt; object in the 
        mix &lt;em&gt;at all&lt;/em&gt;, pretty much prohibits any sort of queueing of queries. 
        If we assume that a single SQL execution can be unpacked to a number of 
        result-set variables in a manner similar to the example code for the first 
        scenario, maybe this consideration is moot. The only thing we lose in this 
        scenario is the ability to have state-data associated with the SQL of the 
        query.&lt;/dd&gt;
&lt;/dl&gt;
An example of code using this structure would look something like this:
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;# Create the data-source, where DataConnector is derived from BaseDataConnector
dataSource = DataConnector( host=&#39;host_name&#39;, database=&#39;database_name&#39;, user=&#39;user_name&#39;, password=&#39;password&#39; )
# execute a query against the data-source
sql = &quot;&quot;&quot;SELECT somefield 
    FROM sometable 
    ORDER BY somefield;
SELECT someotherfield
    FROM sometable
    ORDER BY someotherfield;&quot;&quot;&quot;
someResults, someOtherResults = dataSource.Query( sql )
&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;I&#39;m going to stop with these two scenarios, at least for now, because the desire 
for state-data/SQL association is a point woth more detailed consideration. When 
I originally conceived the DataConnectors module, it was, admittedly, with a specific 
eye towards a structure more along the lines of the structure that I&#39;d gotten 
accustomed to as a CFML programmer. That included some query- or result-set-specific 
properties being available, including:
    &lt;ol&gt;
        &lt;li&gt;Data-source type (through a data-source association);&lt;/li&gt;
        &lt;li&gt;Authentication and authorization (also through a data-source association, but easily converted into a host/database/user/password structure);&lt;/li&gt;
        &lt;li&gt;Timeout control (though I don&#39;t know that I ever used this functionality);&lt;/li&gt;
        &lt;li&gt;Caching control;&lt;/li&gt;
        &lt;li&gt;Association of results to variables within the code;&lt;/li&gt;
        &lt;li&gt;Execution-time information; and&lt;/li&gt;
        &lt;li&gt;Paging of results;&lt;/li&gt;
    &lt;/ol&gt;
Of these, the first two can be converted into object-constructor arguments easily 
enough, by passing the authentication/authorization values as simple values, and 
the data-source type as a class derived from &lt;code&gt;BaseDataConnector&lt;/code&gt;. They 
don&#39;t require any sort of state-data at a &lt;code&gt;Query&lt;/code&gt;-object level at all. 
Timeout control, if it&#39;s even something that &lt;em&gt;can&lt;/em&gt; be usefully implemented, 
doesn&#39;t feel like it would require any local state-data.&lt;/p&gt;

&lt;p&gt;Caching control might, though, depending on how that caching were implemented. 
If it&#39;s implemented outside the database (say, by picking a result-set and writing 
it to a file), the query would potentially need to be able to keep track of how 
long the cached data-sets were valid. Since data-source-side caching may or may 
not be available for any given database-engine, that&#39;s a point of variation that 
should be encapsulated somewhere (though &lt;em&gt;where&lt;/em&gt; is still an open question).&lt;/p&gt;

&lt;p&gt;So long as we assume that the unpacking of results (as shown in the previous 
code-examples) will work, the association of results to variables is moot.&lt;/p&gt;

&lt;p&gt;Execution-time information would require state-data &lt;em&gt;somewhere&lt;/em&gt;, and 
the &lt;code&gt;Query&lt;/code&gt; object seems like the only place that makes sense. It 
can&#39;t be usefully associated as a &lt;code&gt;ResultSet&lt;/code&gt; level, since the number 
of result-sets from a given query will vary - particularly if multiple result-sets 
are expected with any frequency.&lt;/p&gt;

&lt;p&gt;Paging of results is probably something that makes more sense at the 
&lt;code&gt;ResultSet&lt;/code&gt; level, so it can be safely disregarded for the purpose of 
discussion of &lt;code&gt;BaseDataConnector&lt;/code&gt; and &lt;code&gt;Query&lt;/code&gt; 
responsibilities.&lt;/p&gt;

&lt;p&gt;With all of this in mind, my decision is to make the &lt;code&gt;Query&lt;/code&gt; object 
as smart as possible, and the &lt;code&gt;BaseDataConnector&lt;/code&gt;-derived objects 
pretty simple (though not completely dumb). I believe that this will afford the 
best development experience in the long run, and it keeps the possibility of 
query-state-dependent capabilities open on a longer-term basis, should there be 
a need for them somewhere down the line. With all of that in mind, then, the 
&lt;code&gt;IsDataConnector&lt;/code&gt; interface loses it&#39;s &lt;code&gt;Query&lt;/code&gt; method in 
favor of an &lt;code&gt;Execute&lt;/code&gt; method, with changes made accordingly to it&#39;s 
unit-tests:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;query&#39;, None, &#39;The IsQuery object that will be executed against the object\&#39;s Connection.&#39; )
    def Execute( self, query ):
        &quot;&quot;&quot;Executes the provided IsQuery object&#39;s query against the object&#39;s connection, returning one or more results.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s.Query is not implemented as required by IsDataConnector.&#39; % ( self.__class__.__name__ ) )&lt;/pre&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;        def testExecute( self ):
            &quot;&quot;&quot;Unit-tests the Execute method of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.Execute( None )
                self.fail( &#39;Execute should raise a NotImplementedError.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Execute should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__mame__, error ) )&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;BaseDataConnector&lt;/code&gt; inherits that abstract &lt;code&gt;Execute&lt;/code&gt; method, so it&#39;s unit-tests need to be updated accordingly:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;        def testExecute( self ):
            &quot;&quot;&quot;Unit-tests the Execute method of the BaseDataConnector abstract class.&quot;&quot;&quot;
            self.assertEquals( BaseDataConnector.Execute, IsDataConnector.Execute, &#39;BaseDataConnector should not define the Execute method.&#39; )&lt;/pre&gt;

&lt;p&gt;...and &lt;em&gt;now&lt;/em&gt; &lt;code&gt;Query&lt;/code&gt; can be defined. There are some commented-out 
properties and methods in this code that I&#39;ve left in place for later, mostly 
centering around caching of query results in the future.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Query&lt;/b&gt; (Nominal final class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class Query( IsQuery, object ):
    &quot;&quot;&quot;Represents a query (and it&#39;s results) against a back-end data-source.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

#    def _GetCacheExpiresAt( self ):
#        &quot;&quot;&quot;Gets the time at which the query&#39;s cached results will expire.&quot;&quot;&quot;
#        return self._cacheExpiresAt

    def _GetDatasource( self ):
        &quot;&quot;&quot;Gets the Datasource used to execute the query&#39;s SQL.&quot;&quot;&quot;
        return self._datasource

    def _GetResults( self ):
        &quot;&quot;&quot;Gets the results from the execution of the query&#39;s SQL against the data-source.&quot;&quot;&quot;
        if not self._results:
            if self._sql:
                self.__Execute()
        return self._results

    def _GetSql( self ):
        &quot;&quot;&quot;Gets the SQL to be executed against the data-source to generate the results.&quot;&quot;&quot;
        return self._sql

    ##################################
    # Class Property-Setter Methods  #
    ##################################

#    def _SetCacheExpiresAt( self, value ):
#        &quot;&quot;&quot;Sets the time at which the query&#39;s cached results will expire.&quot;&quot;&quot;
#        self.self._cacheExpiresAt = value

    def _SetDatasource( self, value ):
        &quot;&quot;&quot;Sets the Datasource used to execute the query&#39;s SQL.&quot;&quot;&quot;
        if not isinstance( value, IsDataConnector ):
            raise TypeError( &#39;%s.Datasource expects an instance of IsDataConnector.&#39; % ( self.__class__.__name__ ) )
        if self._results != None:
            raise AttributeError( &#39;%s.Datasource cannot be reset once results have been retrieved.&#39; % ( self.__class__.__name__ ) )
        self._datasource = value

    def _SetResults( self, value ):
        &quot;&quot;&quot;Sets the results from the execution of the query&#39;s SQL against the data-source.&quot;&quot;&quot;
        self._results = value

    def _SetSql( self, value ):
        &quot;&quot;&quot;Sets the SQL to be executed against the data-source to generate the results.&quot;&quot;&quot;
        if type( value ) != types.StringType:
            raise TypeError( &#39;%s.Sql expects a non-empty string value.&#39; % ( self.__class__.__name__ ) )
        if value == &#39;&#39;:
            raise ValueError( &#39;%s.Sql expects a non-empty string value.&#39; % ( self.__class__.__name__ ) )
        if self._results != None:
            raise AttributeError( &#39;%s.Sql cannot be reset once results have been retrieved.&#39; % ( self.__class__.__name__ ) )
        self._sql = value

    ##################################
    # Class Property-Deleter Methods #
    ##################################

#    def _DelCacheExpiresAt( self ):
#        &quot;&quot;&quot;Deletes the time at which the query&#39;s cached results will expire - uncaches the results in the process.&quot;&quot;&quot;
#        self._cacheExpiresAt = 0

    def _DelDatasource( self ):
        &quot;&quot;&quot;Deletes the Datasource used to execute the query&#39;s SQL.&quot;&quot;&quot;
        self._datasource = None

    def _DelResults( self ):
        &quot;&quot;&quot;Deletes the results from the execution of the query&#39;s SQL against the data-source.&quot;&quot;&quot;
        self._results = None

    def _DelSql( self ):
        &quot;&quot;&quot;Deletes the SQL to be executed against the data-source to generate the results.&quot;&quot;&quot;
        self._sql = None

    ##################################
    # Class Properties               #
    ##################################

#    CacheExpiresAt = property( _GetCacheExpiresAt, _SetCacheExpiresAt, None, IsQuery.CacheExpiresAt.__doc__ )
    Datasource = property( _GetDatasource, _SetDatasource, None, IsQuery.Datasource.__doc__ )
    Results = property( _GetResults, None, None, IsQuery.Results.__doc__ )
    Sql = property( _GetSql, _SetSql, None, IsQuery.Sql.__doc__ )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;datasource&#39;, None, &#39;The Datasource used to execute the query\&#39;s SQL.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;sql&#39;, None, &#39;The SQL to be executed against the data-source to generate the results.&#39; )
#    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;cacheFor&#39;, &#39;(Non-negative integer, optional) The number of seconds that the Query\&#39;s results should be cached for.&#39; )
#    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;cacheUntil&#39;, &#39;(Time, optional) A time that the Query\&#39;s results will be cached until.&#39; )
#    def __init__( self, datasource, sql, **parameters ):
    def __init__( self, datasource, sql ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != Query:
            raise NotImplementedError( &#39;Query is (nominally) a final class, and is not intended to be derived from.&#39; )
        # Set default values:
        self._DelDatasource()
        self._DelResults()
        self._DelSql()
        # Set properties from arguments
        self._SetDatasource( datasource )
        self._SetSql( sql )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

#    def Cache( self ):
#        &quot;&quot;&quot;Caches the results of the Query in a manner yet to be determined.&quot;&quot;&quot;
#        raise NotImplementedError( &#39;Query.Cache has not been implemented yet.&#39; )

    def __Execute( self ):
        &quot;&quot;&quot;Executes the query&#39;s SQL against it&#39;s specified Datasource, retrieves the results, and builds out a list of ResultSet objects in the Query&#39;s Results.&quot;&quot;&quot;
        self._results = TypedList( [ ResultSet ] )
        rawResults = self.Datasource.Execute( self )
        for theResult in rawResults:
            self._results.append( ResultSet( theResult ) )

#    def Uncache( self ):
#        &quot;&quot;&quot;Uncaches the results of the Query.&quot;&quot;&quot;
#        raise NotImplementedError( &#39;Query.Uncache has not been implemented yet.&#39; )

__all__ += [ &#39;Query&#39; ]&lt;/pre&gt;

&lt;p&gt;Given my typical patterns, and assuming that you&#39;ve been following along for 
any length of time, there&#39;s not a whole lot out of the ordinary here. The 
&lt;code&gt;Datasource&lt;/code&gt; and &lt;code&gt;Sql&lt;/code&gt; properties are the exception, in that 
they allow modification &lt;em&gt;until&lt;/em&gt; the query&#39;s results are retrieved.&lt;/p&gt;

&lt;p&gt;Similarly, with the exception of the &lt;code&gt;FixedResultsConnector&lt;/code&gt; class, 
the unit-tests are pretty typical, though again, they are explicitly testing the 
mutability of the &lt;code&gt;Datasource&lt;/code&gt; and &lt;code&gt;Sql&lt;/code&gt; properties before 
and after results are acquired. The &lt;code&gt;Results&lt;/code&gt; property tests may feel 
a bit on the sparse side also, but bear in mind that all we care about at this 
level is that we get back a &lt;code&gt;TypedList&lt;/code&gt; of &lt;code&gt;ResultSet&lt;/code&gt; 
instances - &lt;code&gt;ResultSet&lt;/code&gt; is already tested, so that&#39;s all we need to 
know...&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class QueryDerived( Query ):
        def __init__( self, datasource, sql ):
            Query.__init__( self, datasource, sql )
    
    class FixedResultsConnector( BaseDataConnector ):
        def __init__( self, **parameters ):
            BaseDataConnector.__init__( self, **parameters )
        def Execute( self, query ):
            return [
                    # First result-set
                    [
                        {&#39;results1row&#39;:1, &#39;results1field1&#39;:&#39;results1row1field1&#39;, &#39;results1field2&#39;:&#39;results1row1field2&#39; },
                        {&#39;results1row&#39;:2, &#39;results1field1&#39;:&#39;results1row2field1&#39;, &#39;results1field2&#39;:&#39;results1row2field2&#39; },
                    ],
                    # Second result-set
                    [
                        {&#39;results2row&#39;:1, &#39;results2field1&#39;:&#39;results2row1field1&#39;},
                    ],
                ]
        def ResultsCount( self ):
            return length( self.Execute( self, &#39;&#39; ) )
        def ResultsFields( self ):
            results = self.Execute( self, &#39;&#39; )
            keySets = []
            for theResult in results:
                keysets.append( theResult[0].keys() )
            return keySets
            

    class testQuery( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the Query class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testFinal( self ):
            &quot;&quot;&quot;Testing final nature of the Query class.&quot;&quot;&quot;
            testDataSource = FixedResultsConnector()
            testSql = &#39;SELECT * FROM sometable;&#39;
            try:
                testObject = QueryDerived( testDataSource, testSql )
                self.fail( &#39;Query is nominally a final class, and should not be instantiable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to instantiate an object derived from Query should raise NotImplemetedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = Query( testDataSource, testSql )
            self.assertTrue( isinstance( testObject, Query ), &#39;Creating a Query instance should result in a Query instance.&#39; )
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the Query class.&quot;&quot;&quot;
            pass
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the Query class.&quot;&quot;&quot;
            items = getMemberNames( Query )[0]
            actual = len( items )
            expected = 3
            self.assertEquals( expected, actual, &#39;Query is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the Query class.&quot;&quot;&quot;
            items = getMemberNames( Query )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;Query is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test properties

        def testDatasource( self ):
            &quot;&quot;&quot;Tests the Datasource property of the Query class.&quot;&quot;&quot;
            testDataSource = FixedResultsConnector()
            testOtherDataSource = FixedResultsConnector()
            testSql = &#39;SELECT * FROM sometable;&#39;
            testObject = Query( testDataSource, testSql )
            self.assertEquals( testObject.Datasource, testDataSource, &#39;The Datasource property should be set and gettable after creation.&#39; )
            testObject.Datasource = testOtherDataSource
            self.assertEquals( testObject.Datasource, testOtherDataSource, &#39;The Datasource property should be settabe and gettable after creation if results haven\&#39;t been retrieved.&#39; )
            testResults = testObject.Results
            try:
                testObject.Datasource = testDataSource
                self.fail( &#39;A Query\&#39;s Datasource property should not be modifiable after results have been retrieved&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to change a Query\&#39;s Datasource should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testResults( self ):
            &quot;&quot;&quot;Tests the Results property of the Query class.&quot;&quot;&quot;
            testDataSource = FixedResultsConnector()
            testSql = &#39;SELECT * FROM sometable;&#39;
            testObject = Query( testDataSource, testSql )
            queryResults1, queryResults2 = testObject.Results
            resultSet1, resultSet2 = testDataSource.Execute( &#39;&#39; )
            self.assertEquals( resultSet1[0].keys(), queryResults1.FieldNames )
            self.assertEquals( len( resultSet1 ), len( queryResults1 ) )
            self.assertEquals( resultSet2[0].keys(), queryResults2.FieldNames )
            self.assertEquals( len( resultSet2 ), len( queryResults2 ) )

        def testSql( self ):
            &quot;&quot;&quot;Tests the Sql property of the Query class.&quot;&quot;&quot;
            testDataSource = FixedResultsConnector()
            testSql = &#39;SELECT * FROM sometable;&#39;
            testOtherSql = &#39;SELECT * FROM someothertable;&#39;
            testObject = Query( testDataSource, testSql )
            self.assertEquals( testObject.Sql, testSql, &#39;The Sql property should be set and gettable after creation.&#39; )
            testObject.Sql = testOtherSql
            self.assertEquals( testObject.Sql, testOtherSql, &#39;The Sql property should be settabe and gettable after creation if results haven\&#39;t been retrieved.&#39; )
            testResults = testObject.Results
            try:
                testObject.Sql = testSql
                self.fail( &#39;A Query\&#39;s Sql property should not be modifiable after results have been retrieved&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to change a Query\&#39;s Sql should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testQuery ) )&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/4249131024830351793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-6.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4249131024830351793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4249131024830351793'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-6.html' title='The DataConnectors module (part 6)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-4111013309630561056</id><published>2011-12-16T04:30:00.000-08:00</published><updated>2011-12-16T04:30:02.534-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="concrete class"/><category scheme="http://www.blogger.com/atom/ns#" term="DataConnectors"/><category scheme="http://www.blogger.com/atom/ns#" term="ResultSet"/><title type='text'>The DataConnectors module (part 5)</title><content type='html'>&lt;p&gt;Looking at the requirements (both functional and logical) for &lt;code&gt;ResultSet&lt;/code&gt;, most of them are already accounted for simply by making &lt;code&gt;ResultSet&lt;/code&gt; derive from &lt;code&gt;TypedList&lt;/code&gt;, and hard-setting the member-types to &lt;code&gt;Record&lt;/code&gt;. Consider:
    &lt;ul&gt;
        &lt;li&gt;A result-set is a sequence of records - Check;&lt;/li&gt;
        &lt;li&gt;A result-set can be iterated over, and the fields of any individual 
            row or record can be accessed using a syntax along the lines of 
            &lt;code&gt;result[ i ].FieldName&lt;/code&gt;, or within an iteration over 
            &lt;code&gt;results&lt;/code&gt;, &lt;code&gt;results.FieldName&lt;/code&gt; - Check;&lt;/li&gt;
        &lt;li&gt;The individual rows, once populated, are immutable - Check;&lt;/li&gt;
    &lt;/ul&gt;
But there are other requirements, more based on the logical expectation(s) of a 
record or row from a query:
    &lt;ul&gt;
        &lt;li&gt;Each row or record of a result-set should have the same structure 
            (e.g., the same field-names as attributes), even if they have a null 
            or &lt;code&gt;None&lt;/code&gt; value;&lt;/li&gt;
        &lt;li&gt;Once populated, the sequence of rows cannot be altered, including adding 
            new rows, removing existing ones, or changing any row-values, either 
            singly or as part of a slice;&lt;/li&gt;
    &lt;/ul&gt;
These requirements haven&#39;t been dealt with yet, since they reside at the 
&lt;code&gt;ResultSet&lt;/code&gt; level of the stack (and they may carry back up to the 
&lt;code&gt;IsResultSet&lt;/code&gt; nominal interface as well). But given the functionality 
we know of in &lt;code&gt;TypedList&lt;/code&gt;, and the object-locking-pattern that was 
used in &lt;code&gt;Record&lt;/code&gt;, the basic mechanisms aren&#39;t difficult to implement. 
Since there will be overrides of many (all?) of the &lt;code&gt;TypedList&lt;/code&gt; methods 
in order to check for lock-state on the record-set object, as well as to check the 
field-structure of the records, each of these methods has to be tested at the 
&lt;code&gt;ResultSet&lt;/code&gt; level, just as they were for &lt;code&gt;TypedList&lt;/code&gt;, 
though... And if you recall, there was a substantial amount of testing that had 
to happen there, for all of the various list-behaviors...&lt;/p&gt;

&lt;p&gt;&lt;b&gt;ResultSet&lt;/b&gt; (Nominal final class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class ResultSet( IsResultSet, TypedList, object ):
    &quot;&quot;&quot;Class doc-string&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    def _GetFieldNames( self ):
        return self.__fieldNames

    def _GetRowCount( self ):
        return len( self )

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    FieldNames = property( _GetFieldNames, None, None, IsResultSet.FieldNames.__doc__ )
    RowCount = property( _GetRowCount, None, None, IsResultSet.RowCount.__doc__ )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;results&#39;, None, &#39;(iterable collection of dictionaries) The sequence of rows to populate in the result-set.&#39; )
    def __init__( self, results ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != ResultSet:
            raise NotImplementedError( &#39;ResultSet is (nominally) a final class, and is not intended to be derived from.&#39; )
        TypedList.__init__( self, [ Record ] )
        self.__fieldNames = []
        self.__resultsLocked = False
        for item in results:
            if not isinstance( item, Record ):
                item = Record( **item )
            if self.__fieldNames == []:
                self.__fieldNames = item.FieldNames
            else:
                if self.__fieldNames != item.FieldNames:
                    raise AttributeError( &#39;%s Error: Expected a row with %s fields, but a row with %s fields was present instead&#39; % ( self.__class__.__name__, self.__fieldNames, item.FieldNames ) )
            self.append( item )
        self.__resultsLocked = True

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    ##################################
    # TypedList Methods overridden   #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;items&#39;, None, &#39;(List or derived, required) The list of items to add to the current list.&#39; )
    def __add__( self, items ):
        &quot;&quot;&quot;Called when adding items to the list with a &quot;+&quot; (e.g., TL + items).&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s __add__: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        # check items
        for item in items:
            if item.Keys != self.__fieldNames:
                raise ValueError( &#39;%s __add__: %s is not a Record instance with fields %s&#39; % ( self.__class__.__name__, item, self.__fieldNames ) )
        TypedList.__add__( self, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;item&#39;, None, &#39;(List or derived, required) The list of item to add to the current list.&#39; )
    def __delitem__( self, item ):
        &quot;&quot;&quot;Called when deleting an item from the list.&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s __delitem__: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        TypedList.__delitem__( self, item )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;items&#39;, None, &#39;(List or derived, required) The list of items to append to the current list.&#39; )
    def __iadd__( self, items ):
        &quot;&quot;&quot;Called when adding items to the list with a &quot;+=&quot; (e.g., TL += item).&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s __iadd__: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        # check items
        for item in items:
            if item.Keys != self.__fieldNames:
                raise ValueError( &#39;%s __iadd__: %s is not a Record instance with fields %s&#39; % ( self.__class__.__name__, item, self.__fieldNames ) )
        TypedList.__iadd__( self, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;index&#39;, None, &#39;(Integer, required) The index-location to set the item in.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;item&#39;, None, &#39;(Any, required) The item to set in the specified list-location.&#39; )
    def __setitem__( self, index, item ):
        &quot;&quot;&quot;Called when setting the value of a specified location in the list (TL[n] = item).&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s __setitem__: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        if item.FieldNames != self.__fieldNames:
            raise ValueError( &#39;%s __setitem__: %s is not a Record instance with fields %s&#39; % ( self.__class__.__name__, item, self.__fieldNames ) )
        TypedList.__setitem__( self, index, item )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;start&#39;, None, &#39;(Integer, required) The starting position of the slice to set the supplied items in.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;end&#39;, None, &#39;(Integer, required) The ending position of the slice to set the supplied items in.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;items&#39;, None, &#39;(List or derived, required) The list of items to set in the specified slice-location.&#39; )
    def __setslice__( self, start, end, items ):
        &quot;&quot;&quot;Called when setting the value(s) of a slice-location in the list (TL[1:2] = item).&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s __setslice__: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        # check items
        for item in items:
            if item.FieldNames != self.__fieldNames:
                raise ValueError( &#39;%s __setslice__: %s is not a Record instance with fields %s&#39; % ( self.__class__.__name__, item, self.__fieldNames ) )
        TypedList.__setslice__( self, start, end, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;item&#39;, None, &#39;(Any, required) The item to append.&#39; )
    def append( self, item ):
        &quot;&quot;&quot;Called when appending items to the list.&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s error: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        if item.FieldNames != self.__fieldNames:
            raise ValueError( &#39;%s append: %s is not a Record instance with fields %s&#39; % ( self.__class__.__name__, item, self.__fieldNames ) )
        TypedList.append( self, item )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;items&#39;, None, &#39;(List or derived, required) The list of items to extend to the current list.&#39; )
    def extend( self, items ):
        &quot;&quot;&quot;Called when extending items to a the end of the list.&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s error: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        # check items
        for item in items:
            if item.FieldNames != self.__fieldNames:
                raise ValueError( &#39;%s error: %s is not a Record instance with fields %s&#39; % ( self.__class__.__name__, item, self.__fieldNames ) )
        TypedList.extend( self, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;index&#39;, None, &#39;(Integer, required) The index-location to insert the item in.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;item&#39;, None, &#39;(Any, required) The item to insert in the specified list-location.&#39; )
    def insert( self, index, item ):
        &quot;&quot;&quot;Called when inserting items to a specified location in the list (TL[1] = item).&quot;&quot;&quot;
        if self.__resultsLocked:
            raise AttributeError( &#39;%s error: the %s is locked and cannot be modified&#39; % ( self.__class__.__name__, self.__class__.__name__ ) )
        if item.FieldNames != self.__fieldNames:
            raise ValueError( &#39;%s append: %s is not a Record instance with fields %s&#39; % ( self.__class__.__name__, item, self.__fieldNames ) )
        TypedList.insert( self, index, item )

__all__ += [ &#39;ResultSet&#39; ]&lt;/pre&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;12-16, 30-31&lt;/dt&gt;
    &lt;dd&gt;Standard read-only property set-up...&lt;/dd&gt;

    &lt;dt&gt;37-56&lt;/dt&gt;
    &lt;dd&gt;Object constructor:
        &lt;dl&gt;

            &lt;dt&gt;42-43&lt;/dt&gt;
            &lt;dd&gt;Enforcing a nominally-final class for the object.&lt;/dd&gt;

            &lt;dt&gt;44&lt;/dt&gt;
            &lt;dd&gt;Making sure that &lt;code&gt;TypedList&lt;/code&gt; functionality is available 
                for instances.&lt;/dd&gt;

            &lt;dt&gt;45-46&lt;/dt&gt;
            &lt;dd&gt;Setting instance-values for the two new attributes of the class.&lt;/dd&gt;

            &lt;dt&gt;47-55&lt;/dt&gt;
            &lt;dd&gt;Populating the instance with the results provided:
                &lt;dl&gt;

                    &lt;dt&gt;48-49&lt;/dt&gt;
                    &lt;dd&gt;The class should, ideally, be able to handle results as 
                        either an iterable of &lt;code&gt;Record&lt;/code&gt; instances, or 
                        an iterable of dictionary instances - if the item is a 
                        dictionary, it will need to be made into a &lt;code&gt;Record&lt;/code&gt; 
                        instance.&lt;/dd&gt;
                    &lt;dd&gt;I feel like I&#39;ve neglected type-checking the item at this 
                        point, making sure that it&#39;s a dictionary, but I don&#39;t 
                        know that it would really make a difference. Instantiation 
                        of a &lt;code&gt;Record&lt;/code&gt; with anything &lt;em&gt;other&lt;/em&gt; than 
                        a dictionary or a keyword argument list raises an error 
                        on it&#39;s own, so I don&#39;t &lt;em&gt;think&lt;/em&gt; explicitly checking 
                        makes enough of a difference to implement that. At least 
                        not yet.&lt;/dd&gt;

                    &lt;dt&gt;50-54&lt;/dt&gt;
                    &lt;dd&gt;This is where the &lt;code&gt;ResultSet&lt;/code&gt;&#39;s field-names 
                        are set, on the first pass through the loop, and where 
                        the structural consistency of subsequent records is 
                        checked.&lt;/dd&gt;

                    &lt;dt&gt;55&lt;/dt&gt;
                    &lt;dd&gt;If no errors were raised by the time that we reach this 
                        point, then the item is appended to the underlying list.&lt;/dd&gt;

                    &lt;dt&gt;56&lt;/dt&gt;
                    &lt;dd&gt;Locks the object, preventing futher modification.&lt;/dd&gt;

                &lt;/dl&gt;
            &lt;/dd&gt;

        &lt;/dl&gt;
    &lt;/dd&gt;

    &lt;dt&gt;70-158&lt;/dt&gt;
    &lt;dd&gt;The various built-ins and methods here all follow more or less the same 
        pattern:
        &lt;ul&gt;
            &lt;li&gt;Check to see if the object is locked, and raise &lt;code&gt;AttributeError&lt;/code&gt; 
                if it is;&lt;/li&gt;
            &lt;li&gt;Check for structural consistency with the established field-names 
                for the item or items supplied;&lt;/li&gt;
            &lt;li&gt;If no errors are raised, call the parent method from &lt;code&gt;TypedList&lt;/code&gt; 
                to perform the actual operation.&lt;/li&gt;
        &lt;/ul&gt;
        Ultimately, they&#39;re all just checking to see if the operation is allowed 
        before performing it - nothing too fancy, though some cases may look a 
        bit more convoluted than is perhaps typical. 
    &lt;/dd&gt;

    &lt;dt&gt;82-88&lt;/dt&gt;
    &lt;dd&gt;Since the records in a &lt;code&gt;ResultSet&lt;/code&gt; should be immutable, deletion 
        has to be prevented as well.&lt;/dd&gt;
    &lt;dd&gt;This is something that I overlooked in &lt;code&gt;Record&lt;/code&gt;, so I&#39;ll need 
        to get that in place there as well.&lt;/dd&gt;

&lt;/dl&gt;

&lt;p&gt;Unit-tests&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class ResultSetDerived( ResultSet ):
        def __init__( self, results ):
            ResultSet.__init__( self, results )
    
    class testResultSet( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the ResultSet class.&quot;&quot;&quot;

        def getBadRecord( self ):
            return { &#39;Number1&#39;:3, &#39;Name&#39;:&#39;ook eek&#39;, &#39;IsValid&#39;:True }

        def getGoodRecord( self ):
            return { &#39;Number&#39;:3, &#39;Name&#39;:&#39;ook eek&#39;, &#39;IsValid&#39;:True }

        def getBadValues( self ):
            return [ 1, &#39;ook&#39;, True, None, {} ]

        def getGoodValues( self ):
            return [
                { &#39;Number&#39;:1, &#39;Name&#39;:&#39;ook&#39;, &#39;IsValid&#39;:True },
                { &#39;Number&#39;:2, &#39;Name&#39;:&#39;eek&#39;, &#39;IsValid&#39;:False },
                { &#39;Number&#39;:None, &#39;Name&#39;:None, &#39;IsValid&#39;:None },
                ]

        def getInconsistentValues( self ):
            return [
                { &#39;Number&#39;:1, &#39;Name&#39;:&#39;ook&#39;, &#39;IsValid&#39;:True },
                { &#39;Number&#39;:1, &#39;IsValid&#39;:True },
                { &#39;Number&#39;:2, &#39;Name&#39;:&#39;eek&#39; },
                { &#39;Name&#39;:&#39;eek&#39;, &#39;IsValid&#39;:False },
                { &#39;Number&#39;:None },
                { &#39;IsValid&#39;:None },
                ]

        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testFinal( self ):
            &quot;&quot;&quot;Testing final nature of the ResultSet class.&quot;&quot;&quot;
            try:
                testObject = ResultSetDerived( self.getGoodValues() )
                self.fail( &#39;ResultSet is nominally a final class, and should not be extendable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Instantiating an object derived from ResultSet should raise NotImplementedError, by %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the ResultSet class.&quot;&quot;&quot;
            testObject = ResultSet( self.getGoodValues() )
            # TODO: Test values...
            # Test inconsistent values - all should raise errors
            inconsistentValues = self.getInconsistentValues()
            # Test creation with an iterable
            try:
                testObject = ResultSet( inconsistentValues )
                self.fail( &#39;Construction with inconsistent record-structures should raise an error.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Construction with inconsistent record-structures should raise AttributeError, but %s was raised instead:\n  %s.&#39; % ( error.__class__.__name__, error ) )

        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the ResultSet class.&quot;&quot;&quot;
            items = getMemberNames( ResultSet )[0]
            actual = len( items )
            expected = 3
            self.assertEquals( expected, actual, &#39;ResultSet is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the ResultSet class.&quot;&quot;&quot;
            items = getMemberNames( ResultSet )[1]
            actual = len( items )
            expected = 4
            self.assertEquals( expected, actual, &#39;ResultSet is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test list behavior
        def testListBehavior( self ):
            &quot;&quot;&quot;Unit-tests list-like behavior of ResultSet.&quot;&quot;&quot;
            # Test good values - all should raise errors
            goodValues = self.getGoodValues()
            testObject = ResultSet( goodValues )
            validRecord = Record( **self.getGoodRecord() )
            # Test L = L + i structure
            try:
                testObject + [ validRecord ]
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            # Test L = L += i structure
            try:
                testObject += [ validRecord ]
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            # Test L.append equivalent
            try:
                testObject.append( validRecord )
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            # Test L.extend equivalent
            try:
                testObject.extend( [ validRecord ] )
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            # Test L.insert equivalent
            try:
                testObject.insert( 0, validRecord )
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            # Test L[index] = value equivalent
            try:
                testObject[ 0 ] = validRecord
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            # Test L[slice] = [ i, j ] equivalent
            try:
                testObject[ 0:1 ] = validRecord
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            # Test del L[i]
            try:
                del testObject[ 0 ]
                self.fail( &#39;Once a ResultSet has been populated, it should be immutable.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to modify a ResultSet once it\&#39;s been populated should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        # Test Properties

        def testMemberTypes( self ):
            &quot;&quot;&quot;Unit-tests the MemberTypes property of the ResultSet class.&quot;&quot;&quot;
            self.assertEquals( TypedList.MemberTypes, BaseTypedCollection.MemberTypes, &#39;TypedList should inherit the MemberTypes property of BaseTypedCollection.&#39; )

        def testFieldNames( self ):
            &quot;&quot;&quot;Unit-tests the FieldNames property of the ResultSet class.&quot;&quot;&quot;
            goodValues = self.getGoodValues()
            testObject = ResultSet( goodValues )
            expected = Record( **goodValues[ 0 ] ).FieldNames
            self.assertEquals( testObject.FieldNames, expected, &#39;The field-names of a ResultSet should be the same as the field-names of it\&#39;s Record-instances.&#39; )

        def testRowCount( self ):
            &quot;&quot;&quot;Unit-tests the RowCount property of the ResultSet class.&quot;&quot;&quot;
            goodValues = self.getGoodValues()
            testObject = ResultSet( goodValues )
            self.assertEquals( testObject.RowCount, len(goodValues), &#39;The number of rows in RowCount should be the same as the number of items presented at construction.&#39; )

        # Test Methods

        def testappend( self ):
            &quot;&quot;&quot;Unit-tests the append method of the ResultSet class.&quot;&quot;&quot;
            pass # This is tested in testListBehavior, above.

        def testextend( self ):
            &quot;&quot;&quot;Unit-tests the extend method of the ResultSet class.&quot;&quot;&quot;
            pass

        def testinsert( self ):
            &quot;&quot;&quot;Unit-tests the insert method of the ResultSet class.&quot;&quot;&quot;
            pass # This is tested in testListBehavior, above.

        def testIsValidMemberType( self ):
            &quot;&quot;&quot;Unit-tests the IsValidMemberType method of the ResultSet class.&quot;&quot;&quot;
            self.assertEquals( TypedList.IsValidMemberType, BaseTypedCollection.IsValidMemberType, &#39;TypedList should inherit the IsValidMemberType method of BaseTypedCollection.&#39; )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testResultSet ) )&lt;/pre&gt;

&lt;p&gt;I&#39;m going to skip commenting on the unit-tests this time around - they are all 
either very straightforward (I believe), or repeats of previous test-structures. 
The only new thing here, really, is the use of a couple of methods (&lt;code&gt;getBadRecord&lt;/code&gt;, 
&lt;code&gt;getGoodRecord&lt;/code&gt;, &lt;code&gt;getGoodValues&lt;/code&gt;, and &lt;code&gt;getInconsistentValues&lt;/code&gt;) 
used to generate results for creation of &lt;code&gt;RecordSet&lt;/code&gt; instances for the 
various test-methods.&lt;/p&gt;

&lt;p&gt;The unit-tests for &lt;code&gt;RecordSet&lt;/code&gt; also revealed a flaw in &lt;code&gt;Record&lt;/code&gt;: 
I&#39;d forgotten to initialize a local &lt;code&gt;__fieldNames&lt;/code&gt; attribute, and as 
a result, the value being set in that property for the first instance was shared 
across all instances (it was behaving as a static attribute). The changes to 
&lt;code&gt;Record&lt;/code&gt; and it&#39;s unit-tests are:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;keyword&#39;, &#39;properties&#39;, &#39;variable&#39;, &#39;Key/value pairs providing the record\&#39;s field-names and values of those fields.&#39; )
    def __init__( self, **properties ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != Record:
            raise NotImplementedError( &#39;Record is (nominally) a final class, and is not intended to be derived from.&#39; )
        self.__fieldNames = []
        for theKey in properties:
            self.__setattr__( theKey, properties[ theKey ] )
            if not theKey in self.__fieldNames:
                self.__fieldNames.append( theKey )
        self.__recordIsLocked = True

# break

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;name&#39;, None, &#39;(String, required) The name of the attribute to be set.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(Any type, required) The value to be set in the named attribute.&#39; )
    def __setattr__( self, name, value ):
        &quot;&quot;&quot;Object-attribute setter.&quot;&quot;&quot;
        if name == &#39;_Record__recordIsLocked&#39; and self.__recordIsLocked != False:
            raise AttributeError( &#39;The record is locked, and cannot be unlocked&#39; )
        if self.__recordIsLocked:
            raise AttributeError( &#39;The record has been locked, and the &quot;%s&quot; attribute cannot be set.&#39; % ( name ) )
        if hasattr( self, name ) and not name in [ &#39;_Record__recordIsLocked&#39;, &#39;_Record__fieldNames&#39; ]:
            raise AttributeError( &#39;The &quot;%s&quot; attribute already exists, and cannot be reset.&#39; % ( name ) )
        return object.__setattr__( self, name, value )&lt;/pre&gt;

&lt;p&gt;The changes here are:

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;8&lt;/dt&gt;
    &lt;dd&gt;&lt;code&gt;self.__fieldNames&lt;/code&gt; is being explicitly set to an empty list&lt;/dd&gt;

    &lt;dt&gt;26&lt;/dt&gt;
    &lt;dd&gt;Explicitly checking the property-name against both &lt;code&gt;_Record__recordIsLocked&lt;/code&gt; 
        and &lt;code&gt;_Record__fieldNames&lt;/code&gt;, in order to allow either to be set 
        if the object isn&#39;t locked yet.&lt;/dd&gt;
    &lt;dd&gt;This feels like it may not be complete, so I may very well come back to 
        it later...&lt;/dd&gt;
&lt;/dl&gt;
&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;        def testFieldNames( self ):
            &quot;&quot;&quot;Unit-tests the FieldNames property of the Record class.&quot;&quot;&quot;
            testProperties = { &#39;Property1&#39;:1, &#39;Property2&#39;:&#39;ook&#39;, &#39;Property3&#39;:True }
            testProperties2 = { &#39;Property&#39;:1 }
            testObject = Record( **testProperties )
            self.assertEquals( testObject.FieldNames, testProperties.keys() )
            try:
                testObject.Keys = &#39;ook&#39;
                self.fail( &#39;A Record\&#39;s FieldNames cannot be altered.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Changes to a record\&#39;s FieldNames should raise AttributeError, bu %s was raised instead:\n  %s &#39; % ( error.__class__.__name__, error ) )
            testObject = Record( **testProperties2 )
            self.assertEquals( testObject.FieldNames, testProperties2.keys(), &#39;The field-names for each record should be local to the instance&#39; )&lt;/pre&gt;

&lt;p&gt;The change here is the addition of the last two lines - since each &lt;code&gt;Record&lt;/code&gt; 
instance should have set it&#39;s FieldNames property independently, the second 
&lt;code&gt;testObject&lt;/code&gt; should have the same keys as the &lt;code&gt;testProperties2&lt;/code&gt; 
dictionary. If the attribute were not explicitly set locally, it would have the 
keys from &lt;code&gt;testProperties&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;And that&#39;s the last of the &quot;simple&quot; concrete classes that weren&#39;t waiting on 
research. At least until the need for another one rears it&#39;s head. Next (finally), 
I&#39;ll get back to defining where query-execution takes place - you may remember 
that I set that aside to do some research in my previous post. I&#39;ve done that 
research, and I can move on to implementation now...&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/4111013309630561056/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-5.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4111013309630561056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4111013309630561056'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-5.html' title='The DataConnectors module (part 5)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-6894415047545001295</id><published>2011-12-14T04:30:00.000-08:00</published><updated>2011-12-14T04:47:17.642-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="concrete class"/><category scheme="http://www.blogger.com/atom/ns#" term="DataConnectors"/><category scheme="http://www.blogger.com/atom/ns#" term="Record"/><title type='text'>The DataConnectors module (part 4)</title><content type='html'>&lt;p&gt;So, before I start digging into the code for the concrete classes of 
&lt;code&gt;DataConnectors&lt;/code&gt;, I want to do a quick review of how they are all 
supposed to fit together:
    &lt;ul&gt;
        &lt;li&gt;An instance of &lt;code&gt;BaseDataConnector&lt;/code&gt; (which implements 
            &lt;code&gt;IsDataConnector&lt;/code&gt;) will provide the basic connectivity to 
            the back-end data-store, as well as the mechanism(s) for executing 
            queries against that data-store;&lt;/li&gt;
        &lt;li&gt;Queries are objects implementing &lt;code&gt;IsQuery&lt;/code&gt;, that store the 
            SQL to be executed, as well as the results of that execution once it&#39;s 
            run in one or more &lt;code&gt;ResultSet&lt;/code&gt; objects;&lt;/li&gt;
        &lt;li&gt;The &lt;code&gt;ResultSet&lt;/code&gt; instances (which implement 
            &lt;code&gt;IsResultSet&lt;/code&gt;, and are &lt;code&gt;TypedLists&lt;/code&gt; keep track 
            of the fields returned, row-by-row.&lt;/li&gt;
    &lt;/ul&gt;
There&#39;s a piece missing from the most recent class-diagram: &lt;em&gt;how&lt;/em&gt; the 
records are going to be represented... We need to add a &lt;code&gt;Record&lt;/code&gt; class 
to handle that, like so:&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0ZPj2wcJQLgrrvWuu3xvyitEs7gOWfKT7U2_uAd2vCBlocJepcCUwh0xO2EebtIGUejTns_6gLgMlHlOCiDN3dttsz-jbDDhJyUFT5uVTMBzAh4EfNeeQm0-x27tlaQDEvTofrlqHdVw/s1600/DataConnectors-uml.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
        &lt;img border=&quot;0&quot; height=&quot;208&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0ZPj2wcJQLgrrvWuu3xvyitEs7gOWfKT7U2_uAd2vCBlocJepcCUwh0xO2EebtIGUejTns_6gLgMlHlOCiDN3dttsz-jbDDhJyUFT5uVTMBzAh4EfNeeQm0-x27tlaQDEvTofrlqHdVw/s320/DataConnectors-uml.png&quot; width=&quot;320&quot; /&gt;
    &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;Ideally, what I&#39;d prefer from a code-structure standpoint, is to be able to 
iterate over a &lt;code&gt;ResultSet&lt;/code&gt; instance, and use object-/dot-notation for 
the fields. For example, something like:
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;for record in results:
    print record.FirstName, record.LastName&lt;/pre&gt;
Providing that capability is &lt;em&gt;easy&lt;/em&gt;, since Python&#39;s objects can have 
dynamically-assigned attributes and values. The downside to taking that very basic 
approach, though is that those records, and the fields within them, are mutable 
in the code, and for a database-record that&#39;s read-only, that&#39;s &lt;em&gt;not&lt;/em&gt; a 
good thing. There is a way to work around that, though, and I&#39;ll demonstrate that 
a bit later.
&lt;/p&gt;

&lt;p&gt;Another thing that hasn&#39;t really been thrashed out yet is when and where 
query-execution occurs. I&#39;ve been contemplating two basic approaches, and trying 
to weight between their merits and drawbacks:
    &lt;dl&gt;
        &lt;dt&gt;Execution happens at the &lt;code&gt;Query&lt;/code&gt;:&lt;/dt&gt;
        &lt;dd&gt;In this model, the &lt;code&gt;Query&lt;/code&gt; uses it&#39;s &lt;code&gt;Datasource&lt;/code&gt; to connect to the back-end database, executes the SQL, and generates the result-set.
            &lt;dl&gt;
                &lt;dt&gt;Advantages&lt;/dt&gt;
                &lt;dd&gt;
                    &lt;ul&gt;
                        &lt;li&gt;It feels to me like query-objects preserve the 
                            &lt;a href=&quot;http://en.wikipedia.org/wiki/Single_responsibility_principle&quot;&gt;single 
                            responsibility principle&lt;/a&gt; better this way - really, 
                            at least in theory, all they &lt;em&gt;need&lt;/em&gt; is a 
                            connection-object, supplied by the data-connector, 
                            and away they go.&lt;/li&gt;
                        &lt;li&gt;I suspect that this will allow for easier 
                            implementation of &quot;one-off&quot; queries - I don&#39;t know how 
                            many times in my career a project has crossed my desk 
                            where the work needed essentially boiled down to &quot;run 
                            another query, and add the results to the display.&quot; I 
                            &lt;em&gt;believe&lt;/em&gt; that optimizing the data-connector 
                            approach for this sort of one-off case wouldn&#39;t be 
                            difficult, but I&#39;m not completely sure about that at 
                            this point.&lt;/li&gt;
                        &lt;li&gt;It feels like this approach would lend itself well 
                            (or at least &lt;em&gt;better&lt;/em&gt;) to lazy instantiation 
                            of query-results: that is, create as many query-objects 
                            as even &lt;em&gt;might&lt;/em&gt; be necessary, but don&#39;t actually 
                            get the results from the database until (or even unless) 
                            they are specifically requested. At least for basic 
                            fetch-and-display processes, this could be very 
                            advantageous.&lt;/li&gt;
                    &lt;/ul&gt;
                &lt;/dd&gt;
                &lt;dt&gt;Drawbacks&lt;/dt&gt;
                &lt;dd&gt;
                    &lt;ul&gt;
                        &lt;li&gt;There is a strong potential, when (not if) this module 
                            is expanded to include back-end databases other than 
                            MySQL, that query-objects may need to be customized 
                            to each individual database-engine. I don&#39;t have a 
                            strong feeling at this point for just how likely this 
                            really &lt;em&gt;is&lt;/em&gt;, but since the logical point of 
                            encapsulation for any database-engine-specific 
                            capabilities is at the data-connector level, &lt;em&gt;not&lt;/em&gt; 
                            at the query level, this feels like it would be less 
                            than optimal the first time that this kind of 
                            database-engine variation happens. It&#39;s not 
                            future-proof, and could easily require a lot of new 
                            work or rework, and I feel that&#39;s a major concern.&lt;/li&gt;
                        &lt;li&gt;If the assumption that one-off queries would be easier 
                            to implement in this model holds true, that could tend 
                            to reinforce that sort of design/solution in applications. 
                            On the surface, this doesn&#39;t &lt;em&gt;sound&lt;/em&gt; all that 
                            bad, but if each one-off query represents some (hopefully 
                            small) amount of database-connection time, the more 
                            this sort of design is propagated, the more performance 
                            could suffer as a result. I&#39;m not a big fan of making 
                            things easier in the shorter term at the expense of 
                            longer-term stability or maintainability.&lt;/li&gt;
                        &lt;li&gt;It doesn&#39;t feel like this approach would facilitate 
                            building up a list of queries, then executing all of 
                            them at once - at least not as easily.&lt;/li&gt;
                    &lt;/ul&gt;
                &lt;/dd&gt;
            &lt;/dl&gt;
        &lt;/dd&gt;

        &lt;dt&gt;Execution happens at the &lt;code&gt;BaseDataConnector&lt;/code&gt;:&lt;/dt&gt;
        &lt;dd&gt;In this model, the connector (a derivative of &lt;code&gt;BaseDataConnector&lt;/code&gt;) 
            connects to the datasource, then runs the query or queries specified, 
            handing the results back to the &lt;code&gt;Query&lt;/code&gt; object(s) that 
            supplied the SQL in the first place so that &lt;em&gt;they&lt;/em&gt; can keep 
            track of them.
            &lt;dl&gt;
                &lt;dt&gt;Advantages&lt;/dt&gt;
                &lt;dd&gt;
                    &lt;ul&gt;
                        &lt;li&gt;&lt;em&gt;The&lt;/em&gt; major advantage is almost certainly that 
                            keeping the actual execution of the query encapsulated 
                            in the data-connector (which is the place where it 
                            might vary) will protect us from the potential need 
                            to have to spin off a &lt;code&gt;Query&lt;/code&gt;-variant for 
                            each and every data-connector-type that we might 
                            eventually want. As I noted above, I don&#39;t have a 
                            strong feeling for how likely this really is, so more 
                            research on that topic is probably in order. It should 
                            be noted, however, that even when that research is 
                            complete, and even &lt;em&gt;if&lt;/em&gt; it proves that the 
                            connector-types that we&#39;re planning aren&#39;t going to 
                            be affected, that does &lt;em&gt;not&lt;/em&gt; mean that they 
                            won&#39;t change, or that some new, hot-topic database-engine 
                            won&#39;t come out that would require distinct &lt;code&gt;Query&lt;/code&gt;
                            classes...&lt;/li&gt;
                        &lt;li&gt;A major advantage to this approach is, I think, 
                            that queries could be queued up and deferred until 
                            the developer knows that the code will need them.&lt;/li&gt;
                    &lt;/ul&gt;
                &lt;/dd&gt;
                &lt;dt&gt;Drawbacks&lt;/dt&gt;
                &lt;dd&gt;
                    &lt;ul&gt;
                        &lt;li&gt;There might be significant design changes needed for 
                            &lt;code&gt;BaseDataObject&lt;/code&gt;-derived objects in order 
                            to take advantage of query-queueing capabilities. Or 
                            it may not even be feasible to reconcile those objects&#39; 
                            data-needs with a queueable query-stack, in which case 
                            a mixed model might be necessary (which is more 
                            up-front work).&lt;/li&gt;
                    &lt;/ul&gt;
                &lt;/dd&gt;
            &lt;/dl&gt;
        &lt;/dd&gt;
    &lt;/dl&gt;
&lt;/p&gt;

&lt;p&gt;So, it feels like more research is needed, and possibly some rework on the 
designs of &lt;code&gt;BaseDataConnector&lt;/code&gt;, and maybe some minor changes to 
&lt;code&gt;Query&lt;/code&gt; will be needed as well. In the interests of moving this post 
along (while I do my research in the background), I&#39;m not going to work on 
&lt;code&gt;Query&lt;/code&gt; just yet. That class, and any changes to 
&lt;code&gt;BaseDataConnector&lt;/code&gt; will be the topic of the next post, and I&#39;ll 
attack the two remaining general-purpose concrete classes left: &lt;code&gt;Record&lt;/code&gt; 
and &lt;code&gt;ResultsSet&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instances of the &lt;code&gt;Record&lt;/code&gt; class represent single records/rows returned 
from a query executed against the database. They are the core member-type for a 
&lt;code&gt;ResultSet&lt;/code&gt; instance (which in turn represent the collections of records 
from a query). Record-objects should (for the time being) be &lt;em&gt;immutable&lt;/em&gt; 
once instantiated: we don&#39;t want to allow changes to a record&#39;s data inside an 
application without having some way to re-synchronize that data with the original 
record, which is why the &lt;code&gt;BaseDataObject&lt;/code&gt; abstract class exists. For 
similar reasons, &lt;code&gt;ResultSet&lt;/code&gt; instances should &lt;em&gt;also&lt;/em&gt; be 
immutable once instantiated. Both are variations of a &quot;dumb&quot; data-object concept 
(though &lt;code&gt;ResultSet&lt;/code&gt; is not &lt;em&gt;completely&lt;/em&gt; dumb, since it has all 
of the functionality of a &lt;code&gt;TypedList&lt;/code&gt;): objects whose sole purpose is 
to represent some data-structure.&lt;/p&gt;

&lt;p&gt;Since I&#39;m expecting a &lt;code&gt;Record&lt;/code&gt; instance to be generated from a 
dictionary data-structure, where the dictionary&#39;s keys are the column-names and 
the values are the field-values, the first challenge to overcome is to find a 
way to pass a dictionary structure into an object at creation time, &lt;em&gt;without&lt;/em&gt; 
having to go through the typical &lt;code&gt;property2=value2, property2=value2&lt;/code&gt; 
structure that we might expect given the code I&#39;ve shown so far. These names/values 
will vary from one record-structure to another, and having to code that structure 
into a record&#39;s constructor would mean having to generate a &lt;code&gt;Record&lt;/code&gt;-derived 
class for &lt;em&gt;every&lt;/em&gt; data-type that needs to be represented. That, obviously, 
is not sustainable. Fortunately, Python provides a mechanism to pass a dictionary 
as a keyword-argument list. We&#39;ve already seen the recieving-end structure (see 
&lt;code&gt;BaseDataConnector.__init__&lt;/code&gt; for an example), though I don&#39;t see that 
I&#39;ve provided an example of the &quot;sending&quot; structure yet. To illustrate &lt;em&gt;that&lt;/em&gt;, 
here&#39;s a quick little script:

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class DictProperties( object ):
    &quot;&quot;&quot;A test-class, created to see how dictionary arguments can be 
pushed into an object&#39;s properties.&quot;&quot;&quot;
    def __init__( self, **properties ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        for theKey in properties:
            self.__setattr__( theKey, properties[ theKey ] )

properties = {
    &#39;Prop1&#39;:1,
    &#39;Prop2&#39;:&#39;ook&#39;,
    &#39;Prop3&#39;:True,
    }

testObject = DictProperties( **properties )

print &#39;testObject.Prop1 ... %s&#39; % ( testObject.Prop1 )
print &#39;testObject.Prop2 ... %s&#39; % ( testObject.Prop2 )
print &#39;testObject.Prop3 ... %s&#39; % ( testObject.Prop3 )&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;When this script is run, it prints out:

&lt;pre&gt;testObject.Prop1 ... 1
testObject.Prop2 ... ook
testObject.Prop3 ... True
&lt;/pre&gt;

This is what we want: The elements of the dictionary (&lt;code&gt;properties&lt;/code&gt;) 
are being set as &quot;real&quot; attributes in the object-instance. But how does it work? 
The secret is in lines 4, 6-7, and 15:&lt;/p&gt;

&lt;dl compact=&quot;compact&quot;&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;4&lt;/dt&gt;
    &lt;dd&gt;The &lt;code&gt;**properties&lt;/code&gt; argument is a keyword-argument list. Internally, 
        that is a dictionary that can be iterated over in line 6.&lt;/dd&gt;

    &lt;dt&gt;6-7&lt;/dt&gt;
    &lt;dd&gt;As the &lt;code&gt;properties&lt;/code&gt; keyword/dictionary is being iterated over 
        (line 6), we take advantage of a Python built-in: &lt;code&gt;__setattr__&lt;/code&gt;. 
        When present, &lt;code&gt;__setattr__&lt;/code&gt; is called when an attribute assignment 
        is attempted, instead of simply storing the value in the object&#39;s instance 
        dictionary. Remember this, because it&#39;s going to be a key part of the 
        immutability of a &lt;code&gt;Record&lt;/code&gt; later on...&lt;/dd&gt;

    &lt;dt&gt;15&lt;/dt&gt;
    &lt;dd&gt;This is the part that allows an arbitrary dictionary to be passed into the 
        object&#39;s constructor (it would work with any other method as well). Instead 
        of supplying &lt;code&gt;Prop1=1, Prop2=&#39;ook&#39;, Prop3=True&lt;/code&gt; as arguments 
        to the constructor, which would tie each instantiation to a known structure, 
        we pass what is essentially a keyword-argument-reference to a dictionary 
        (&lt;code&gt;**properties&lt;/code&gt;) instead. The constructor recognizes it as a 
        keyword-arguments structure (which it&#39;s expecting), and handles it &lt;em&gt;just 
        like it would if each keyword/value were supplied directly&lt;/em&gt;.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;So an arbitrary data-structure can be passed to an object during creation. That&#39;s the first hurdle passed.&lt;/p&gt;

&lt;p&gt;The next hurdle is to make those values immutable after they&#39;ve been set. Getting that to work takes a bit of modification that&#39;s easier to explain by showing the code, so here&#39;s a modified copy of the same script above:

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class DictProperties( object ):
    &quot;&quot;&quot;A test-class, created to see how dictionary arguments can be 
pushed into an object&#39;s properties.&quot;&quot;&quot;
    # Reserved attributes
    __objectIsLocked = False
    # Object constructor
    def __init__( self, **properties ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        for theKey in properties:
            self.__setattr__( theKey, properties[ theKey ] )
        self.__objectIsLocked = True
    def __setattr__( self, name, value ):
        &quot;&quot;&quot;Object-attribute setter.&quot;&quot;&quot;
        if name == &#39;__objectIsLocked&#39; and self.__objectIsLocked:
            raise AttributeError( &#39;The object is locked, and cannot be unlocked&#39; )
        if self.__objectIsLocked:
            raise AttributeError( &#39;The object has been locked, and the &quot;%s&quot; attribute cannot be set.&#39; % ( name ) )
        return object.__setattr__( self, name, value )

properties = {
    &#39;Prop1&#39;:1,
    &#39;Prop2&#39;:&#39;ook&#39;,
    &#39;Prop3&#39;:True,
    }

testObject = DictProperties( **properties )

print &#39;testObject.Prop1 ... %s&#39; % ( testObject.Prop1 )
print &#39;testObject.Prop2 ... %s&#39; % ( testObject.Prop2 )
print &#39;testObject.Prop3 ... %s&#39; % ( testObject.Prop3 )

try:
    testObject.Prop3 = False
except AttributeError:
    print &#39;testObject.Prop3 ... Reset failed as expected&#39;
try:
    testObject.Ook = False
except AttributeError:
    print &#39;testObject.Ook ..... New-attribute set failed as expected&#39;&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;When this is run, the results are:

&lt;pre&gt;testObject.Prop1 ... 1
testObject.Prop2 ... ook
testObject.Prop3 ... True
testObject.Prop3 ... Reset failed as expected
testObject.Ook ..... New-attribute set failed as expected&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;The magic that makes &lt;em&gt;this&lt;/em&gt; work is in the overridce of &lt;code&gt;__setattr__&lt;/code&gt; 
(lines 12-18). Since &lt;code&gt;__setattr__&lt;/code&gt; is being overridden, the &lt;em&gt;local&lt;/em&gt; 
method gets called instead of the built-in that exists in &lt;code&gt;object&lt;/code&gt;. It 
checks to see if the private &lt;code&gt;__objectIsLocked&lt;/code&gt; attribute is being set, 
and if it&#39;s already been set to &lt;code&gt;True&lt;/code&gt; (which would lock the object&#39;s 
attributes, as we&#39;ll see shortly), and raises an error if this is the case. If 
the attribute being set is &lt;em&gt;not&lt;/em&gt; &lt;code&gt;__objectIsLocked&lt;/code&gt;, control 
passes to the next &lt;code&gt;if&lt;/code&gt; on line 16, where a general check of 
&lt;code&gt;__objectIsLocked&lt;/code&gt; is performed. If &lt;code&gt;__objectIsLocked&lt;/code&gt; is 
&lt;code&gt;True&lt;/code&gt;, then the object&#39;s attributes are locked, and an error is 
raised, otherwise control passes on, and the results of the base object&#39;s 
&lt;code&gt;__setattr__&lt;/code&gt;, with the same arguments, is returned.&lt;/p&gt;

&lt;p&gt;That&#39;s pretty much all we need, really, for &lt;code&gt;Record&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Record&lt;/b&gt; (Nominal final class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class Record( object ):
    &quot;&quot;&quot;Represents an immutable record as retrieved from a query against a database.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    __fieldNames = []
    __recordIsLocked = False

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    def _GetFieldNames( self ):
        &quot;&quot;&quot;Gets the field-names of the Record object created at instantiation.&quot;&quot;&quot;
        return self.__fieldNames

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    FieldNames = property( _GetFieldNames, None, None, &#39;Gets the field-names of the Record object created at instantiation.&#39; )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;keyword&#39;, &#39;properties&#39;, &#39;variable&#39;, &#39;Key/value pairs providing the record\&#39;s field-names and values of those fields.&#39; )
    def __init__( self, **properties ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != Record:
            raise NotImplementedError( &#39;Record is (nominally) a final class, and is not intended to be derived from.&#39; )
        for theKey in properties:
            self.__setattr__( theKey, properties[ theKey ] )
            if not theKey in self.__fieldNames:
                self.__fieldNames.append( theKey )
        self.__recordIsLocked = True

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;name&#39;, None, &#39;(String, required) The name of the attribute to be set.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(Any type, required) The value to be set in the named attribute.&#39; )
    def __setattr__( self, name, value ):
        &quot;&quot;&quot;Object-attribute setter.&quot;&quot;&quot;
        if name == &#39;_Record__recordIsLocked&#39; and self.__recordIsLocked != False:
            raise AttributeError( &#39;The record is locked, and cannot be unlocked&#39; )
        if self.__recordIsLocked:
            raise AttributeError( &#39;The record has been locked, and the &quot;%s&quot; attribute cannot be set.&#39; % ( name ) )
        if hasattr( self, name ) and name != &#39;_%s__recordIsLocked&#39; % ( self.__class__.__name__ ):
            raise AttributeError( &#39;The &quot;%s&quot; attribute already exists, and cannot be reset.&#39; % ( name ) )
        return object.__setattr__( self, name, value )

__all__ += [ &#39;Record&#39; ]&lt;/pre&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;8-9&lt;/dt&gt;
    &lt;dd&gt;Since &lt;code&gt;Record&lt;/code&gt; is nominally a final class, and cannot be 
        extended, the &lt;code&gt;__keys&lt;/code&gt; and &lt;code&gt;__recordIsLocked&lt;/code&gt; can 
        be safely made private. They are also defined as class-level attributes 
        so that they will already exist when &lt;code&gt;__setattr__&lt;/code&gt; is defined, 
        helping to protect those attributes from modification if a &lt;code&gt;Record&lt;/code&gt; 
        object is created with &quot;__keys&quot; or &quot;__recordIsLocked&quot; fields.&lt;/dd&gt;

    &lt;dt&gt;15-17, 31&lt;/dt&gt;
    &lt;dd&gt;A typical, though read-only property for the record, providing the 
        field-names of the record.&lt;/dd&gt;

    &lt;dt&gt;37-48&lt;/dt&gt;
    &lt;dd&gt;The object constructor:
        &lt;dl&gt;
            &lt;dt&gt;42-43&lt;/dt&gt;
            &lt;dd&gt;My standard check to assure that classes extending &lt;code&gt;Record&lt;/code&gt;, 
                so long as they call &lt;code&gt;Record.__init__&lt;/code&gt;, will raise an 
                error.&lt;/dd&gt;

            &lt;dt&gt;44-47&lt;/dt&gt;
            &lt;dd&gt;Reads in the keyword arguments, and calls &lt;code&gt;__setattr__&lt;/code&gt; 
                to set the supplied property names and values in the object.&lt;/dd&gt;
            &lt;dd&gt;One caveat about this format is that construction of a 
                &lt;code&gt;Record&lt;/code&gt; &lt;em&gt;must&lt;/em&gt; use a syntax like 
                &lt;code&gt;aRecord = Record( **fields )&lt;/code&gt;. If a call like 
                &lt;code&gt;aRecord = Record( fields )&lt;/code&gt; is attempted, a 
                &lt;code&gt;TypeError&lt;/code&gt; will be raised, even if the argument is a 
                dictionary.&lt;/dd&gt;

            &lt;dt&gt;48&lt;/dt&gt;
            &lt;dd&gt;Locks the record, preventing addition or modification of properties 
                in the object.&lt;/dd&gt;
        &lt;/dl&gt;
    &lt;/dd&gt;

    &lt;dt&gt;58-69&lt;/dt&gt;
    &lt;dd&gt;Overrides the default &lt;code&gt;__setattr__&lt;/code&gt; provided by the base 
        &lt;code&gt;object&lt;/code&gt; to prevent modification of the object&#39;s state-data 
        once the object&#39;s been locked:
        &lt;dl&gt;
            &lt;dt&gt;63-64&lt;/dt&gt;
            &lt;dd&gt;Allows the record to be locked, but not unlocked, by specifically 
                checking for the private attribute &lt;code&gt;__recordIsLocked&lt;/code&gt;, 
                and allowing it to be changed &lt;em&gt;only&lt;/em&gt; if it&#39;s False.&lt;/dd&gt;
            &lt;dd&gt;This could be changed, if extension of &lt;code&gt;Record&lt;/code&gt; was 
                needed, by changing the check to look something like this:
                &lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;if name == &#39;_%s__recordIsLocked&#39; % ( self.__class__.__name__ ) and self.__recordIsLocked != False:
    # do the rest&lt;/pre&gt;
            &lt;/dd&gt;

            &lt;dt&gt;65-66&lt;/dt&gt;
            &lt;dd&gt;Checks to see whether the object is locked, regardless of the 
                attribute-modification being attempted, and raises an error if 
                it is.&lt;/dd&gt;

            &lt;dt&gt;67-68&lt;/dt&gt;
            &lt;dd&gt;Checks specifically for attempts to re-set an existing attribute. 
                I don&#39;t &lt;em&gt;think&lt;/em&gt; this would ever come into play, since trying 
                to duplicate key-names in either a dictionary or a keyword-argument 
                list will raise errors, but since I can&#39;t &lt;em&gt;categorically&lt;/em&gt; 
                rule the possibility out, I checked for it anyway. I cannot think 
                of a way to test it, though...&lt;/dd&gt;
        &lt;/dl&gt;
    &lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Unit-tests&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class testRecord( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the Record class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testFinal( self ):
            &quot;&quot;&quot;Testing final nature of the Record class.&quot;&quot;&quot;
            try:
                testObject = RecordDerived()
                self.fail( &#39;Record is nominally a final class, and should not be extendable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Instantiating an object derived from Record should raise NotImplementedError, by %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the Record class.&quot;&quot;&quot;
            testObject = Record()
            self.assertTrue( isinstance( testObject, Record ), &#39;Instances of Record should be instances of Record... Duh!&#39; )
            testProperties = { &#39;Property1&#39;:1, &#39;Property2&#39;:&#39;ook&#39;, &#39;Property3&#39;:True }
            testObject = Record( **testProperties )
            for property in testProperties:
                testValue = eval( &#39;testObject.%s&#39; % property )
                self.assertEquals( testValue, testProperties[ property ], &#39;&#39; )
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the Record class.&quot;&quot;&quot;
            items = getMemberNames( Record )[0]
            actual = len( items )
            expected = 1
            self.assertEquals( expected, actual, &#39;Record is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the Record class.&quot;&quot;&quot;
            items = getMemberNames( Record )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;Record is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test properties

        def testFieldNames( self ):
            &quot;&quot;&quot;Unit-tests the FieldNames property of the Record class.&quot;&quot;&quot;
            testProperties = { &#39;Property1&#39;:1, &#39;Property2&#39;:&#39;ook&#39;, &#39;Property3&#39;:True }
            testObject = Record( **testProperties )
            self.assertEquals( testObject.FieldNames, testProperties.keys() )
            try:
                testObject.Keys = &#39;ook&#39;
                self.fail( &#39;A Record\&#39;s FieldNames cannot be altered.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Changes to a record\&#39;s FieldNames should raise AttributeError, bu %s was raised instead:\n  %s &#39; % ( error.__class__.__name__, error ) )
                

        def testImmutability( self ):
            &quot;&quot;&quot;Unit-tests the immutability of records.&quot;&quot;&quot;
            testProperties = { &#39;Property1&#39;:1, &#39;Property2&#39;:&#39;ook&#39;, &#39;Property3&#39;:True }
            testObject = Record( **testProperties )
            try:
                testObject.NewProperty = &#39;ook&#39;
                self.fail( &#39;Once created, a record should not allow the creation of a new attribute/field.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Changes to a record\&#39;s data should raise AttributeError, bu %s was raised instead:\n  %s &#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.Property1 = &#39;ook&#39;
                self.fail( &#39;Once created, a record should not allow the alteration of an existing attribute/field.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Changes to a record\&#39;s data should raise AttributeError, bu %s was raised instead:\n  %s &#39; % ( error.__class__.__name__, error ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testRecord ) )&lt;/pre&gt;

&lt;p&gt;Most of the unit-tests are pretty typical, so there&#39;s not a lot to comment on:&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;25&lt;/dt&gt;
    &lt;dd&gt;Note the object-construction argument: &lt;code&gt;**testProperties&lt;/code&gt;m not &lt;code&gt;testProperties&lt;/code&gt;. THis is an example of the syntax noted above for creation of a &lt;code&gt;Record&lt;/code&gt;.&lt;/dd&gt;

    &lt;dt&gt;64-81&lt;/dt&gt;
    &lt;dd&gt;Tests the immutability of a &lt;code&gt;Record&lt;/code&gt;:
        &lt;dl&gt;
            &lt;dt&gt;68-74&lt;/dt&gt;
            &lt;dd&gt;Testing that creation of a new attribute on a &lt;code&gt;Record&lt;/code&gt; 
                instance isn&#39;t allowed.&lt;/dd&gt;
            &lt;dt&gt;75-81&lt;/dt&gt;
            &lt;dd&gt;Testing that alteration of an existing attribute is not allowed.&lt;/dd&gt;
        &lt;/dl&gt;
    &lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Though this is only one of the two concrete classes I said I was going to hit, 
this post is pretty long, so I&#39;ll pick up next time with &lt;code&gt;ResultSet&lt;/code&gt;.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/6894415047545001295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-4.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6894415047545001295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6894415047545001295'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-4.html' title='The DataConnectors module (part 4)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0ZPj2wcJQLgrrvWuu3xvyitEs7gOWfKT7U2_uAd2vCBlocJepcCUwh0xO2EebtIGUejTns_6gLgMlHlOCiDN3dttsz-jbDDhJyUFT5uVTMBzAh4EfNeeQm0-x27tlaQDEvTofrlqHdVw/s72-c/DataConnectors-uml.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-4880118616847020561</id><published>2011-12-12T04:30:00.000-08:00</published><updated>2011-12-12T04:30:01.384-08:00</updated><title type='text'>Disruption to my publication-cycle...</title><content type='html'>As I was finishing up the previous post, and getting ready to start the next one (the next part of the &lt;code&gt;DataConnectors&lt;/code&gt; module), I was also preparing for a major job-change, leaving one company for another, for the first time in nearly seven years, and only the second time in the last 22.&lt;br /&gt;
&lt;br /&gt;
As a result of this change, my normal routine for writing these posts may be disrupted for a while, and I&#39;m not sure how (or if) it will recover. I&#39;ll still be making an attempt to keep at least a few items in the blog&#39;s scheduled backlog, but I can&#39;t guarantee that I&#39;ll be able to swing that just yet.&lt;br /&gt;
&lt;br /&gt;
I&#39;ve also reached a major design-decision point in &lt;code&gt;DataConnectors&lt;/code&gt;, and I&#39;m having to thrash through a lot of alternatives about how I want to implement things. In at least one major case, I&#39;m also having to give a lot of thought to &lt;i&gt;why&lt;/i&gt; a given implementation should be chosen. &lt;br /&gt;
&lt;br /&gt;
So, if you&#39;re following my writing/coding, please bear with any disruptions to my publication schedule for at least a little while. I&#39;m hoping that I&#39;ll be able to either resume my normal schedule, or come up with a new one over the next couple of weeks, but I simply don&#39;t know what that&#39;ll look like yet, and until I get through the current design contemplation, even if I &lt;i&gt;have&lt;/i&gt; the time, I may not be able to &lt;i&gt;produce&lt;/i&gt; much for a while.&lt;br /&gt;
&lt;br /&gt;
Awkward timing, I guess... Such is the stuff that life in the glamorous world of software design and development is made of.&lt;br /&gt;
&lt;br /&gt;
On the plus-side, though, by the time &lt;i&gt;this&lt;/i&gt; post is published, I&#39;ll be just a two weeks away from a week-long break for the Christmas/New Year&#39;s holiday-week, and I &lt;i&gt;definitely&lt;/i&gt; should be able to get more backlog going by then.&lt;br /&gt;
&lt;br /&gt;
Stay tuned!</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/4880118616847020561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/disruption-to-my-publication-cycle.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4880118616847020561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4880118616847020561'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/disruption-to-my-publication-cycle.html' title='Disruption to my publication-cycle...'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-1791130880485369103</id><published>2011-12-09T04:30:00.000-08:00</published><updated>2011-12-09T04:30:00.733-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="abstract class"/><category scheme="http://www.blogger.com/atom/ns#" term="BaseDataConnection"/><category scheme="http://www.blogger.com/atom/ns#" term="BaseDataObject"/><category scheme="http://www.blogger.com/atom/ns#" term="DataConnectors"/><title type='text'>The DataConnectors module (part 3)</title><content type='html'>&lt;p&gt;So, now that the interface-definitions are out of the way, I&#39;m going to tackle 
the nominal abstract classes. There are only two, &lt;code&gt;BaseDataConnector&lt;/code&gt; 
that will provide structure for concrete data-connector classes like 
&lt;code&gt;MySQLConnector&lt;/code&gt; (and others down the line, potentially), and 
&lt;code&gt;BaseDataObject&lt;/code&gt;, which will provide &lt;a href=&quot;http://en.wikipedia.org/wiki/Create,_read,_update_and_delete&quot;&gt;CRUD&lt;/a&gt; 
capabilities for objects whose state is going to be persisted in a back-end 
data-store.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BaseDataConnector&lt;/code&gt; provides standard connection parameters (host, 
database, user and password) as properties for connector objects. It does 
&lt;em&gt;not&lt;/em&gt; define the connection property, or the mechanism used to connect 
to the database, however, since those will vary from database-engine to 
database-engine. A typical usage is expected to look something like the code 
below, assuming that &lt;code&gt;Connector&lt;/code&gt; is a class derived from 
&lt;code&gt;BaseDataConnector&lt;/code&gt;:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;connection = Connector( 
    host=&#39;host&#39;, 
    database=&#39;database&#39;, 
    user=&#39;user&#39;, 
    password=&#39;password&#39;
    )
# Optionally, the &quot;Connect&quot; method can be called before executing a query:
# connection.Connect()
results = connection.Query( &#39;some SQL code&#39; ).Results
&lt;/pre&gt;

&lt;p&gt;This looks pretty simple, though there&#39;s a few things I still need to think 
out (specifically, what I want the items in the results to look like). But even 
at this point, there&#39;s a lot going on &quot;under the hood,&quot; as it were, and some 
possibilities that I&#39;m contemplating for the future:
&lt;ul&gt;
    &lt;li&gt;The creation of the &lt;code&gt;Connector&lt;/code&gt; handles all of the 
        connection-information for the back-end datasource - that much we pretty 
        much have to assume.&lt;/li&gt;
    &lt;li&gt;Whether the code written against the Connector explicitly calls 
        &lt;code&gt;Connect&lt;/code&gt; or not, there has to be an active connection before 
        queries can be run, so the first call to a query&#39;s &lt;code&gt;Results&lt;/code&gt; 
        property would perhaps have to tell the connector-object that it&#39;s ready 
        to query, and to create the connection.
        &lt;ul&gt;
            &lt;li&gt;I have to admit that I like the idea of lazily-instantiating 
                queries, results, etc. - Provided that some care is taken to keep 
                things logical, I think that using a lazy instantiation would 
                potentially save some database-connection time.&lt;/li&gt;
            &lt;li&gt;I also contemplated the idea of having one or more queues of 
                queries associated with the connector-object: One for queries to 
                be executed at object destruction, and one to allow queries to be 
                generated and stored before they were actually needed, which 
                would allow the first &quot;real&quot; need for a query&#39;s results to run 
                &lt;em&gt;all&lt;/em&gt; of the pending queries in one connection-batch, 
                maybe. I&#39;m not sure how much (if any) difference this would 
                actually make, and it&#39;s potentially more complicated than I want 
                to deal with right this minute, though, so I&#39;m filing that away 
                for future reference/use.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;The &lt;code&gt;results&lt;/code&gt; returned could/should be allowed to support 
        multiple assignments (e.g., &lt;code&gt;results1, results2 = connection.Query( 
        &#39;some SQL code&#39; ).Results&lt;/code&gt;) as well as single results. I&#39;m not 
        quite sure how I&#39;m going to handle that just yet...&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;At the nominal-abstract-class level, there&#39;s not a lot that can realistically 
be implemented - mostly standard properties and the like - so the code is pretty 
short:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;BaseDataConnector&lt;/b&gt; (Nominal abstract class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class BaseDataConnector( IsDataConnector, IsConfigurable, object ):
    &quot;&quot;&quot;Provides baseline functionality, interface requirements and type-identity for objects that can represent a connection to a back-end database.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetDatabase( self ):
        &quot;&quot;&quot;Gets the name of the database that the connection will be made to.&quot;&quot;&quot;
        return self._database

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetHost( self ):
        &quot;&quot;&quot;Gets the name or IP-address of the host that the database resides upon.&quot;&quot;&quot;
        return self._host

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetPassword( self ):
        &quot;&quot;&quot;Gets the password used to connect to the database.&quot;&quot;&quot;
        return self._password

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetUser( self ):
        &quot;&quot;&quot;Gets the user-name used to connect to the database.&quot;&quot;&quot;
        return self._user

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(String, required) The name of the database that the connection will be made to.&#39; )
    def _SetDatabase( self, value ):
        &quot;&quot;&quot;Sets the name of the database that the connection will be made to.&quot;&quot;&quot;
        if type( value ) != types.StringType:
            raise TypeError( &#39;%s.Database expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        if value == &#39;&#39; or &#39;\n&#39; in value or &#39;\r&#39; in value or &#39;\t&#39; in value:
            raise ValueError( &#39;%s.Database expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        self._database = value

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(String, required) The name of the Host that the connection will be made to.&#39; )
    def _SetHost( self, value ):
        &quot;&quot;&quot;Sets the name of the Host that the connection will be made to.&quot;&quot;&quot;
        if type( value ) != types.StringType:
            raise TypeError( &#39;%s.Host expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        if value == &#39;&#39; or &#39;\n&#39; in value or &#39;\r&#39; in value or &#39;\t&#39; in value:
            raise ValueError( &#39;%s.Host expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        self._host = value

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(String, required) The password that the connection will be made with.&#39; )
    def _SetPassword( self, value ):
        &quot;&quot;&quot;Sets the password that the connection will be made with.&quot;&quot;&quot;
        if type( value ) != types.StringType:
            raise TypeError( &#39;%s.Password expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        if value == &#39;&#39; or &#39;\n&#39; in value or &#39;\r&#39; in value or &#39;\t&#39; in value:
            raise ValueError( &#39;%s.Password expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        self._password = value

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(String, required) The name of the User that the connection will be made with.&#39; )
    def _SetUser( self, value ):
        &quot;&quot;&quot;Sets the name of the User that the connection will be made with.&quot;&quot;&quot;
        if type( value ) != types.StringType:
            raise TypeError( &#39;%s.User expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        if value == &#39;&#39; or &#39;\n&#39; in value or &#39;\r&#39; in value or &#39;\t&#39; in value:
            raise ValueError( &#39;%s.User expects a single-line, non-empty string value that contains to tabs.&#39; % ( self.__class__.__name__ ) )
        self._user = value

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelDatabase( self ):
        &quot;&quot;&quot;Deletes the name of the database that the connection will be made to.&quot;&quot;&quot;
        self._database = None

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelHost( self ):
        &quot;&quot;&quot;Deletes the name or IP-address of the host that the database resides upon.&quot;&quot;&quot;
        self._host = None

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelPassword( self ):
        &quot;&quot;&quot;Deletes the password used to connect to the database.&quot;&quot;&quot;
        self._password = None

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelUser( self ):
        &quot;&quot;&quot;Deletes the user-name used to connect to the database.&quot;&quot;&quot;
        self._user = None

    ##################################
    # Class Properties               #
    ##################################

    Database = property( _GetDatabase, _SetDatabase, _DelDatabase, &#39;Gets, sets or deletes the name of the database that the connection will be made to.&#39; )
    Host = property( _GetHost, _SetHost, _DelHost, &#39;Gets, sets or deletes the name or IP-address of the host that the database resides upon.&#39; )
    Password = property( _GetPassword, _SetPassword, _DelPassword, &#39;Gets, sets or deletes the password used to connect to the database.&#39; )
    User = property( _GetUser, _SetUser, _DelUser, &#39;Gets, sets or deletes the user-name used to connect to the database.&#39; )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;host&#39;, &#39;The name or IP-address of the host that the database resides upon.&#39; )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;database&#39;, &#39;The name of the database that the connection will be made to.&#39; )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;user&#39;, &#39;The user-name used to connect to the database.&#39; )
    @DocumentArgument( &#39;keyword&#39;, &#39;parameters&#39;, &#39;password&#39;, &#39;The password used to connect to the database.&#39; )
    def __init__( self, **parameters ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == BaseDataConnector:
            raise NotImplementedError( &#39;BaseDataConnector is (nominally) an abstract class, and is not intended to be instantiated.&#39; )
        self._DelDatabase()
        if parameters.has_key( &#39;database&#39; ):
            self._SetDatabase( parameters[ &#39;database&#39; ] )
        self._DelHost()
        if parameters.has_key( &#39;host&#39; ):
            self._SetHost( parameters[ &#39;host&#39; ] )
        self._DelPassword()
        if parameters.has_key( &#39;password&#39; ):
            self._SetPassword( parameters[ &#39;password&#39; ] )
        self._DelUser()
        if parameters.has_key( &#39;user&#39; ):
            self._SetUser( parameters[ &#39;user&#39; ] )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    ##################################
    # IsConfigurable Methods         #
    ##################################

    @ToDo( &#39;Figure out how to approach unit-testing of this method.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;configProvider&#39;, None, &#39;A configuration provider object, an instance of one of IsConfigurable.ConfigurationTypes&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;configSection&#39;, None, &#39;String, required) The configuration-section name that the object\&#39;s configuration-state should be retrieved from.&#39; )
    @DocumentConfiguration( &#39;Section Name&#39;, &#39;host&#39;, &#39;The name or IP-address of the host that the database resides upon.&#39; )
    @DocumentConfiguration( &#39;Section Name&#39;, &#39;database&#39;, &#39;The name of the database that the connection will be made to.&#39; )
    @DocumentConfiguration( &#39;Section Name&#39;, &#39;user&#39;, &#39;The user-name used to connect to the database.&#39; )
    @DocumentConfiguration( &#39;Section Name&#39;, &#39;password&#39;, &#39;The password used to connect to the database.&#39; )
    def Configure( self, configProvider, configSection ):
        &quot;&quot;&quot;Configures the object using configuration data from the specified section of the specified provider.&quot;&quot;&quot;
        parameters = configProvider[ configSection ]
        if parameters.has_key( &#39;database&#39; ):
            self._SetDatabase( parameters[ &#39;database&#39; ] )
        if parameters.has_key( &#39;host&#39; ):
            self._SetHost( parameters[ &#39;host&#39; ] )
        if parameters.has_key( &#39;password&#39; ):
            self._SetPassword( parameters[ &#39;password&#39; ] )
        if parameters.has_key( &#39;user&#39; ):
            self._SetUser( parameters[ &#39;user&#39; ] )

    ##################################
    # IsDataConnector Methods        #
    ##################################

    # Leaving Connect method abstracted from IsDataConnector

    # Leaving Query method abstracted from IsDataConnector

__all__ += [ &#39;BaseDataConnector&#39; ]&lt;/pre&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;12-30&lt;/dt&gt;
    &lt;dd&gt;Typical (by now) property-getter methods.&lt;/dd&gt;

    &lt;dt&gt;36-74&lt;/dt&gt;
    &lt;dd&gt;Typical (by now) property-setter methods.&lt;/dd&gt;

    &lt;dt&gt;80-98&lt;/dt&gt;
    &lt;dd&gt;I&#39;m generating explicit Deleter methods as well, since I want the properties 
        to be &quot;deletable,&quot; but without allowing &lt;em&gt;actual&lt;/em&gt; deletion - instead 
        I want them to be set to &lt;code&gt;None&lt;/code&gt;. Partly this is so that I&#39;ve 
        got an easy way to set them to their default values during construction 
        or configuration, but mostly it&#39;s because I anticipate that in actual 
        implementations I&#39;ll need to check whether they&#39;re set or not, and I&#39;d 
        rather that they always have &lt;em&gt;at least&lt;/em&gt; a &lt;code&gt;None&lt;/code&gt; value, 
        rather than raising &lt;code&gt;AttributeError&lt;/code&gt;s if they get accidentally 
        deleted.&lt;/dd&gt;

    &lt;dt&gt;104-107&lt;/dt&gt;
    &lt;dd&gt;The &quot;real&quot; property declarations.&lt;/dd&gt;

    &lt;dt&gt;113-134&lt;/dt&gt;
    &lt;dd&gt;The object constructor. Note that we&#39;re using a keyword-list style argument, 
        documenting the recognized names, and using the class&#39; &lt;code&gt;_Set...&lt;/code&gt; 
        methods to actually set the values. We &lt;em&gt;could&lt;/em&gt; use the properties 
        that those methods map to, but by going directly to the methods themselves, 
        if the &quot;settability&quot; of the property is changed, that won&#39;t introduce 
        errors.&lt;/dd&gt;

    &lt;dt&gt;148-166&lt;/dt&gt;
    &lt;dd&gt;Object configuration. To keep things simple, it uses the same names as 
        the object constructor.&lt;/dd&gt;
    &lt;dd&gt;I&#39;m not sure how I want to approach the unit-testing of this method just 
        yet (as noted in the ToDo on 148). I&#39;ll have to give that some thought, 
        and I&#39;ll likely generate a &quot;clean-up&quot; post once everything else is done 
        that will address that and any other items that seem relevant, but for 
        now, I&#39;m going to leave this untested.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Unit-tests&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class BaseDataConnectorDerived( BaseDataConnector ):
        def __init__( self, **parameters ):
            BaseDataConnector.__init__( self, **parameters )
    
    class testBaseDataConnector( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the BaseDataConnector class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testDerived( self ):
            &quot;&quot;&quot;Testing abstract nature of the BaseDataConnector class.&quot;&quot;&quot;
            try:
                testObject = BaseDataConnector()
                self.fail( &#39;BaseDataConnector is nominally abstract, and should not be instantiable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;BaseDataConnector is nominally abstract, and should raise NotImplementedError when instantiated, but %s was raised instead\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = BaseDataConnectorDerived()
            self.assertTrue( isinstance( testObject, BaseDataConnector ) , &#39;BaseDataConnector-derived objects should be instances of BaseDataConnector&#39; )
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the BaseDataConnector class.&quot;&quot;&quot;
            items = getMemberNames( BaseDataConnector )[0]
            actual = len( items )
            expected = 5
            self.assertEquals( expected, actual, &#39;BaseDataConnector is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the BaseDataConnector class.&quot;&quot;&quot;
            items = getMemberNames( BaseDataConnector )[1]
            actual = len( items )
            expected = 3
            self.assertEquals( expected, actual, &#39;BaseDataConnector is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Unit-test properties

        def testConnection( self ):
            &quot;&quot;&quot;Unit-tests the Connection property of the BaseDataConnector abstract class.&quot;&quot;&quot;
            self.assertEquals( BaseDataConnector.Connection, IsDataConnector.Connection, &#39;BaseDataConnector implements IsdataConnector, but should not define the Connection property.&#39; )

        def testDatabase( self ):
            &quot;&quot;&quot;Unit-tests the Database property of the BaseDataConnector abstract class.&quot;&quot;&quot;
            goodValues = [ &#39;databasename&#39;, &#39;database_name&#39;, &#39;a&#39; ]
            for testValue in goodValues:
                testObject = BaseDataConnectorDerived()
                testObject.Database = testValue
                self.assertEquals( testObject.Database, testValue, &#39;The database property, if set, should get the set value.&#39; )
                testObject = BaseDataConnectorDerived( database=testValue )
                self.assertEquals( testObject.Database, testValue, &#39;The database property, if set, should get the set value.&#39; )
            badTypes = [ None, True, 0, [] ]
            for testValue in badTypes:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.Database = testValue
                    self.fail( &#39;Setting Database to %s should raise a TypeError.&#39; )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting Database to %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( database=testValue)
                    self.fail( &#39;Creating a BaseDataConnector with a database value of %s should raise a TypeError.&#39; )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a database value of %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            badValues = [ &#39;&#39;, &#39;bad\nvalue&#39;, &#39;bad\tvalue&#39;, &#39;bad\rvalue&#39; ]
            for testValue in badValues:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.Database = testValue
                    self.fail( &#39;Setting Database to %s should raise a ValueError.&#39; )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting Database to %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( database=testValue)
                    self.fail( &#39;Creating a BaseDataConnector with a database value of %s should raise a ValueError.&#39; )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a database value of %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testHost( self ):
            &quot;&quot;&quot;Unit-tests the Host property of the BaseDataConnector abstract class.&quot;&quot;&quot;
            goodValues = [ &#39;Hostname&#39;, &#39;Host_name&#39;, &#39;a&#39; ]
            for testValue in goodValues:
                testObject = BaseDataConnectorDerived()
                testObject.Host = testValue
                self.assertEquals( testObject.Host, testValue, &#39;The Host property, if set, should get the set value.&#39; )
                testObject = BaseDataConnectorDerived( host=testValue )
                self.assertEquals( testObject.Host, testValue, &#39;The Host property, if set, should get the set value.&#39; )
            badTypes = [ None, True, 0, [] ]
            for testValue in badTypes:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.Host = testValue
                    self.fail( &#39;Setting Host to %s should raise a TypeError.&#39; )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting Host to %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( host=testValue)
                    self.fail( &#39;Creating a BaseDataConnector with a Host value of %s should raise a TypeError.&#39; )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a Host value of %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            badValues = [ &#39;&#39;, &#39;bad\nvalue&#39;, &#39;bad\tvalue&#39;, &#39;bad\rvalue&#39; ]
            for testValue in badValues:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.Host = testValue
                    self.fail( &#39;Setting Host to %s should raise a ValueError.&#39; % ( testValue ) )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting Host to %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( host=testValue)
                    self.fail( &#39;Creating a BaseDataConnector with a Host value of %s should raise a ValueError.&#39; )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a Host value of %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )

        def testPassword( self ):
            &quot;&quot;&quot;Unit-tests the Password property of the BaseDataConnector abstract class.&quot;&quot;&quot;
            goodValues = [ &#39;password&#39;, &#39;pass_word&#39;, &#39;a&#39; ]
            for testValue in goodValues:
                testObject = BaseDataConnectorDerived()
                testObject.Password = testValue
                self.assertEquals( testObject.Password, testValue, &#39;The Password property, if set, should get the set value.&#39; )
                testObject = BaseDataConnectorDerived( password=testValue )
                self.assertEquals( testObject.Password, testValue, &#39;The Password property, if set, should get the set value.&#39; )
            badTypes = [ None, True, 0, [] ]
            for testValue in badTypes:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.Password = testValue
                    self.fail( &#39;Setting Password to %s should raise a TypeError.&#39; )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting Password to %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( password=testValue )
                    self.fail( &#39;Creating a BaseDataConnector with a password value of %s should raise a TypeError.&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a password value of %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            badValues = [ &#39;&#39;, &#39;bad\nvalue&#39;, &#39;bad\tvalue&#39;, &#39;bad\rvalue&#39; ]
            for testValue in badValues:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.Password = testValue
                    self.fail( &#39;Setting Password to %s should raise a ValueError.&#39; % ( testValue ) )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting Password to %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( password=testValue)
                    self.fail( &#39;Creating a BaseDataConnector with a password value of %s should raise a ValueError.&#39; )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a password value of %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testUser( self ):
            &quot;&quot;&quot;Unit-tests the User property of the BaseDataConnector abstract class.&quot;&quot;&quot;
            goodValues = [ &#39;User&#39;, &#39;user_name&#39;, &#39;a&#39; ]
            for testValue in goodValues:
                testObject = BaseDataConnectorDerived()
                testObject.User = testValue
                self.assertEquals( testObject.User, testValue, &#39;The User property, if set, should get the set value.&#39; )
                testObject = BaseDataConnectorDerived( user=testValue )
                self.assertEquals( testObject.User, testValue, &#39;The User property, if set, should get the set value.&#39; )
            badTypes = [ None, True, 0, [] ]
            for testValue in badTypes:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.User = testValue
                    self.fail( &#39;Setting User to %s should raise a TypeError.&#39; )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting User to %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( user=testValue )
                    self.fail( &#39;Creating a BaseDataConnector with a User value of %s should raise a TypeError.&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a User value of %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            badValues = [ &#39;&#39;, &#39;bad\nvalue&#39;, &#39;bad\tvalue&#39;, &#39;bad\rvalue&#39; ]
            for testValue in badValues:
                testObject = BaseDataConnectorDerived()
                try:
                    testObject.User = testValue
                    self.fail( &#39;Setting User to %s should raise a ValueError.&#39; % ( testValue ) )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting User to %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
                try:
                    testObject = BaseDataConnectorDerived( user=testValue)
                    self.fail( &#39;Creating a BaseDataConnector with a User value of %s should raise a ValueError.&#39; )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Creating a BaseDataConnector with a User value of %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        # Unit-test methods

        def testConfigure( self ):
            &quot;&quot;&quot;Unit-tests the Configure method of the BaseDataConnector abstract class.&quot;&quot;&quot;
            # Not sure how I want to test this yet. See ToDo on Configure.
            pass

        def testConnect( self ):
            &quot;&quot;&quot;Unit-tests the Connect method of the BaseDataConnector abstract class.&quot;&quot;&quot;
            self.assertEquals( BaseDataConnector.Connect, IsDataConnector.Connect, &#39;BaseDataConnector should not define the Connect method.&#39; )

        def testQuery( self ):
            &quot;&quot;&quot;Unit-tests the Query method of the BaseDataConnector abstract class.&quot;&quot;&quot;
            self.assertEquals( BaseDataConnector.Query, IsDataConnector.Query, &#39;BaseDataConnector should not define the Query method.&#39; )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testBaseDataConnector ) )&lt;/pre&gt;

&lt;p&gt;With the exception of the method-tests, this should be pretty straightforward by now - it&#39;s my typical structure...&lt;/p&gt;

&lt;p&gt;The method-tests, though, may deserve some explanation. I noted above that I 
wasn&#39;t sure how I wanted to test the &lt;code&gt;Configure&lt;/code&gt; method (as much as 
anything else because it, ideally, should be tested against real configuration-files, 
and I&#39;m not sure I want to generate the sheer number of files that would be needed). 
For now, I&#39;m going to leave that method &lt;em&gt;untested&lt;/em&gt;, though I&#39;ll have to get 
back to that fairly soon to maintain my code-coverage goals for code tied to my 
posts.&lt;/p&gt;

&lt;p&gt;The tests for &lt;code&gt;Connect&lt;/code&gt; and &lt;code&gt;Query&lt;/code&gt; are pretty simple, 
but what they &lt;em&gt;do&lt;/em&gt; may not be readily apparent. Each of these is really 
just making sure that there is no local method (e.g., that &lt;code&gt;BaseDataConnector&lt;/code&gt; 
doesn&#39;t define them, but inherits the nominally-abstract stubs from 
&lt;code&gt;IsDataConnector&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The next nominally abstract class to tackle is &lt;code&gt;BaseDataObject&lt;/code&gt;. 
The intention behind &lt;code&gt;BaseDataObject&lt;/code&gt; is to provide common CRUD 
functionality for derived objects, as noted previously. In order to try to keep 
the actual read/write/update/delete process as simple as possible, creation of 
and update to a state-data record is going to be wrapped in a &lt;code&gt;Save&lt;/code&gt; 
method that pays attention to various flag-value properties:
    &lt;dl&gt;
        &lt;dt&gt;IsDeleted&lt;/dt&gt;
        &lt;dd&gt;Indicates whether an object&#39;s state-data record is pseudo-deleted. 
            It&#39;s not uncommon, at least in my experience, to save even &quot;deleted&quot; 
            records in a system for any of several reasons, practical and legal 
            alike. This flag exists in order to assure that data-objects can be 
            saved while being easily flagged as deleted.&lt;/dd&gt;
        &lt;dt&gt;IsDirty&lt;/dt&gt;
        &lt;dd&gt;Indicates whether an object&#39;s state-data record needs to be 
            &lt;em&gt;updated&lt;/em&gt; in the back-end data-store. This means that objects 
            deriving from &lt;code&gt;BaseDataObject&lt;/code&gt; will have to make sure that 
            when a persistent state value changes, the entire object is flagged 
            as &quot;dirty&quot;.&lt;/dd&gt;
        &lt;dt&gt;IsNew&lt;/dt&gt;
        &lt;dd&gt;Indicates whether an object&#39;s state-data record needs to be 
            &lt;em&gt;created&lt;/em&gt; in the back-end data-store. When a record is created, 
            the unique idenitifier of the record will need to be returned in some 
            fashion, and stored in the object&#39;s state if at all possible.&lt;/dd&gt;
    &lt;/dl&gt;
&lt;/p&gt;

&lt;p&gt;These properties have implications on the design of database-tables that store 
object state-data: They should ideally have a unique identifier (which is generally 
desirable anyway), and a flag-field that indicates whether the state-record is 
&quot;deleted&quot; (or is active, if you prefer that approach). Other common fields (creation 
date, last modified date) could be added easily enough to &lt;code&gt;BaseDataObject&lt;/code&gt;, 
but they feel less... globally used, I guess, to me.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;IsDirty&lt;/code&gt; and &lt;code&gt;IsNew&lt;/code&gt; flags raise some questions as 
well. Ideally, I think, no given object should ever be considered both New and Dirty, since 
one implies that the object-record hasn&#39;t been created, while the other implies 
that it has, and on top of that, that it&#39;s been modified &lt;em&gt;since&lt;/em&gt; it was 
created. An object that is created in an application and that will be saved to a 
database should, therefore, be considered &quot;new&quot; until it has been saved (at which 
point both flags should be False). Changes to that object&#39;s state before it&#39;s 
saved &lt;em&gt;should&lt;/em&gt; also make the object &quot;dirty,&quot; logically, but we &lt;em&gt;don&#39;t&lt;/em&gt; 
want both a creation &lt;em&gt;and&lt;/em&gt; an update to happen on the same object when it&#39;s 
being saved. I believe that all of this should be handled at the &lt;code&gt;Save&lt;/code&gt;-method 
level, with logic that would look something like this:
    &lt;ul&gt;
        &lt;li&gt;When an object is created, it is new and not dirty; Saving it should 
            create a state-record.&lt;/li&gt;
        &lt;li&gt;When that object is modified, it is both new and dirty; Saving it 
            should create a state-record.&lt;/li&gt;
        &lt;li&gt;When that object is pseudo-deleted, it is both new and deleted; 
            Saving it should create a state-record, and that record should indicate 
            that it is &quot;deleted&quot;.&lt;/li&gt;
        &lt;li&gt;When an object is retrieved from the database, it is not new, nor is 
            it dirty; Saving it should do nothing, if only to avoid unneeded 
            database connections/interaction.&lt;/li&gt;
        &lt;li&gt;When a retrieved object is modified, it is dirty but still not new; 
            Saving it should cause an update.&lt;/li&gt;
        &lt;li&gt;When a retrieved object is pseudo-deleted, it is both dirty and deleted; 
            Saving it should cause an update.&lt;/li&gt;
        &lt;li&gt;Any of these changes should be accounted for when the object is destroyed, 
            in order to assure that state-data is correctly persisted.&lt;/li&gt;
    &lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;BaseDataObject&lt;/b&gt; (Nominal abstract class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class BaseDataObject( object ):
    &quot;&quot;&quot;Provides baseline functionality, interface requirements and type-identity for objects whose state can be persisted in a back-end database.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetDataSource( self ):
        &quot;&quot;&quot;Gets the data-source (an IsDataConnector instance) that the data-object will use to perform it&#39;s queries against.&quot;&quot;&quot;
        return self._dataSource

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetId( self ):
        &quot;&quot;&quot;Gets the unique ID of the object&#39;s state-data-record in the back-end database.&quot;&quot;&quot;
        return self._id

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetIsDeleted( self ):
        &quot;&quot;&quot;Gets the flag indicating whether the object&#39;s state-data record is &quot;deleted&quot;.&quot;&quot;&quot;
        return self._isDeleted

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetIsDirty( self ):
        &quot;&quot;&quot;Gets the flag indicating whether the object&#39;s state-data record is &quot;Dirty&quot;.&quot;&quot;&quot;
        return self._isDirty

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetIsNew( self ):
        &quot;&quot;&quot;Gets the flag indicating whether the object&#39;s state-data record is &quot;New&quot;.&quot;&quot;&quot;
        return self._isNew

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The data-source (an IsDataConnector instance) that the data-object will use to perform it\&#39;s queries against.&#39; )
    def _SetDataSource( self, value ):
        &quot;&quot;&quot;Sets the data-source (an IsDataConnector instance) that the data-object will use to perform it&#39;s queries against.&quot;&quot;&quot;
        if self._dataSource:
            raise NotImplementedError( &#39;%s.DataSource cannot be changed after it has been set.&#39; % ( self.__class__.__name__ ) )
        if not isinstance( value, IsDataConnector ):
            raise TypeError( &#39;%s.DataSource expects an instance of IsDataConnector.&#39; % ( self.__class__.__name__ ) )
        self._dataSource = value

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The unique ID of the object\&#39;s state-data-record in the back-end database.&#39; )
    def _SetId( self, value ):
        &quot;&quot;&quot;Sets the unique ID of the object&#39;s state-data-record in the back-end database.&quot;&quot;&quot;
        self._id = value

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The flag indicating whether the object\&#39;s state-data record is &quot;deleted&quot;.&#39; )
    def _SetIsDeleted( self, value ):
        &quot;&quot;&quot;Sets the flag indicating whether the object&#39;s state-data record is &quot;deleted&quot;.&quot;&quot;&quot;
        if value != True and value != False and value != 1 and value != 0:
            raise TypeError( &#39;%s.IsDeleted expects a True, False, 1 or 0 value.&#39; % ( self.__class__.__name__ ) )
        self._isDeleted = value

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The flag indicating whether the object\&#39;s state-data record is &quot;dirty&quot; (in need of an update).&#39; )
    def _SetIsDirty( self, value ):
        &quot;&quot;&quot;Sets the flag indicating whether the object&#39;s state-data record is &quot;dirty&quot; (in need of an update).&quot;&quot;&quot;
        if value != True and value != False and value != 1 and value != 0:
            raise TypeError( &#39;%s.IsDirty expects a True, False, 1 or 0 value.&#39; % ( self.__class__.__name__ ) )
        self._isDirty = value
        if value:
            self._isNew = False

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The flag indicating whether the object\&#39;s state-data record is &quot;New&quot;.&#39; )
    def _SetIsNew( self, value ):
        &quot;&quot;&quot;Sets the flag indicating whether the object&#39;s state-data record is &quot;New&quot;.&quot;&quot;&quot;
        if value != True and value != False and value != 1 and value != 0:
            raise TypeError( &#39;%s.IsNew expects a True, False, 1 or 0 value.&#39; % ( self.__class__.__name__ ) )
        self._isNew = value

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelDataSource( self ):
        &quot;&quot;&quot;Deletes the data-source that the data-object will use to perform it&#39;s queries against.&quot;&quot;&quot;
        self._dataSource = None

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelId( self ):
        &quot;&quot;&quot;Deletes the data-source that the data-object will use to perform it&#39;s queries against.&quot;&quot;&quot;
        self._id = None

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelIsDeleted( self ):
        &quot;&quot;&quot;Deletes the flag indicating whether the object&#39;s state-data record is &quot;Deleted&quot;.&quot;&quot;&quot;
        self._isDeleted = None

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelIsDirty( self ):
        &quot;&quot;&quot;Deletes the flag indicating whether the object&#39;s state-data record is &quot;dirty&quot; (in need of an update).&quot;&quot;&quot;
        self._isDirty = None

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelIsNew( self ):
        &quot;&quot;&quot;Deletes the flag indicating whether the object&#39;s state-data record is &quot;New&quot; (in need of creation).&quot;&quot;&quot;
        self._isNew = None

    ##################################
    # Class Properties               #
    ##################################

    DataSource = property( _GetDataSource, _SetDataSource, None, &#39;Gets or sets the data-source (an IsDataConnector instance) that the data-object will use to perform it\&#39;s queries against.&#39; )
    Id = property( _GetId, None, None, &#39;Gets the unique identifier of the object\&#39;s state-data record.&#39;)
    IsDeleted = property( _GetIsDeleted, _SetIsDeleted, None, &#39;Gets or sets the flag indicating that the object\&#39;s state-data record is pseudo-deleted.&#39; )
    IsDirty = property( _GetIsDirty, _SetIsDirty, None, &#39;Gets or sets the flag indicating that the object\&#39;s state-data record is &quot;dirty,&quot; and in need of an update.&#39; )
    IsNew = property( _GetIsNew, None, None, &#39;Gets or sets the flag indicating that the object\&#39;s state-data record is &quot;New,&quot; and needs to be created.&#39; )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __init__( self ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Set new and dirty flags before checking for instantiability, in order to 
        # avoid raising errors at destruction.
        self._isDirty = None
        self._isNew = None
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == BaseDataObject:
            raise NotImplementedError( &#39;BaseDataObject is (nominally) an abstract class, and is not intended to be instantiated.&#39; )
        self._DelDataSource()
        self._DelId()
        self.IsDeleted = False
        self.IsDirty = False
        self._isNew = True

    ##################################
    # Object Destructor              #
    ##################################

    def __del__( self ):
        &quot;&quot;&quot;Object destructor.

Saves the object&#39;s state-data before destruction.&quot;&quot;&quot;
        self.Save()

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _Create( self ):
        &quot;&quot;&quot;(Nominal abstract method) Creates a record in the back-end database for the object&#39;s state-data, updating the object&#39;s Id property with the identity of the created record.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s._Create has not been implemented as required by BaseDataObject.&#39; % ( self.__class__.__name__ ) )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def Delete( self ):
        &quot;&quot;&quot;(Nominal abstract method) Performs a physical record-deletion of a specific object&#39;s state-data record from the back-end database.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s.Delete has not been implemented as required by BaseDataObject.&#39; % ( self.__class__.__name__ ) )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;id&#39;, None, &#39;The unique identifier for the state-data record of the object to be retrieved from the database.&#39; )
    def Read( self, id ):
        &quot;&quot;&quot;(Nominal abstract method) Reads a specific object&#39;s state-data record from the back-end database, and returns an object-instance with the retrieved state-data.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s.Read has not been implemented as required by BaseDataObject.&#39; % ( self.__class__.__name__ ) )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def Save( self ):
        &quot;&quot;&quot;Creates or updates the object&#39;s state-data record based on whether the object&#39;s IsNew or IsDirty flag is set.&quot;&quot;&quot;
        if self._isNew:
            self._Create()
        elif self._isDirty:
            self._Update()
        self.IsDirty = False
        self._isNew = False

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _Update( self ):
        &quot;&quot;&quot;(Nominal abstract method) Updates the object&#39;s state-data record in the back-end database.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s._Update has not been implemented as required by BaseDataObject.&#39; % ( self.__class__.__name__ ) )

__all__ += [ &#39;BaseDataObject&#39; ]&lt;/pre&gt;

&lt;p&gt;Commentary&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;13-35&lt;/dt&gt;
    &lt;dd&gt;Typical property-getter structures.&lt;/dd&gt;

    &lt;dt&gt;42-49&lt;/dt&gt;
    &lt;dd&gt;Standard property-setter structure.&lt;/dd&gt;

    &lt;dt&gt;51-55&lt;/dt&gt;
    &lt;dd&gt;The property-setter for the &lt;code&gt;Id&lt;/code&gt; property is slightly different 
        than the typical pattern I&#39;ve shown in previous code, in that it just sets 
        the value, performing no type- or value-checking. &lt;code&gt;Id&lt;/code&gt; could, 
        arguably, be left abstract at the &lt;code&gt;BaseDataObject&lt;/code&gt; level, but 
        there doesn&#39;t seem to me to be any significant advantage to doing so - I&#39;d 
        rather have a single point of definition, and (if necessary) override the 
        setter method in derived classes to provide type- and/or value-checking 
        when or if necessary. I have a lingering suspicion that I&#39;ll regret that 
        design decision somewhere along the line, but for the time being, I&#39;ll 
        hold to it, and seee what happens.&lt;/dd&gt;

    &lt;dt&gt;58-63, 66-72, 75-80&lt;/dt&gt;
    &lt;dd&gt;Since the properties behind these setters are boolean values, there&#39;s no 
        real need to do a type-check them - a simple check of all of the allowed 
        values should suffice.
        &lt;dl&gt;
            &lt;dt&gt;71-73&lt;/dt&gt;
            &lt;dd&gt;Since setting the object&#39;s state to &quot;dirty&quot; should reset it&#39;s 
                &quot;new&quot; status, we&#39;re doing that here, but &lt;em&gt;only&lt;/em&gt; if the 
                &quot;dirty&quot; status is &lt;code&gt;True&lt;/code&gt; (or equivalent).&lt;/dd&gt;
        &lt;/dl&gt;
    &lt;/dd&gt;

    &lt;dt&gt;88-110&lt;/dt&gt;
    &lt;dd&gt;Typical property-deleter method-structure.&lt;/dd&gt;

    &lt;dt&gt;116-120&lt;/dt&gt;
    &lt;dd&gt;Typical property definitions.&lt;/dd&gt;

    &lt;dt&gt;146-150&lt;/dt&gt;
    &lt;dd&gt;By default, any object derived from &lt;code&gt;BaseDataObject&lt;/code&gt; should 
        save it&#39;s state before it is destroyed. Since the &lt;code&gt;_Create&lt;/code&gt; 
        and &lt;code&gt;_Update&lt;/code&gt; methods are still abstract (and thus need to be 
        defined by the derived classes), but still &lt;em&gt;exist&lt;/em&gt; at the 
        &lt;code&gt;BaseDataObject&lt;/code&gt; level, the destructor can take advantage of 
        the methods&#39; existence, define the &lt;code&gt;Save&lt;/code&gt; method, and call 
        it in the object destructor.&lt;/dd&gt;

    &lt;dt&gt;157-159, 162-164, 168-170, 183-185&lt;/dt&gt;
    &lt;dd&gt;The nominally-abstract methods for state-data-record creation, deletion, 
        read/retrieval and update, respectively. These must be abstract at this 
        level, because there is no way to anticipate exactly &lt;em&gt;how&lt;/em&gt; they 
        will work, but we know that they need to &lt;em&gt;exist&lt;/em&gt;.&lt;/dd&gt;

    &lt;dt&gt;173-180&lt;/dt&gt;
    &lt;dd&gt;The concrete method that creates or updates a state-data record for the 
        derived object, based on the object&#39;s &quot;dirty&quot; or &quot;new&quot; state.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;For the most part, the unit-tests are pretty much what you&#39;d expect, particularly 
if you&#39;ve been following this blog for any length of time:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class BaseDataObjectDerived( BaseDataObject ):
        def __init__( self ):
            BaseDataObject.__init__( self )
        def __del__( self ):
            pass
    
    class BaseDataObjectImplemented( BaseDataObject ):
        def __init__( self ):
            BaseDataObject.__init__( self )
        def __del__( self ):
            BaseDataObject.__del__( self )
        def _Create( self ):
            fp = open( &#39;/tmp/create&#39;, &#39;w&#39; )
            fp.write( &#39;_Create&#39; )
            fp.close()
        def _Update( self ):
            fp = open( &#39;/tmp/update&#39;, &#39;w&#39; )
            fp.write( &#39;_Update&#39; )
            fp.close()
    
    class testBaseDataObject( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the BaseDataObject class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            try:
                os.unlink( &#39;/tmp/create&#39; )
            except:
                pass
            try:
                os.unlink( &#39;/tmp/update&#39; )
            except:
                pass
        
        def testDerived( self ):
            &quot;&quot;&quot;Testing abstract nature of the BaseDataObject class.&quot;&quot;&quot;
            try:
                testObject = BaseDataObject()
                self.fail( &#39;BaseDataObject is nominally an abstract class, and should not be instantiable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to instantiate a BaseDataObject should raise NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = BaseDataObjectDerived()
            self.assertTrue( isinstance( testObject, BaseDataObject ), &#39;Objects derived from BaseDataObject should be instances of BaseDataObject.&#39; )

        def testDestruction( self ):
            &quot;&quot;&quot;Testing destruction of the BaseDataObject class.&quot;&quot;&quot;
            testObject = BaseDataObjectImplemented()
            del testObject
            try:
                fp = open( &#39;/tmp/create&#39;, &#39;r&#39; )
                fp.close()
            except IOError:
                self.fail( &#39;Destruction of a new object should result in it\&#39;s _Create method being called.&#39; )
            except Exception, error:
                self.fail( &#39;Destruction of a new object should result in it\&#39;s _Create method being called, but %s was raised&quot;\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = BaseDataObjectImplemented()
            testObject.IsDirty = True
            del testObject
            try:
                fp = open( &#39;/tmp/update&#39;, &#39;r&#39; )
                fp.close()
            except IOError:
                self.fail( &#39;Destruction of a modified object should result in it\&#39;s _Update method being called.&#39; )
            except Exception, error:
                self.fail( &#39;Destruction of a modified object should result in it\&#39;s _Update method being called, but %s was raised&quot;\n  %s&#39; % ( error.__class__.__name__, error ) )
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the BaseDataObject class.&quot;&quot;&quot;
            items = getMemberNames( BaseDataObject )[0]
            actual = len( items )
            expected = 5
            self.assertEquals( expected, actual, &#39;BaseDataObject is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the BaseDataObject class.&quot;&quot;&quot;
            items = getMemberNames( BaseDataObject )[1]
            actual = len( items )
            expected = 3
            self.assertEquals( expected, actual, &#39;BaseDataObject is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test properties

        def testDataSource( self ):
            &quot;&quot;&quot;Unit-tests the DataSource property of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            testValue = IsDataConnectorDerived()
            testObject = BaseDataObjectDerived()
            testObject.DataSource = testValue
            self.assertEquals( testObject.DataSource, testValue, &#39;Setting the DataSource should return the set value on a get.&#39; )
            badValues = [ None, True, 1, &#39;ook&#39; ]
            for testValue in badValues:
                try:
                    testObject = BaseDataObjectDerived()
                    testObject.DataSource = testValue
                    self.fail( &#39;Setting DataSource to a value of %s should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting DataSource to a value of %s should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            testObject = BaseDataObjectDerived()
            testValue = IsDataConnectorDerived()
            testValue2 = IsDataConnectorDerived()
            testObject.DataSource = testValue
            try:
                testObject.DataSource = testValue2
                self.fail( &#39;Changing an established DataSource of a BaseDataObject should not be allowed&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Changing an established DataSource of a BaseDataObject should raise NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testId( self ):
            &quot;&quot;&quot;Unit-tests the Id property of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            # There really isn&#39;t any useful test that can be done here without
            # mandating an ID type or structure, so this test really doesn&#39;t do
            # anything except get counted as a testable property.
            pass

        def testIsDeleted( self ):
            &quot;&quot;&quot;Unit-tests the IsDeleted property of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            testObject = BaseDataObjectDerived()
            goodValues = [ True, False, 1, 0 ]
            for testValue in goodValues:
                testObject.IsDeleted = testValue
                self.assertEquals( testObject.IsDeleted, testValue, &#39;Setting the IsDeleted value should returnt he same value when gotten.&#39; )
            badValues = [ None, 2, &#39;ook&#39;, -1, object() ]
            for testValue in badValues:
                try:
                    testObject.IsDeleted = testValue
                    self.fail( &#39;Setting IsDeleted to a value of %s should raise TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting IsDeleted to a value of %s should raise TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )

        def testIsDirty( self ):
            &quot;&quot;&quot;Unit-tests the IsDirty property of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            testObject = BaseDataObjectDerived()
            goodValues = [ True, False, 1, 0 ]
            for testValue in goodValues:
                testObject.IsDirty = testValue
                self.assertEquals( testObject.IsDirty, testValue, &#39;Setting the IsDirty value should returnt he same value when gotten.&#39; )
            badValues = [ None, 2, &#39;ook&#39;, -1, object() ]
            for testValue in badValues:
                try:
                    testObject.IsDirty = testValue
                    self.fail( &#39;Setting IsDirty to a value of %s should raise TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting IsDirty to a value of %s should raise TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )

        def testIsNew( self ):
            &quot;&quot;&quot;Unit-tests the IsNew property of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            testObject = BaseDataObjectDerived()
            self.assertEquals( testObject.IsNew, True )
#            self.assertEquals( testObject.IsNew, True, &#39;A default BaseDataObject-derived object should start with IsNew of True&#39; )

        # Test methods

        def testDelete( self ):
            &quot;&quot;&quot;Unit-tests the Delete method of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            testObject = BaseDataObjectDerived()
            try:
                testObject.Delete()
                self.fail( &#39;BaseDataObject should not implement Delete.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;BaseDataObject.Delete should raise NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testRead( self ):
            &quot;&quot;&quot;Unit-tests the Read method of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            testObject = BaseDataObjectDerived()
            try:
                testObject.Read( None )
                self.fail( &#39;BaseDataObject should not implement Read.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;BaseDataObject.Read should raise NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testSave( self ):
            &quot;&quot;&quot;Unit-tests the Save method of the BaseDataObject nominal abstract class.&quot;&quot;&quot;
            testObject = BaseDataObjectImplemented()
            testObject.Save()
            try:
                fp = open( &#39;/tmp/create&#39;, &#39;r&#39; )
                fp.close()
            except IOError:
                self.fail( &#39;Save called on a new object should result in it\&#39;s _Create method being called.&#39; )
            except Exception, error:
                self.fail( &#39;Save called on a new object should result in it\&#39;s _Create method being called, but %s was raised:\n  %s.&#39; % ( error.__class__.__name__, error ) )
            testObject.IsDirty = True
            testObject.Save()
            try:
                fp = open( &#39;/tmp/create&#39;, &#39;r&#39; )
                fp.close()
            except IOError:
                self.fail( &#39;Save called on an updated object should result in it\&#39;s _Create method being called.&#39; )
            except Exception, error:
                self.fail( &#39;Save called on an updated object should result in it\&#39;s _Create method being called, but %s was raised:\n  %s.&#39; % ( error.__class__.__name__, error ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testBaseDataObject ) )&lt;/pre&gt;

&lt;dl&gt;

    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;1-5, 7-19&lt;/dt&gt;
    &lt;dd&gt;Since there&#39;s a need to test both the default, unimplemented methods as 
        well as whether those methods get called as expected, there are actually 
        &lt;em&gt;two&lt;/em&gt; derived test-classes that are used. The first, 
        &lt;code&gt;BaseDataObjectDerived&lt;/code&gt; is used to make sure that any unimplemented 
        abstract methods raise the exceptions that they should (a key part of 
        making sure that the nominally-abstract class will respond with expected 
        abstract-like behavior). The second, &lt;code&gt;BaseDataObjectImplemented&lt;/code&gt;, 
        provides implementations for &lt;code&gt;_Create&lt;/code&gt; and &lt;code&gt;_Update&lt;/code&gt; 
        that write to files that the test-methods can access - these are a fair, 
        but low-weight representation of the basic &lt;em&gt;kind&lt;/em&gt; of data-persistence 
        process that would be implemented (storing data to some external source), 
        though in this case, it&#39;s file-based instead of being tied to a database 
        (which we&#39;d want to abstract away anyway).&lt;/dd&gt;

    &lt;dt&gt;27-35&lt;/dt&gt;
    &lt;dd&gt;Since some of the test-methods are writing files, and a failure in a 
        test-method could leave a written file that would generate a false positive 
        result in/for later tests, the &lt;code&gt;tearDown&lt;/code&gt; method is used to 
        make sure that if the expected files exist, they are deleted.&lt;/dd&gt;
    &lt;dd&gt;The potential for false positives in this test is particularly worrisome, 
        since it would be possible for a bad test-run one day to leave an undeleted 
        test-result file that another test-run could pick up minutes, hours, or 
        days later. The fact that the files in question are being generated in 
        &lt;code&gt;/tmp&lt;/code&gt; would potentially help, but not prevent this from 
        happening.&lt;/dd&gt;

    &lt;dt&gt;37-51&lt;/dt&gt;
    &lt;dd&gt;Fairly typical construction/derivation &lt;/dd&gt;

    &lt;dt&gt;49-69&lt;/dt&gt;
    &lt;dd&gt;Tests the destruction of a &lt;code&gt;BaseDataObject&lt;/code&gt;-derived class with 
        &lt;code&gt;_Create&lt;/code&gt; and &lt;code&gt;_Update&lt;/code&gt; implemented.
        &lt;dl&gt;
            &lt;dt&gt;51&lt;/dt&gt;
            &lt;dd&gt;Creates a new test-object, with an expected &lt;code&gt;IsNew&lt;/code&gt; of 
                &lt;code&gt;True&lt;/code&gt; and &lt;code&gt;IsDirty&lt;/code&gt; of &lt;code&gt;False&lt;/code&gt;. 
                This is the default state for such a derived object.&lt;/dd&gt;

            &lt;dt&gt;52&lt;/dt&gt;
            &lt;dd&gt;Deletes the newly-created object, which should fire the 
                &lt;code&gt;_Create&lt;/code&gt; method.&lt;/dd&gt;

            &lt;dt&gt;53-59&lt;/dt&gt;
            &lt;dd&gt;Checks for the existence of the expected file that 
                &lt;code&gt;_Create&lt;/code&gt; should create&lt;/dd&gt;

            &lt;dt&gt;60-69&lt;/dt&gt;
            &lt;dd&gt;With the exception of setting &lt;code&gt;IsDirty&lt;/code&gt; on the test-object 
                (which should cause the &lt;code&gt;_Update&lt;/code&gt; method to fire on 
                destruction) and the file expected, the tests for a &quot;dirty&quot; object 
        &lt;/dl&gt;
    &lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;With the exception of the unit-test for the &lt;code&gt;Id&lt;/code&gt; property, which 
really can&#39;t usefully test the property anyway, the property- and method tests 
are, I hope, what would be expected given the typical structure/pattern of such 
tests from previous posts.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/1791130880485369103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/1791130880485369103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/1791130880485369103'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/dataconnectors-module-part-3.html' title='The DataConnectors module (part 3)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-6193190698434238496</id><published>2011-12-07T04:30:00.000-08:00</published><updated>2011-12-07T04:30:00.676-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="list"/><category scheme="http://www.blogger.com/atom/ns#" term="strongly typed"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><category scheme="http://www.blogger.com/atom/ns#" term="test-driven development"/><category scheme="http://www.blogger.com/atom/ns#" term="TypedContainers"/><category scheme="http://www.blogger.com/atom/ns#" term="TypedList"/><title type='text'>Test-driven Development: Implementing TypedList (part 3)</title><content type='html'>&lt;p&gt;I was also going to undertake some research into the methods that 
&lt;code&gt;TypedList&lt;/code&gt; needed to override or otherwise implement in order to make 
it work the way it needs to. That research leads me to believe that there are a 
total of 8 methods that I need to be concerned with:
&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;__init__&lt;/code&gt;: the object constructor has to check any items 
        supplied to it for type-restrictions, but if there are no invalid types 
        provided, it can call &lt;code&gt;list.__init__&lt;/code&gt; and return whatever 
        comes back from that;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;__add__&lt;/code&gt;: called when concatenating two lists (in this case). 
        I have to assume that any items provided are suspect until proven otherwise. 
        I also couldn&#39;t determine how many times this method gets called in cases 
        of multiple additions;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;__iadd__&lt;/code&gt;: called when concatenating with &lt;code&gt;+=&lt;/code&gt;, 
        same assumptions as for &lt;code&gt;__add__&lt;/code&gt;;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;__setitem__&lt;/code&gt;: called when assigning a value to a specific 
        position within the list. The value is treated as a non-iterable (e.g., 
        passing a list results in a list in the position of the original list);&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;__setslice__&lt;/code&gt;: Called when assigning values to a slice, items 
        being set need to be type-checked;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;append&lt;/code&gt;: Called when appending values to the list, items 
        added need to be type-checked;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;insert&lt;/code&gt;: Called when inserting a value to a specific position 
        in the list, the value needs to be type-checked;&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;extend&lt;/code&gt;: Called to extend items to the end of the list, same 
        drill;&lt;/li&gt;
&lt;/ul&gt;
This list of methods was mostly generated by looking at the documentation-sources 
noted in the last post, but since I wasn&#39;t sure exactly what some of the operational 
parameters for them were (whether they could handle single items only, iterable 
items, etc., and how they would deal with unexpected types), I also hacked up a 
quick little test-script to provide some visibility into what exactly happened 
when various operations and list-methods were called:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class MyList( list, object ):

    def __init__( self, iterable=None ):
        print &quot;MyList.__init__( %s )&quot; % ( iterable )
        list.__init__( self, iterable )
    def __add__( self, items ):
        print &quot;MyList.__add__( %s )&quot; % ( items )
        return list.__add__( self, items )
    def __iadd__( self, items ):
        print &quot;MyList.__iadd__( %s )&quot; % ( items )
        return list.__iadd__( self, items )
    def __setitem__( self, index, item ):
        print &quot;MyList.__setitem__( %s, %s )&quot; % ( index, item )
        return list.__setitem__( self, index, item )
    def __setslice__( self, start, end, items ):
        print &quot;MyList.__setslice__( %s, %s, %s )&quot; % ( start, end, items )
        return list.__setslice__( self, start, end, items )
    def append( self, item ):
        print &quot;MyList.append( %s )&quot; % ( item )
        return list.append( self, item )
    def insert( self, index, item ):
        print &quot;MyList.insert( %s, %s )&quot; % ( index, item )
        return list.insert( self, index, item )
    def extend( self, items ):
        print &quot;MyList.extend( %s )&quot; % ( items )
        return list.extend( self, items )

tester = MyList( [&#39;eek&#39;] )                # uses __init__
tester += [ &#39;ook&#39; ]                       # uses __iadd__
tester.append( &#39;eek&#39; )                    # uses append
tester.insert( 1, &#39;OOOOK!&#39; )              # uses insert
tester[1:2] = [ &#39;spam&#39;, &#39;eggs&#39; ]          # uses __setslice__
tester += [ &#39;oooo&#39;, &#39;ooooo!&#39; ]            # uses __iadd__
tester[2] = &#39;bork!&#39;                       # uses __setitem__
tester.extend( [ &#39;eeee&#39;, &#39;eeee&#39; ] )       # uses extend
tester = tester + [ &#39;mooo!&#39; ] + [ &#39;me&#39; ]  # uses __add__
print
print tester&lt;/pre&gt;

&lt;p&gt;This little script provided a lot of insight, actually - Though I&#39;d seen some 
of the documentation for &lt;code&gt;__add__&lt;/code&gt; and &lt;code&gt;__iadd__&lt;/code&gt;, for 
example, it never really stuck in my head that these were the methods that were 
called when &lt;code&gt;+&lt;/code&gt; and &lt;code&gt;+=&lt;/code&gt; operators were used.&lt;/p&gt;

&lt;p&gt;So, with our unit-tests set, and a solid feel for what the code needed to 
override from &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;TypedList&lt;/code&gt; was pretty easily 
written:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;TypedList&lt;/b&gt; (Nominal final class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class TypedList( BaseTypedCollection, list ):
    &quot;&quot;&quot;Represents a strongly-typed List.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;memberTypes&#39;, None, &#39;(Iterable, required) An iterable collection of item-types to be allowed as members in the list.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;iterable&#39;, None, &#39;(Iterable, required) An iterable collection of item-values to populate the TypedList with at creation.&#39; )
    def __init__( self, memberTypes, iterable=None ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != TypedList:
            raise NotImplementedError( &#39;TypedList is (nominally) a final class, and is not intended to be derived from.&#39; )
        self._SetMemberTypes( memberTypes )
        list.__init__( self )
        if iterable != None:
            for item in iterable:
                self.append( item )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    ##################################
    # Methods overridden from list   #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __add__( self, items ):
        &quot;&quot;&quot;Called when adding items to the list with a &quot;+&quot; (e.g., TL + items).&quot;&quot;&quot;
        if not isinstance( items, list ):
            raise TypeError( &#39;TypedList.__add__ expects a list (or list-derived) instance of values.&#39; % ( self._memberTypes ) )
        for item in items:
            if not self.IsValidMemberType( item ):
                raise TypeError( &#39;TypedList.__add__ expects a list (or list-derived) instance of values of any of %s. %s is not valid.&#39; % ( self._memberTypes, items ) )
        return list.__add__( self, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __iadd__( self, items ):
        &quot;&quot;&quot;Called when adding items to the list with a &quot;+=&quot; (e.g., TL += item).&quot;&quot;&quot;
        if not isinstance( items, list ):
            raise TypeError( &#39;TypedList.__iadd__ expects a list (or list-derived) instance of values.&#39; % ( self._memberTypes ) )
        for item in items:
            if not self.IsValidMemberType( item ):
                raise TypeError( &#39;TypedList.__iadd__ expects a list (or list-derived) instance of values of any of %s. %s is not valid.&#39; % ( self._memberTypes, items ) )
        return list.__iadd__( self, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __setitem__( self, index, item ):
        &quot;&quot;&quot;Called when setting the value of a specified location in the list (TL[n] = item).&quot;&quot;&quot;
        if not self.IsValidMemberType( item ):
            raise TypeError( &#39;TypedList.__setitem__ expects an instance of any of %s.&#39; % ( self._memberTypes ) )
        return list.__setitem__( self, index, item )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __setslice__( self, start, end, items ):
        &quot;&quot;&quot;Called when setting the value(s) of a slice-location in the list (TL[1:2] = item).&quot;&quot;&quot;
        for item in items:
            if not self.IsValidMemberType( item ):
                raise TypeError( &#39;TypedList.__setslice__ expects an iterable of instances of any of %s.&#39; % ( self._memberTypes ) )
        return list.__setslice__( self, start, end, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def append( self, item ):
        &quot;&quot;&quot;Called when appending items to the list.&quot;&quot;&quot;
        if not self.IsValidMemberType( item ):
            raise TypeError( &#39;TypedList.append expects an instance of any of %s.&#39; % ( self._memberTypes ) )
        return list.append( self, item )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def extend( self, items ):
        &quot;&quot;&quot;Called when extending items to a the end of the list.&quot;&quot;&quot;
        if not isinstance( items, list ):
            raise TypeError( &#39;TypedList.extend expects a list (or list-derived) instance of values.&#39; % ( self._memberTypes ) )
        for item in items:
            if not self.IsValidMemberType( item ):
                raise TypeError( &#39;TypedList.extend expects a list (or list-derived) instance of values of any of %s. %s is not valid.&#39; % ( self._memberTypes, items ) )
        return list.extend( self, items )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def insert( self, index, item ):
        &quot;&quot;&quot;Called when inserting items to a specified location in the list (TL[1] = item).&quot;&quot;&quot;
        if not self.IsValidMemberType( item ):
            raise TypeError( &#39;TypedList.insert expects an instance of any of %s.&#39; % ( self._memberTypes ) )
        return list.insert( self, index, item )

__all__ += [ &#39;TypedList&#39; ]&lt;/pre&gt;

&lt;p&gt;Ultimately, as with &lt;code&gt;TypedDictionary&lt;/code&gt; earlier, there&#39;s not a huge 
amount of code to be written. Python&#39;s built-ins and the methods specific to 
adding items to or replacing items in a list were the only methods that had to 
be overridden, and there were only two patterns for those modifications:
    &lt;ul&gt;
        &lt;li&gt;If the method expected a list-type (e.g., &lt;code&gt;__add__&lt;/code&gt;, 
            &lt;code&gt;__iadd__&lt;/code&gt;, &lt;code&gt;__setslice__&lt;/code&gt; and &lt;code&gt;extend&lt;/code&gt;):
            &lt;ul&gt;
                &lt;li&gt;The supplied items are checked to make sure that they are 
                    derived from a list. This allows list-types and extensions 
                    of list types (like &lt;code&gt;Typedlist&lt;/code&gt;) to be used.&lt;/li&gt;
                &lt;li&gt;Once the basic supplied type is successfully checked, the 
                    items &lt;em&gt;in&lt;/em&gt; it have to be individually type-checked 
                    (using &lt;code&gt;IsValidMemberType&lt;/code&gt;), and if any of them 
                    are invalid, raise an error.&lt;/li&gt;
                &lt;li&gt;If everything is good, then call (and return) the parent 
                    list-class method with the supplied value(s).&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/li&gt;
        &lt;li&gt;If the method expects a non-list instance value, then all that need 
            be done is type-check the value (again, with &lt;code&gt;IsValidMemberType&lt;/code&gt;):
            &lt;ul&gt;
                &lt;li&gt;If it&#39;s invalid, raise a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/li&gt;
                &lt;li&gt;Otherwise, call (and return) the parent list-class method 
                    with the supplied value.&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/li&gt;
    &lt;/ul&gt;
&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;34-35&lt;/dt&gt;
    &lt;dd&gt;Treat &lt;code&gt;TypedList&lt;/code&gt; as a final class, and don&#39;t allow extension.&lt;/dd&gt;

    &lt;dt&gt;36&lt;/dt&gt;
    &lt;dd&gt;Set the member-types allowed by the instance.&lt;/dd&gt;

    &lt;dt&gt;37&lt;/dt&gt;
    &lt;dd&gt;Makes sure that the instance calls a basic list-constructor. We can (and 
        should) ignore any supplied instance-values in the &lt;code&gt;iterable&lt;/code&gt; 
        argument when calling the parent constructor, though, because we want to 
        use the overridden &lt;code&gt;append&lt;/code&gt; method to build out the instance&#39;s 
        list of values.&lt;/dd&gt;

    &lt;dt&gt;38-40&lt;/dt&gt;
    &lt;dd&gt;Checks to see if the constructor is being passed an iterable collection 
        of values to populate the instance with, loops over them if they are 
        supplied, and appends them to the instance&#39;s values one by one.&lt;/dd&gt;
    &lt;dd&gt;I could&#39;ve just as easily looped over any supplied &lt;code&gt;iterable&lt;/code&gt; 
        values in the constructor, raising an error at the first bad type supplied, 
        and then called the parent constructor &lt;em&gt;with&lt;/em&gt; the &lt;code&gt;iterable&lt;/code&gt; 
        value. I have a gut-level feeling, however, that taking that approach 
        could raise issues in the future, and since I&#39;d&#39;ve had to iterate over 
        the values anyway, it just seemed safer to implement the way I did. I 
        can&#39;t justify it, but I&#39;m going to trust my instincts.&lt;/dd&gt;

    &lt;dt&gt;55-62, 65-72, 82-87, 97-104&lt;/dt&gt;
    &lt;dd&gt;Uses the list-checking pattern noted above to override &lt;code&gt;__add__&lt;/code&gt;, 
        &lt;code&gt;__iadd__&lt;/code&gt;, &lt;code&gt;__setslice__&lt;/code&gt; and &lt;code&gt;extend&lt;/code&gt;, 
        respectively.&lt;/dd&gt;

    &lt;dt&gt;75-81, 90-94, 107-111&lt;/dt&gt;
    &lt;dd&gt;Uses the value-checking pattern noted above to override &lt;code&gt;__setitem__&lt;/code&gt;, 
        &lt;code&gt;append&lt;/code&gt; and &lt;code&gt;insert&lt;/code&gt;, respectively.&lt;/dd&gt;
&lt;/dl&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/6193190698434238496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/test-driven-development-implementing_07.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6193190698434238496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6193190698434238496'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/test-driven-development-implementing_07.html' title='Test-driven Development: Implementing TypedList (part 3)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-7880143457493088243</id><published>2011-12-05T04:30:00.000-08:00</published><updated>2011-12-05T04:30:03.863-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="list"/><category scheme="http://www.blogger.com/atom/ns#" term="strongly typed"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><category scheme="http://www.blogger.com/atom/ns#" term="test-driven development"/><category scheme="http://www.blogger.com/atom/ns#" term="TypedContainers"/><category scheme="http://www.blogger.com/atom/ns#" term="TypedList"/><title type='text'>Test-driven Development: Implementing TypedList (part 2)</title><content type='html'>&lt;p&gt;The next step, if we approach this in a TDD fashion, is probably to define our 
unit-tests. Technically, this step &lt;em&gt;probably&lt;/em&gt; should&#39;ve been undertaken 
earlier, perhaps even before starting to track down the methods that we&#39;d need 
to override, but since I expected that there were going to be a lot of them (or 
at least a lot to evaluate), I wanted to get that started fairly early. The 
unit-tests need to encompass all of the expected uses that we can put a 
&lt;code&gt;TypedList&lt;/code&gt; to, and need to include both passing and failing (negative) 
tests in order to assure that they&#39;re covering the code sufficiently.&lt;/p&gt;

&lt;p&gt;At the minimum, that means that we need to test each of the following cases:
&lt;ul&gt;
    &lt;li&gt;Instantiation of a &lt;code&gt;TypedList&lt;/code&gt; with values presented to the 
        constructor;&lt;/li&gt;
    &lt;li&gt;Appending items to a &lt;code&gt;TypedList&lt;/code&gt; with &lt;code&gt;+&lt;/code&gt;;&lt;/li&gt;
    &lt;li&gt;Appending items to a &lt;code&gt;TypedList&lt;/code&gt; with &lt;code&gt;+=&lt;/code&gt;;&lt;/li&gt;
    &lt;li&gt;Appending items to a &lt;code&gt;TypedList&lt;/code&gt; with &lt;code&gt;append&lt;/code&gt;;&lt;/li&gt;
    &lt;li&gt;Appending (or extending) items to a &lt;code&gt;TypedList&lt;/code&gt; with 
        &lt;code&gt;extend&lt;/code&gt;;&lt;/li&gt;
    &lt;li&gt;Setting the value in a specific &lt;code&gt;TypedList&lt;/code&gt; location with 
        &lt;code&gt;TypedList[n] = value&lt;/code&gt;;&lt;/li&gt;
    &lt;li&gt;Setting one or more values in a slice of a &lt;code&gt;TypedList&lt;/code&gt; with 
        &lt;code&gt;TypedList[i:j] = value&lt;/code&gt;;&lt;/li&gt;
    &lt;li&gt;Inserting a value into a specific &lt;code&gt;TypedList&lt;/code&gt; location with 
        &lt;code&gt;insert&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given that we need to test all of these both positively (to prove that basic list 
behavior for allowed types is preserved) and negatively (to raise errors when they 
are presented with an invalid type), there are a &lt;em&gt;lot&lt;/em&gt; of assertions that 
have to be made in the &lt;code&gt;testListBehavior&lt;/code&gt; test-method. Most of the rest 
of the test-methods are pretty typical of the pattern I&#39;ve followed thus far, so 
I won&#39;t spend a lot of time commenting on those.&lt;/p&gt;

&lt;p&gt;Unit-tests&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class TypedListDerived( TypedList ):
        def __init__( self, memberTypes, iterable=None ):
            TypedList.__init__( self, memberTypes, iterable )
    
    class testTypedList( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the TypedList class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass

        def testFinal( self ):
            &quot;&quot;&quot;Testing final nature of the TypedList class.&quot;&quot;&quot;
            try:
                testObject = TypedListDerived( [ types.IntType ] )
                self.fail( &#39;TypedList is nominally a final class, and is not intended to be extended.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;TypedList is expected to raise NotImplementedError if a derived class is instantiated, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = TypedList( [ types.IntType ] )
            self.assertEquals( testObject, [], &#39;A Typedlist constructed with no values should equal an empty list.&#39; )

        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the TypedList class.&quot;&quot;&quot;
            items = getMemberNames( TypedList )[0]
            actual = len( items )
            expected = 1
            self.assertEquals( expected, actual, &#39;TypedList is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the TypedList class.&quot;&quot;&quot;
            items = getMemberNames( TypedList )[1]
            actual = len( items )
            expected = 4
            self.assertEquals( expected, actual, &#39;TypedList is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test list behavior
        def testListBehavior( self ):
            &quot;&quot;&quot;Unit-tests list-like behavior of TypedList.&quot;&quot;&quot;
            # Test good values - all should pass
            goodValues = [ -1, 0, 1, 2, -1L, 0L, 1L, 2L, -1.0, 0.0, 1.0, 2.0 ]
            # Test creation with an iterable
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ], goodValues )
            self.assertEquals( testObject, goodValues, &#39;Values set in a TypedList at creation should act like values set in a normal list at creation.&#39; )
            # Test L = L + i structure
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ] )
            compareList = []
            for testValue in goodValues:
                compareList = compareList + [ testValue ]
                testObject = testObject + [ testValue ]
                self.assertEquals( testObject, compareList, &#39;Values added to a TypedList should act like values added to a normal list.&#39; )
            # Test L = L += i structure
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ] )
            compareList = []
            for testValue in goodValues:
                compareList += [ testValue ]
                testObject += [ testValue ]
                self.assertEquals( testObject, compareList, &#39;Values added to a TypedList should act like values added to a normal list.&#39; )
            # Test L.append equivalent
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ] )
            compareList = []
            for testValue in goodValues:
                compareList.append( testValue )
                testObject.append( testValue )
                self.assertEquals( testObject, compareList, &#39;Values appended to a TypedList should act like values appended to a normal list.&#39; )
            # Test L.extend equivalent
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ] )
            compareList = []
            for testValue in goodValues:
                compareList.extend( [ testValue ] )
                testObject.extend( [ testValue ] )
                self.assertEquals( testObject, compareList, &#39;Values extended to a TypedList should act like values extended to a normal list.&#39; )
            # Test L.insert equivalent
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ] )
            compareList = []
            for testValue in goodValues:
                compareList.insert( 0, testValue )
                testObject.insert( 0, testValue )
                self.assertEquals( testObject, compareList, &#39;Values inserted to a TypedList should act like values inserted to a normal list.&#39; )
            # Test L[index] = value equivalent
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ], [ 0 ] )
            compareList = [ 0 ]
            for testValue in goodValues:
                compareList[0] = testValue
                testObject[0] = testValue
                self.assertEquals( testObject, compareList, &#39;Values set in a position in a TypedList should act like values set in a position in a normal list.&#39; )
            # Test L[slice] = [ i, j ] equivalent
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ], [ 0, 0, 0 ] )
            compareList = [ 0, 0, 0 ]
            goodValues = [ [ -1, 0 ], [ 1, 2 ] ]
            for testValue in goodValues:
                compareList[0:1] = testValue
                testObject[0:1] = testValue
                self.assertEquals( testObject, compareList, &#39;Values set in a slice position in a TypedList should act like values set in a slice position in a normal list.&#39; )
            # Test bad values - all should raise errors
            badValues = [ None, &#39;string&#39;, [ &#39;ook&#39; ] ]
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ] )
            # Test L = L + i structure
            for testValue in badValues:
                try:
                    testObject = testObject + testValue
                    self.fail( &#39;Adding %s with &quot;+&quot; to a TypedList that doesn\&#39;t allow that type should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Adding %s with &quot;+&quot; to a TypedList that doesn\&#39;t allow that type should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            # Test L = L += i structure
            for testValue in badValues:
                try:
                    testObject += testValue
                    self.fail( &#39;Adding %s with &quot;+=&quot; to a TypedList that doesn\&#39;t allow that type should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Adding %s with &quot;+=&quot; to a TypedList that doesn\&#39;t allow that type should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            # Test L.append equivalent
            for testValue in badValues:
                try:
                    testObject.append( testValue )
                    self.fail( &#39;Appending %s to a TypedList that doesn\&#39;t allow that type should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Appending %s to a TypedList that doesn\&#39;t allow that type should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            # Test L.extend equivalent
            for testValue in badValues:
                try:
                    testObject.extend( [ testValue ] )
                    self.fail( &#39;Extending %s to a TypedList that doesn\&#39;t allow that type should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Extending %s to a TypedList that doesn\&#39;t allow that type should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            # Test L.insert equivalent
            for testValue in badValues:
                try:
                    testObject.insert( 0, testValue )
                    self.fail( &#39;Inserting %s to a TypedList that doesn\&#39;t allow that type should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Inserting %s to a TypedList that doesn\&#39;t allow that type should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            # Test L[index] = value equivalent
            for testValue in badValues:
                try:
                    testObject[ 0 ] = testValue
                    self.fail( &#39;Setting %s to a position in a TypedList that doesn\&#39;t allow that type should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting %s to a position in a TypedList that doesn\&#39;t allow that type should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )
            # Test L[slice] = [ i, j ] equivalent
            badValues = [ [ None, &#39;string&#39; ], [ &#39;string&#39;, None ] ]
            testObject = TypedList( [ types.IntType, types.LongType, types.FloatType ], [ 0, 0, 0 ] )
            for testValue in badValues:
                try:
                    testObject[ 0:1 ] = testValue
                    self.fail( &#39;Setting %s to a slice-position in a TypedList that doesn\&#39;t allow that type should raise a TypeError&#39; % ( testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting %s to a slice-position in a TypedList that doesn\&#39;t allow that type should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( testValue, error.__class__.__name__, error ) )

        # Test Properties
        def testMemberTypes( self ):
            &quot;&quot;&quot;Unit-tests the MemberTypes property of the TypedList class.&quot;&quot;&quot;
            self.assertEquals( TypedList.MemberTypes, BaseTypedCollection.MemberTypes, &#39;TypedList should inherit the MemberTypes property of BaseTypedCollection.&#39; )

        # Test Methods

        def testappend( self ):
            &quot;&quot;&quot;Unit-tests the append method of the TypedList class.&quot;&quot;&quot;
            pass # This is tested in testListBehavior, above.

        def testextend( self ):
            &quot;&quot;&quot;Unit-tests the extend method of the TypedList class.&quot;&quot;&quot;
            pass

        def testinsert( self ):
            &quot;&quot;&quot;Unit-tests the insert method of the TypedList class.&quot;&quot;&quot;
            pass # This is tested in testListBehavior, above.

        def testIsValidMemberType( self ):
            &quot;&quot;&quot;Unit-tests the IsValidMemberType method of the TypedList class.&quot;&quot;&quot;
            self.assertEquals( TypedList.IsValidMemberType, BaseTypedCollection.IsValidMemberType, &#39;TypedList should inherit the IsValidMemberType method of BaseTypedCollection.&#39; )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testTypedList ) )&lt;/pre&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;48&lt;/dt&gt;
    &lt;dd&gt;Most of these tests will need a simple baseline list to work with, or to 
        draw test-values from. The &lt;code&gt;TypedList&lt;/code&gt; instance I&#39;m going to 
        work with will allow integers, floats amd long integers, so all of the 
        &quot;good&quot; values must fall into one of those types. I also want to make sure 
        that it handles positive, zero and negative values correctly, and it never 
        hurts to test for even/odd differences either, which is why there&#39;s a 
        value of 2 of each of those types as well.&lt;/dd&gt;

    &lt;dt&gt;50, 53, 60, 67, etc.&lt;/dt&gt;
    &lt;dd&gt;In order to make sure that each test has a fresh, clean value, I&#39;m creating 
        a new &lt;code&gt;TypedList&lt;/code&gt; instance each time.&lt;/dd&gt;

    &lt;dt&gt;54, 61, 68, etc.&lt;/dt&gt;
    &lt;dd&gt;All of the &quot;good&quot; value tests are based around the concept that if we 
        perform the same actions/operations (with the same value[s]) on a 
        &lt;code&gt;TypedList&lt;/code&gt; instance as we do on an equivalent &lt;code&gt;list&lt;/code&gt; 
        instance, the end results will be identical. If that proves not to be the 
        case, then the code has broken core list-functionality. Again, in order 
        to assure that there&#39;s a fresh comparison-list copy each time, a new list 
        is created.&lt;/dd&gt;

    &lt;dt&gt;50, 51&lt;/dt&gt;
    &lt;dd&gt;Standard lists can be initialized with an iterable set of values, so a 
        &lt;code&gt;TypedList&lt;/code&gt; needs to be able to do that as well.&lt;/dd&gt;

    &lt;dt&gt;55-58&lt;/dt&gt;
    &lt;dd&gt;Tests the &quot;+&quot; operator as it applies to &lt;code&gt;TypedList&lt;/code&gt;s&lt;/dd&gt;

    &lt;dt&gt;62-65&lt;/dt&gt;
    &lt;dd&gt;Tests the &quot;+=&quot; operator as it applies to &lt;code&gt;TypedList&lt;/code&gt;s&lt;/dd&gt;

    &lt;dt&gt;69-72&lt;/dt&gt;
    &lt;dd&gt;Tests the &lt;code&gt;append&lt;/code&gt; method - this could be broken out into the 
        dedicated test-method created specifically for it (&lt;code&gt;testappend&lt;/code&gt;), 
        but it feels better to keep this test-code here along with the &lt;em&gt;other&lt;/em&gt; 
        list-behavior tests.&lt;/dd&gt;

    &lt;dt&gt;76-79&lt;/dt&gt;
    &lt;dd&gt;Tests the &lt;code&gt;extend&lt;/code&gt; method - this could be broken out into the 
        dedicated test-method created specifically for it (&lt;code&gt;testextend&lt;/code&gt;), 
        but it feels better to keep this test-code here along with the &lt;em&gt;other&lt;/em&gt; 
        list-behavior tests.&lt;/dd&gt;

    &lt;dt&gt;83-86&lt;/dt&gt;
    &lt;dd&gt;Tests the &lt;code&gt;insert&lt;/code&gt; method - this could be broken out into the 
        dedicated test-method created specifically for it (&lt;code&gt;testinsert&lt;/code&gt;), 
        but it feels better to keep this test-code here along with the &lt;em&gt;other&lt;/em&gt; 
        list-behavior tests.&lt;/dd&gt;

    &lt;dt&gt;90-93&lt;/dt&gt;
    &lt;dd&gt;Tests the ability to set a value at a specific index-location in the 
        &lt;code&gt;TypedList&lt;/code&gt;.&lt;/dd&gt;

    &lt;dt&gt;97&lt;/dt&gt;
    &lt;dd&gt;For the slice-value test, we need a slightly different good-values structure, 
        so we reset it here before proceeding.&lt;/dd&gt;

    &lt;dt&gt;98-101&lt;/dt&gt;
    &lt;dd&gt;Tests the ability to set values to a slice-location in the 
        &lt;code&gt;TypedList&lt;/code&gt;.&lt;/dd&gt;

    &lt;dt&gt;102-169&lt;/dt&gt;
    &lt;dd&gt;These are the negative tests for the same &lt;code&gt;TypedList&lt;/code&gt; functionality 
        and capabilities that have been positively tested in the previous code. 
        They are somewhat simpler, in that there is no comparison that needs to 
        be made, though it might not be a bad idea to check at each iteration 
        that the values in the underlying list haven&#39;t changed, but it doesn&#39;t 
        feel necessary at this point - if they do, that feels to me like there&#39;s 
        something greviously wrong at a level in the language itself that I won&#39;t 
        be able to fix anyway.&lt;/dd&gt;

&lt;/dl&gt;

&lt;p&gt;That&#39;s a fair amount of code for today, even if it is all unit-tests, so I&#39;ll 
stop here, and finish up with the &quot;real&quot; development in my next post. Bear in 
mind that we &lt;em&gt;still&lt;/em&gt; don&#39;t have the actual &lt;code&gt;TypedList&lt;/code&gt; 
functionality built at all yet - and that the unit-tests will fail, because that 
functionality doesn&#39;t exist. At this point in time, &lt;em&gt;that&#39;s OK&lt;/em&gt; - when 
all of the &quot;real&quot; code is writtem &lt;em&gt;and&lt;/em&gt; all of our tests pass, we&#39;re done! 
That ought to go pretty quickly, though.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/7880143457493088243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/test-driven-development-implementing_05.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/7880143457493088243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/7880143457493088243'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/test-driven-development-implementing_05.html' title='Test-driven Development: Implementing TypedList (part 2)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-1024778405445585046</id><published>2011-12-02T04:30:00.001-08:00</published><updated>2011-12-02T04:30:02.363-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="list"/><category scheme="http://www.blogger.com/atom/ns#" term="strongly typed"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><category scheme="http://www.blogger.com/atom/ns#" term="test-driven development"/><category scheme="http://www.blogger.com/atom/ns#" term="TypedContainers"/><category scheme="http://www.blogger.com/atom/ns#" term="TypedList"/><title type='text'>Test-driven Development: Implementing TypedList (part 1)</title><content type='html'>&lt;p&gt;As I&#39;ve been contemplating the next steps in the &lt;code&gt;DataConnectors&lt;/code&gt; 
module, I&#39;ve been waffling back and forth on how best to implement certain aspects 
of it. That&#39;s usually a sign, for me at least, that I need to take a step back and 
let things percolate for a while. I mentioned that I&#39;d want/need to implement the 
&lt;code&gt;TypedList&lt;/code&gt; before I got to the concrete classes of &lt;code&gt;DataConnectors&lt;/code&gt; 
anyway, so:&lt;/p&gt;

&lt;p&gt;In today&#39;s post, I&#39;m going to tackle the creation of the &lt;code&gt;TypedList&lt;/code&gt; 
class in the &lt;code&gt;TypedContainers&lt;/code&gt; module. The shared code can be found at 
&lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/TypedContainers.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/TypedContainers.py&lt;/a&gt;, 
as noted when I posted the &lt;code&gt;TypedDictionary&lt;/code&gt; some time back. I&#39;m going 
to approach this from a slightly different angle, though - I&#39;m going to start with 
as little as I can on the class-definition, build out the unit-tests that it needs 
in order to do what it needs to do, and write code until the tests pass. If this 
sounds familiar to you, then you&#39;re probably already using &lt;a href=&quot;http://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;Test-Driven 
Development&lt;/a&gt; (TDD).&lt;/p&gt;

&lt;p&gt;Part of the reason I&#39;m taking this approach is because the standard unit-test 
structure that I use (specifically the &lt;code&gt;testPropertyCountAndTests&lt;/code&gt; and 
&lt;code&gt;testMethodCountAndTests&lt;/code&gt; test-methods that&#39;ve been part of all of the 
recent test-code I&#39;ve shown make the discovery of the underlying methods and their 
signatures fairly easy, though it can also be a bit on the tedious side when 
there&#39;s a significant number of properties or methods to be discovered. Another 
option, and one that might be considerably faster, is to just build a stub-class 
extending the base that we&#39;re starting with (a &lt;code&gt;list&lt;/code&gt; in this case), 
then take a look at it&#39;s members with &lt;code&gt;dir()&lt;/code&gt; or with &lt;code&gt;pydoc&lt;/code&gt;. 
Use whatever works best, or is easiest for you.&lt;/p&gt;

&lt;p&gt;So, to start with, we&#39;ll define the &lt;code&gt;TypedList&lt;/code&gt; class with as little 
to it as we can manage. That means that it must inherit from the Python &lt;code&gt;list&lt;/code&gt;, 
and, since we&#39;re going to be using the &lt;code&gt;MemberTypes&lt;/code&gt; functionality 
provided by &lt;code&gt;BaseTypedCollection&lt;/code&gt;, it needs to inherit from that as 
well. The bare-bones starting-point, then looks something like this:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class TypedList( BaseTypedCollection, list ):
    &quot;&quot;&quot;Represents a strongly-typed List.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;memberTypes&#39;, None, &#39;(Iterable, required) An iterable collection of item-types to be allowed as members in the list.&#39; )
    def __init__( self, memberTypes ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != TypedList:
            raise NotImplementedError( &#39;TypedList is (nominally) a final class, and is not intended to be derived from.&#39; )
        self._SetMemberTypes( memberTypes )
        raise NotImplementedError( &#39;TypedList is not implemented yet.&#39; )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

__all__ += [ &#39;TypedList&#39; ]&lt;/pre&gt;

&lt;p&gt;Next, and more importantly, I&#39;m going to generate the unit-tests for the Typedlist class. I&#39;m going to start with &lt;em&gt;only&lt;/em&gt; my standard unit-test stub-code, in order to show how it can be used to reveal the underlying properties and methods of other objects. It&#39;s starting-point, then is very simple:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class TypedListDerived( TypedList ):
        def __init__( self ):
            pass
    
    class testTypedList( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the TypedList class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
#        def testDerived( self ):
#            &quot;&quot;&quot;Testing abstract nature of the TypedList class.&quot;&quot;&quot;
#            pass
    
#        def testFinal( self ):
#            &quot;&quot;&quot;Testing final nature of the TypedList class.&quot;&quot;&quot;
#            pass
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the TypedList class.&quot;&quot;&quot;
            pass
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the TypedList class.&quot;&quot;&quot;
            items = getMemberNames( TypedList )[0]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;TypedList is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the TypedList class.&quot;&quot;&quot;
            items = getMemberNames( TypedList )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;TypedList is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testTypedList ) )&lt;/pre&gt;

&lt;p&gt;With that basic test-stub in place, I&#39;m going to run my module, and see what 
the unit-tests reveal (I&#39;ll trim the detailed code-dumps, though, and just show 
the results):

&lt;pre&gt;Running unit-tests
###############################################################################
Unit-tests failed (see items below)
###############################################################################
Failures
#-----------------------------------------------------------------------------#
AssertionError: TypedList is expected to have 0 methods to test, but 1 were 
dicovered by inspection.
#-----------------------------------------------------------------------------#
AssertionError: TypedList is expected to have 0 properties to test, but 1 were 
dicovered by inspection.
#-----------------------------------------------------------------------------#
###############################################################################&lt;/pre&gt;

So, immediately, we can see that there&#39;s at least one property and one method 
that need to be added to our test-cases. My next step is to see what those items 
are, and in order to do that, I&#39;ll change the &lt;code&gt;expected&lt;/code&gt; values in 
&lt;code&gt;testPropertyCountAndTests&lt;/code&gt; and &lt;code&gt;testMethodCountAndTests&lt;/code&gt; 
to match what the test-output results are expecting, then re-run the tests:

&lt;pre&gt;Running unit-tests
###############################################################################
Unit-tests failed (see items below)
###############################################################################
Failures
#-----------------------------------------------------------------------------#
AssertionError: There should be a test for the IsValidMemberType method 
(testIsValidMemberType), but none was identifiable.
#-----------------------------------------------------------------------------#
AssertionError: There should be a test for the MemberTypes property 
(testMemberTypes), but none was identifiable.
#-----------------------------------------------------------------------------#
###############################################################################&lt;/pre&gt;

So, already, the standard unit-test structure has shown that we need to create 
&lt;code&gt;testIsValidMemberType&lt;/code&gt; to test the &lt;code&gt;IsValidMemberType&lt;/code&gt; 
method that &lt;code&gt;TypedList&lt;/code&gt; inherits from &lt;code&gt;BaseTypedCollection&lt;/code&gt;, 
and the &lt;code&gt;MemberTypes&lt;/code&gt; property, also from &lt;code&gt;BaseTypedCollection&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since those are actually &lt;em&gt;implemented&lt;/em&gt; in &lt;code&gt;BaseTypedCollection&lt;/code&gt;, all we really need to test &lt;em&gt;for&lt;/em&gt; is that they are the inherited members, though:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;        # Test Properties
        def testMemberTypes( self ):
            &quot;&quot;&quot;Unit-tests the MemberTypes property of the TypedList class.&quot;&quot;&quot;
            self.assertEquals( TypedList.MemberTypes, BaseTypedCollection.MemberTypes, &#39;TypedList should inherit the MemberTypes property of BaseTypedCollection.&#39; )

        # Test Methods
        def testIsValidMemberType( self ):
            &quot;&quot;&quot;Unit-tests the IsValidMemberType method of the TypedList class.&quot;&quot;&quot;
            self.assertEquals( TypedList.IsValidMemberType, BaseTypedCollection.IsValidMemberType, &#39;TypedList should inherit the IsValidMemberType method of BaseTypedCollection.&#39; )&lt;/pre&gt;

&lt;p&gt;Once those test-methods are in place, we re-run the tests:

&lt;pre&gt;###############################################################################
Running unit-tests
###############################################################################
All unit-tests passed! Ready for build/copy!
 + Copied TypedContainers.py to ~/.local/lib/python2.6/site-packages
###############################################################################&lt;/pre&gt;

&lt;em&gt;Et viola!&lt;/em&gt; We now have the members inherited from &lt;code&gt;BaseTypedCollection&lt;/code&gt; 
confirmed. In retrospect, these same two tests should also be added to &lt;code&gt;TypedDictionary&lt;/code&gt;, 
as well as the &lt;code&gt;testMethodCountAndTests&lt;/code&gt; and &lt;code&gt;testPropertyCountAndTests&lt;/code&gt; 
test-methods, which I&#39;m going to do now, before I forget. They didn&#39;t cause any 
test-failures, so that was an easy addition...&lt;/p&gt;

&lt;p&gt;That doesn&#39;t even begin to cover the members (public or otherwise) of a &lt;code&gt;list&lt;/code&gt;, though... If we take a look at a list-instance in a terminal, we see that there are actually a &lt;em&gt;lot&lt;/em&gt; of members (I actually used a quick little hacked-up script to generate this list so I could show what &lt;em&gt;types&lt;/em&gt; of members they all are):
&lt;ul style=&quot;font-family: monospace&quot;&gt;
    &lt;li&gt;__add__ ............ &amp;lt;method-wrapper &#39;__add__&#39;... &lt;/li&gt;
    &lt;li&gt;__class__ .......... &amp;lt;type &#39;list&#39;&amp;gt;
    &lt;li&gt;__contains__ ....... &amp;lt;method-wrapper &#39;__contains__&#39;... &lt;/li&gt;
    &lt;li&gt;__delattr__ ........ &amp;lt;method-wrapper &#39;__delattr__&#39;... &lt;/li&gt;
    &lt;li&gt;__delitem__ ........ &amp;lt;method-wrapper &#39;__delitem__&#39;... &lt;/li&gt;
    &lt;li&gt;__delslice__ ....... &amp;lt;method-wrapper &#39;__delslice__&#39;... &lt;/li&gt;
    &lt;li&gt;__doc__ ............ list() -&gt; new empty list&lt;br/&gt;list(iterable) -&gt; new list initialized from iterable&#39;s items&lt;/li&gt;
    &lt;li&gt;__eq__ ............. &amp;lt;method-wrapper &#39;__eq__&#39;... &lt;/li&gt;
    &lt;li&gt;__format__ ......... &amp;lt;built-in method __format__... &lt;/li&gt;
    &lt;li&gt;__ge__ ............. &amp;lt;method-wrapper &#39;__ge__&#39;... &lt;/li&gt;
    &lt;li&gt;__getattribute__ ... &amp;lt;method-wrapper &#39;__getattribute__&#39;... &lt;/li&gt;
    &lt;li&gt;__getitem__ ........ &amp;lt;built-in method __getitem__... &lt;/li&gt;
    &lt;li&gt;__getslice__ ....... &amp;lt;method-wrapper &#39;__getslice__&#39;... &lt;/li&gt;
    &lt;li&gt;__gt__ ............. &amp;lt;method-wrapper &#39;__gt__&#39;... &lt;/li&gt;
    &lt;li&gt;__hash__ ........... None&lt;/li&gt;
    &lt;li&gt;__iadd__ ........... &amp;lt;method-wrapper &#39;__iadd__&#39;... &lt;/li&gt;
    &lt;li&gt;__imul__ ........... &amp;lt;method-wrapper &#39;__imul__&#39;... &lt;/li&gt;
    &lt;li&gt;__init__ ........... &amp;lt;method-wrapper &#39;__init__&#39;... &lt;/li&gt;
    &lt;li&gt;__iter__ ........... &amp;lt;method-wrapper &#39;__iter__&#39;... &lt;/li&gt;
    &lt;li&gt;__le__ ............. &amp;lt;method-wrapper &#39;__le__&#39;... &lt;/li&gt;
    &lt;li&gt;__len__ ............ &amp;lt;method-wrapper &#39;__len__&#39;... &lt;/li&gt;
    &lt;li&gt;__lt__ ............. &amp;lt;method-wrapper &#39;__lt__&#39;... &lt;/li&gt;
    &lt;li&gt;__mul__ ............ &amp;lt;method-wrapper &#39;__mul__&#39;... &lt;/li&gt;
    &lt;li&gt;__ne__ ............. &amp;lt;method-wrapper &#39;__ne__&#39;... &lt;/li&gt;
    &lt;li&gt;__new__ ............ &amp;lt;built-in method __new__... &lt;/li&gt;
    &lt;li&gt;__reduce__ ......... &amp;lt;built-in method __reduce__... &lt;/li&gt;
    &lt;li&gt;__reduce_ex__ ...... &amp;lt;built-in method __reduce_ex__... &lt;/li&gt;
    &lt;li&gt;__repr__ ........... &amp;lt;method-wrapper &#39;__repr__&#39;... &lt;/li&gt;
    &lt;li&gt;__reversed__ ....... &amp;lt;built-in method __reversed__... &lt;/li&gt;
    &lt;li&gt;__rmul__ ........... &amp;lt;method-wrapper &#39;__rmul__&#39;... &lt;/li&gt;
    &lt;li&gt;__setattr__ ........ &amp;lt;method-wrapper &#39;__setattr__&#39;... &lt;/li&gt;
    &lt;li&gt;__setitem__ ........ &amp;lt;method-wrapper &#39;__setitem__&#39;... &lt;/li&gt;
    &lt;li&gt;__setslice__ ....... &amp;lt;method-wrapper &#39;__setslice__&#39;... &lt;/li&gt;
    &lt;li&gt;__sizeof__ ......... &amp;lt;built-in method __sizeof__... &lt;/li&gt;
    &lt;li&gt;__str__ ............ &amp;lt;method-wrapper &#39;__str__&#39;... &lt;/li&gt;
    &lt;li&gt;__subclasshook__ ... &amp;lt;built-in method __subclasshook__... &lt;/li&gt;
    &lt;li&gt;append ............. &amp;lt;built-in method append... &lt;/li&gt;
    &lt;li&gt;count .............. &amp;lt;built-in method count... &lt;/li&gt;
    &lt;li&gt;extend ............. &amp;lt;built-in method extend... &lt;/li&gt;
    &lt;li&gt;index .............. &amp;lt;built-in method index... &lt;/li&gt;
    &lt;li&gt;insert ............. &amp;lt;built-in method insert... &lt;/li&gt;
    &lt;li&gt;pop ................ &amp;lt;built-in method pop... &lt;/li&gt;
    &lt;li&gt;remove ............. &amp;lt;built-in method remove... &lt;/li&gt;
    &lt;li&gt;reverse ............ &amp;lt;built-in method reverse... &lt;/li&gt;
    &lt;li&gt;sort ............... &amp;lt;built-in method sort... &lt;/li&gt;
&lt;/ul&gt;
The reason that the various public members from &lt;code&gt;append&lt;/code&gt; through 
&lt;code&gt;sort&lt;/code&gt; at the end of this list don&#39;t raise errors in the methods-count 
test is because they are &lt;em&gt;built-ins&lt;/em&gt;, rather than &quot;normal&quot; methods. It 
would be possible to add a &lt;code&gt;testBuiltinCountAndTests&lt;/code&gt; test-method, 
using the same pattern as &lt;code&gt;testMethodCountAndTests&lt;/code&gt; and 
&lt;code&gt;testPropertyCountAndTests&lt;/code&gt;, but that would require modification of 
&lt;code&gt;UnitTestUtilities&lt;/code&gt; that I don&#39;t want to get into right now (I&#39;ll 
note it for later addition, though, don&#39;t worry...).&lt;/p&gt;

&lt;p&gt;Instead, I&#39;m going to take a look at the &lt;code&gt;pydoc&lt;/code&gt; output for my 
class, and/or dig around a bit in the &lt;a href=&quot;http://docs.python.org/tutorial/datastructures.html#more-on-lists&quot;&gt;Python 
list documentation&lt;/a&gt; and &lt;a href=&quot;http://docs.python.org/reference/datamodel.html#emulating-container-types&quot;&gt;Emulating 
container types&lt;/a&gt; documentation for some guidance on what all of these members 
&lt;em&gt;do&lt;/em&gt;. As a fall-back, in case I don&#39;t have network-access to check the 
online documentation, I could also create a simple list-extension class, override 
all of it&#39;s methods to print out that they were called (and with what arguments) 
before calling the parent list-methods, and see what happens when I do normal 
list-manipulation. I&#39;ll need to look at those normal list-manipulations anyway 
in order to build out unit-tests for basic list capabilities for the 
&lt;code&gt;TypedList&lt;/code&gt;, so that has benefits beyond just figuring out what each 
method does. But that&#39;d be time-consuming, with 45(!) items to look into, so I&#39;m 
going to check the documentation &lt;em&gt;first&lt;/em&gt;...&lt;/p&gt;

&lt;p&gt;And that seems like a good spot to stop for today.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/1024778405445585046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/test-driven-development-implementing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/1024778405445585046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/1024778405445585046'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/12/test-driven-development-implementing.html' title='Test-driven Development: Implementing TypedList (part 1)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-918152372991651693</id><published>2011-11-30T04:30:00.000-08:00</published><updated>2011-11-30T04:30:00.361-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="DataConnectors"/><category scheme="http://www.blogger.com/atom/ns#" term="design"/><category scheme="http://www.blogger.com/atom/ns#" term="IsQuery"/><category scheme="http://www.blogger.com/atom/ns#" term="IsResultSet"/><category scheme="http://www.blogger.com/atom/ns#" term="ResultSet"/><category scheme="http://www.blogger.com/atom/ns#" term="unit-testing"/><title type='text'>The DataConnectors module (part 2)</title><content type='html'>&lt;p&gt;The next two items tho work through are the &lt;code&gt;IsQuery&lt;/code&gt; and 
&lt;code&gt;IsResultSet&lt;/code&gt; nominal interfaces. With the sort of structure established 
with &lt;code&gt;IsDataConnector&lt;/code&gt; in the last post, all that really need be done 
for either of these is to figure out what functionality (and properties) we&#39;re 
eventually going to want in the classes and objects derived from these interfaces
 - so it&#39;s design time again! This will fill out two more items in the class diagram.&lt;/p&gt;

&lt;p&gt;Without going into too much detail about how the queries, results and connectors 
actually interact, the process for retrieving data with a query using the DataConnectors 
stack will look something like this:
&lt;ul&gt;
    &lt;li&gt;A connector object (derived from &lt;code&gt;BaseDataConnector&lt;/code&gt;, and in 
        turn from &lt;code&gt;IsDataConnector&lt;/code&gt;) will be created, pointing at some 
        back-end database.&lt;/li&gt;
    &lt;li&gt;A query-object (derived from IsQuery) will be created with the SQL to be 
        executed against the datasource, and the datasource itself. Whether the 
        query-object actually executes the SQL when instatiated, or when the 
        results are first requested is still to be determined, but it will have 
        a Results property that will allow access to the results of the query 
        against the database.&lt;/li&gt;
    &lt;li&gt;When the results are requested from the query-object, they will be returned 
        as a list of dumb data-objcets, each having an attribute for each field 
        in the rows returned for the result-set, named after the field-name, and 
        containing the value retrieved. These attributes will be immutable.&lt;/li&gt;
&lt;/ul&gt;
This structure should allow a good balance between flexibility, ease of use, and 
ease of maintaining data-access code.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Code-share:&lt;/b&gt; &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/DataConnectors.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/DataConnectors.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;IsQuery&lt;/b&gt; (Nominal interface):&lt;/p&gt;



&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class IsQuery( object ):
    &quot;&quot;&quot;Class doc-string.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    Datasource = property( None, None, None, &#39;Gets the data-source (an IsDataConnector instance) that the query was or will be executed against.&#39; )
    Results = property( None, None, None, &#39;Gets the results returned by execution of the query against the object\&#39;s Datasource.&#39; )
    Sql = property( None, None, None, &#39;Gets the SQL that has been executed against the object\&#39;s Datasource.&#39; )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;datasource&#39;, None, &#39;(IsDataConncetor, required) The IsDataConnector instance that the query\&#39;s SQL will be executed against.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;sql&#39;, None, &#39;(String, required) The SQL that will be executed against the supplied datasource.&#39; )
    def __init__( self, datasource, sql ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == IsQuery:
            raise NotImplementedError( &#39;IsQuery is (nominally) an interface, and is not intended to be instantiated.&#39; )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

__all__ += [ &#39;IsQuery&#39; ]&lt;/pre&gt;



&lt;p&gt;There really isn&#39;t much to say about &lt;code&gt;IsQuery&lt;/code&gt;: it&#39;s simple, it has 
only a few properties, and there aren&#39;t any method-stubs to it. In fact, the only 
significant change here is that the properties have been documented in the 
interface. We&#39;ll use this documentation-string later, when we define the 
&lt;code&gt;Query&lt;/code&gt; class. The unit-tests are equally straightforward:&lt;/p&gt;



&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class IsQueryDerived( IsQuery ):
        def __init__( self, datasource, sql ):
            IsQuery.__init__( self, datasource, sql )
    
    class testIsQuery( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the IsQuery class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testDerived( self ):
            &quot;&quot;&quot;Testing abstract nature of the IsQuery class.&quot;&quot;&quot;
            try:
                testObject = IsQuery( None, None )
                self.fail( &#39;Instantiation of an IsQuery object should raise a NotImplementedError.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Instantiation of an IsQuery object should raise a NotImplementedError, but %s was raised instead:\n  %s.&#39; % ( error.__class__.__name__, error ) )
            testObject = IsQueryDerived( None, None )
            self.assertTrue( isinstance( testObject, IsQuery ), &#39;Objects derived from IsQuery should be instances of IsQuery.&#39; )

        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the IsQuery class.&quot;&quot;&quot;
            items = getMemberNames( IsQuery )[0]
            actual = len( items )
            expected = 3
            self.assertEquals( expected, actual, &#39;IsQuery is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the IsQuery class.&quot;&quot;&quot;
            items = getMemberNames( IsQuery )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;IsQuery is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test properties

        def testDatasource( self ):
            &quot;&quot;&quot;Tests the datasource property of the IsQuery interface.&quot;&quot;&quot;
            testObject = IsQueryDerived( None, None )
            try:
                x = testObject.Datasource
                self.fail( &#39;IsQuery.Datasource should not be usable as a getter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Datasource, used as a getter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.Datasource = None
                self.fail( &#39;IsQuery.Datasource should not be usable as a setter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Datasource, used as a setter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.Datasource
                self.fail( &#39;IsQuery.Datasource should not be usable as a deleter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Datasource, used as a deleter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testResults( self ):
            &quot;&quot;&quot;Tests the Results property of the IsQuery interface.&quot;&quot;&quot;
            testObject = IsQueryDerived( None, None )
            try:
                x = testObject.Results
                self.fail( &#39;IsQuery.Results should not be usable as a getter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Results, used as a getter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.Results = None
                self.fail( &#39;IsQuery.Results should not be usable as a setter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Results, used as a setter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.Results
                self.fail( &#39;IsQuery.Results should not be usable as a deleter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Results, used as a deleter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testSql( self ):
            &quot;&quot;&quot;Tests the Sql property of the IsQuery interface.&quot;&quot;&quot;
            testObject = IsQueryDerived( None, None )
            try:
                x = testObject.Sql
                self.fail( &#39;IsQuery.Sql should not be usable as a getter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Sql, used as a getter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.Sql = None
                self.fail( &#39;IsQuery.Sql should not be usable as a setter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Sql, used as a setter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.Sql
                self.fail( &#39;IsQuery.Sql should not be usable as a deleter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsQuery.Sql, used as a deleter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )&lt;/pre&gt;



&lt;p&gt;I didn&#39;t provide any commentary on the similarly-structure unit-tests in my 
last post, for &lt;code&gt;IsDataConnector&lt;/code&gt;, so let me go over some of the new 
items that are common to it and to &lt;code&gt;IsQuery&lt;/code&gt; alike:&lt;/p&gt;


&lt;dl&gt;

    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;14-24&lt;/dt&gt;
    &lt;dd&gt;The &lt;code&gt;testDerived&lt;/code&gt; test-methods, in both cases, are really just 
        intended to prove that the nominal interfaces behave as expected when 
        directly instantiated and when implemented. Since there&#39;s not much in the 
        way of &quot;real&quot; functionality associated with either, they shouldn&#39;t be 
        instantiated (it would do no good to anyway, since instances would just 
        be dumb objects with properties and methods that raise errors).&lt;/dd&gt;

    &lt;dt&gt;26-33, 35-42&lt;/dt&gt;
    &lt;dd&gt;The standard &lt;code&gt;testPropertyCountAndTests&lt;/code&gt; and 
        &lt;code&gt;testMethodCountAndTests&lt;/code&gt; make sure that all properties and 
        method have test-methods associated with them, like usual.&lt;/dd&gt;

    &lt;dt&gt;46-69&lt;/dt&gt;
    &lt;dd&gt;The &lt;code&gt;testDatasource&lt;/code&gt; test-method makes certain that the 
        interface&#39;s &lt;code&gt;Datasource&lt;/code&gt; property exists, but is not implemented, 
        raising an &lt;code&gt;AttributeError&lt;/code&gt; when a get, set or delete operation 
        is attempted against it.&lt;/dd&gt;

    &lt;dt&gt;71-94, 96-119&lt;/dt&gt;
    &lt;dd&gt;Test-methods, using the same structure as &lt;code&gt;testDatasource&lt;/code&gt;, 
        but for the &lt;code&gt;Results&lt;/code&gt; and &lt;code&gt;Sql&lt;/code&gt; properties of the 
        interface.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Overall, pretty simple, I hope. The interface isn&#39;t very complex, so the tests 
against it shouldn&#39;t be too complex either.&lt;/p&gt;

&lt;p&gt;The next nominal interface I&#39;m going to delve into is &lt;code&gt;IsResultSet&lt;/code&gt;. 
Before I do, though, I&#39;d like to show the class diagram again, because when I drew 
it up, there was something that I did that cannot, I think, be done in languages 
that provide formal interface declarations. Take a moment and look at it again, 
if you would, paying close attention to the relationships associated with 
&lt;code&gt;IsResultSet&lt;/code&gt; (upper right area):


&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTgnr12oLVKW0bbY8mlWYiCcWmfZHWOdD5nNZe6w_12o3_vgUf3OL2A5lUA12rI4xYQwlr0Eu4RVguWdXIxTQ5DMA2rNSLh0dCUqjw22hbBiS0QUJwlFbIK_-ihfSKPMui7xB6jqeXXM/s1600/DataConnectors-uml.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
        &lt;img border=&quot;0&quot; height=&quot;221&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTgnr12oLVKW0bbY8mlWYiCcWmfZHWOdD5nNZe6w_12o3_vgUf3OL2A5lUA12rI4xYQwlr0Eu4RVguWdXIxTQ5DMA2rNSLh0dCUqjw22hbBiS0QUJwlFbIK_-ihfSKPMui7xB6jqeXXM/s320/DataConnectors-uml.png&quot; width=&quot;320&quot; /&gt;
    &lt;/a&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;The tricky bit here, at least as presented on the class diagram, is that 
&lt;code&gt;IsResultSet&lt;/code&gt;, a nominal interface, is derived from &lt;code&gt;TypedList&lt;/code&gt;, 
which is a concrete class. I&#39;m not going to keep that structure (and I&#39;ll alter 
the class diagram accordingly), but this idea is, possibly, worth some discussion.&lt;/p&gt;

&lt;p&gt;If that inheritance-relationship were kept, what we would end up with is a nominal 
interface that contained some actual, concrete implementation. If you&#39;re used to 
interfaces as they&#39;re handled in most of the languages that support formal interface 
declarations, this may set your eye to twitching. But, really, it might not be 
as bad as you&#39;d think at first blush. In this particular case, since I&#39;m not 
expecting to need more than one &lt;code&gt;ResultSet&lt;/code&gt; class, it &lt;em&gt;almost&lt;/em&gt; 
makes sense to leave the relationship - anything that implemented &lt;code&gt;IsResultSet&lt;/code&gt; 
would then also be, inherently, a &lt;code&gt;TypedList&lt;/code&gt;, which is exactly what 
is needed if we assume that any result-set object will be a list of rows with 
some additional properties.&lt;/p&gt;

&lt;p&gt;Since that&#39;s what I&#39;m aiming to accomplish, that doesn&#39;t actually feel too bad.&lt;/p&gt;

&lt;p&gt;Yes, there are potential encapsulation implications, and 
it could certainly have implications in how unit-tests are written for 
&lt;code&gt;IsResultSet&lt;/code&gt; and anything that derived from it, but even if it was 
discovered somewhere down the line that the &lt;code&gt;TypedList&lt;/code&gt;/&lt;code&gt;IsResultSet&lt;/code&gt; 
joint inheritance was causing problems, it&#39;d be a simple matter of changing the 
inheritance there, removing some (now-superfluous) unit-tests for 
&lt;code&gt;IsResultSet&lt;/code&gt;, and moving them to the derived class&#39; unit-tests. That 
process might be a bit tedious, but it wouldn&#39;t be difficult (and I think it 
would actually be a quick change to make).&lt;/p&gt;

&lt;p&gt;If I were &lt;em&gt;&lt;b&gt;100% confident&lt;/b&gt;&lt;/em&gt; that there wouldn&#39;t be a need to have more than one &lt;code&gt;ResultSet&lt;/code&gt;-like class in the stack, I think I&#39;d leave the relationship and it&#39;s... odd... inheritance. But I&#39;m not &lt;em&gt;that&lt;/em&gt; sure that I won&#39;t want or need to have a custom implementation similar to &lt;code&gt;ResultSet&lt;/code&gt; somewhere down the line, so I&#39;ll break it out.&lt;/p&gt;

&lt;p&gt;So, the &lt;em&gt;revised&lt;/em&gt; class-diagram looks like this:&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJwRFXFCen6P9ugzCx0oQ-itkIyrgwEEzlQfB0oLfEkLb4_hOxEKuhFFbISRDzEfVdrDcXbEE32UE2Dd2GqSOT0tKzudMcbeOWTesUMQg2EZq48kJR7J6wFqTK4LDfB8dtsmcRnap9Xak/s1600/DataConnectors-uml.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
        &lt;img border=&quot;0&quot; height=&quot;214&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJwRFXFCen6P9ugzCx0oQ-itkIyrgwEEzlQfB0oLfEkLb4_hOxEKuhFFbISRDzEfVdrDcXbEE32UE2Dd2GqSOT0tKzudMcbeOWTesUMQg2EZq48kJR7J6wFqTK4LDfB8dtsmcRnap9Xak/s320/DataConnectors-uml.png&quot; width=&quot;320&quot; /&gt;
    &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;Another (minor) advantage to this is that I don&#39;t have to stop and implement 
&lt;code&gt;TypedList&lt;/code&gt; right here and now, though I think I&#39;ll take care of that 
soon, since I &lt;em&gt;will&lt;/em&gt; need that to be in place before we get to implementation 
of &lt;code&gt;ResultSet&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;IsResultSet&lt;/b&gt; (Nominal interface):&lt;/p&gt;



&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class IsResultSet( object ):
    &quot;&quot;&quot;Class doc-string.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    CurrentRow = property( None, None, None, &#39;Gets the current row of the result-set.&#39; )
    FieldNames = property( None, None, None, &#39;Gets the names of the fields in each row of the results.&#39; )
    RowCount = property( None, None, None, &#39;Gets the number of rows in the result-set.&#39; )
    RowIndex = property( None, None, None, &#39;Gets the index of the current row of the result-set in an iteration.&#39; )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __init__( self ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == IsResultSet:
            raise NotImplementedError( &#39;IsResultSet is (nominally) an interface, and is not intended to be instantiated.&#39; )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

__all__ += [ &#39;IsResultSet&#39; ]&lt;/pre&gt;



&lt;p&gt;The only items of note here are the property-definitions (lines 24-27), which are defined the same way the properties for &lt;code&gt;IsQuery&lt;/code&gt; were, above.&lt;/p&gt;



&lt;p&gt;Unit-tests&lt;/p&gt;



&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class IsResultSetDerived( IsResultSet ):
        def __init__( self ):
            pass
    
    class testIsResultSet( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the IsResultSet class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testDerived( self ):
            &quot;&quot;&quot;Testing abstract nature of the IsResultSet class.&quot;&quot;&quot;
            try:
                testObject = IsResultSet()
                self.fail( &#39;Instantiation of an IsResultSet object should raise a NotImplementedError.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Instantiation of an IsResultSet object should raise a NotImplementedError, but %s was raised instead:\n  %s.&#39; % ( error.__class__.__name__, error ) )
            testObject = IsResultSetDerived()
            self.assertTrue( isinstance( testObject, IsResultSet ), &#39;Objects derived from IsResultSet should be instances of IsResultSet.&#39; )

        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the IsResultSet class.&quot;&quot;&quot;
            items = getMemberNames( IsResultSet )[0]
            actual = len( items )
            expected = 4
            self.assertEquals( expected, actual, &#39;IsResultSet is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the IsResultSet class.&quot;&quot;&quot;
            items = getMemberNames( IsResultSet )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;IsResultSet is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Test properties

        def testCurrentRow( self ):
            &quot;&quot;&quot;Tests the CurrentRow property of the IsResultSet interface.&quot;&quot;&quot;
            testObject = IsResultSetDerived()
            try:
                x = testObject.CurrentRow
                self.fail( &#39;IsResultSet.CurrentRow should not be usable as a getter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.CurrentRow, used as a getter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.CurrentRow = None
                self.fail( &#39;IsResultSet.CurrentRow should not be usable as a setter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.CurrentRow, used as a setter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.CurrentRow
                self.fail( &#39;IsResultSet.CurrentRow should not be usable as a deleter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.CurrentRow, used as a deleter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testFieldNames( self ):
            &quot;&quot;&quot;Tests the FieldNames property of the IsResultSet interface.&quot;&quot;&quot;
            testObject = IsResultSetDerived()
            try:
                x = testObject.FieldNames
                self.fail( &#39;IsResultSet.FieldNames should not be usable as a getter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.FieldNames, used as a getter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.FieldNames = None
                self.fail( &#39;IsResultSet.FieldNames should not be usable as a setter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.FieldNames, used as a setter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.FieldNames
                self.fail( &#39;IsResultSet.FieldNames should not be usable as a deleter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.FieldNames, used as a deleter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testRowCount( self ):
            &quot;&quot;&quot;Tests the RowCount property of the IsResultSet interface.&quot;&quot;&quot;
            testObject = IsResultSetDerived()
            try:
                x = testObject.RowCount
                self.fail( &#39;IsResultSet.RowCount should not be usable as a getter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.RowCount, used as a getter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.RowCount = None
                self.fail( &#39;IsResultSet.RowCount should not be usable as a setter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.RowCount, used as a setter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.RowCount
                self.fail( &#39;IsResultSet.RowCount should not be usable as a deleter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.RowCount, used as a deleter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testRowIndex( self ):
            &quot;&quot;&quot;Tests the RowIndex property of the IsResultSet interface.&quot;&quot;&quot;
            testObject = IsResultSetDerived()
            try:
                x = testObject.RowIndex
                self.fail( &#39;IsResultSet.RowIndex should not be usable as a getter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.RowIndex, used as a getter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testObject.RowIndex = None
                self.fail( &#39;IsResultSet.RowIndex should not be usable as a setter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.RowIndex, used as a setter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.RowIndex
                self.fail( &#39;IsResultSet.RowIndex should not be usable as a deleter.&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;IsResultSet.RowIndex, used as a deleter, should raise AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )&lt;/pre&gt;



&lt;p&gt;These unit-tests are &lt;em&gt;also&lt;/em&gt; essentially identical to the ones generated 
for &lt;code&gt;IsQuery&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A side-note, however, on these unit-tests: Since I had stubbed out all of the 
interfaces, abstract classes and classes I knew of when I started writing the 
code and posts for the &lt;code&gt;DataConnectors&lt;/code&gt; module, that included class- 
and unit-test stubs for &lt;code&gt;ResultSet&lt;/code&gt;. As soon as the properties of 
&lt;code&gt;IsResultSet&lt;/code&gt; were defined, the &lt;code&gt;testPropertyCountAndTests&lt;/code&gt; 
test-method on &lt;code&gt;&lt;em&gt;ResultSet&lt;/em&gt;&lt;/code&gt; started failing - it now had 
properties that were visible through inspection, and no tests for those properties!&lt;/p&gt; 

&lt;p&gt;In order to allow the tests for the module to pass (for now), with an eye 
towards raising error once actual implementation in ResultSet was available, I 
added the following test-methods to the tests for &lt;code&gt;ResultSet&lt;/code&gt;:&lt;/p&gt;



&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;        # Test Properties

        def testCurrentRow( self ):
            &quot;&quot;&quot;Unit-tests the CurrentRow property of the ResultSet class.&quot;&quot;&quot;
            self.assertEquals( ResultSet.CurrentRow, IsResultSet.CurrentRow, &#39;Until CurrentRow is implemented in ResultSet, it should be inherited from IsResultSet.&#39; )

        def testFieldNames( self ):
            &quot;&quot;&quot;Unit-tests the FieldNames property of the ResultSet class.&quot;&quot;&quot;
            self.assertEquals( ResultSet.FieldNames, IsResultSet.FieldNames, &#39;Until FieldNames is implemented in ResultSet, it should be inherited from IsResultSet.&#39; )

        def testRowCount( self ):
            &quot;&quot;&quot;Unit-tests the RowCount property of the ResultSet class.&quot;&quot;&quot;
            self.assertEquals( ResultSet.RowCount, IsResultSet.RowCount, &#39;Until RowCount is implemented in ResultSet, it should be inherited from IsResultSet.&#39; )&lt;/pre&gt;

&lt;p&gt;Since I &lt;em&gt;know&lt;/em&gt; that there will be implementation in &lt;code&gt;ResultSet&lt;/code&gt;, 
but it isn&#39;t there &lt;em&gt;yet&lt;/em&gt;, I want these tests to pass &lt;em&gt;until&lt;/em&gt; 
&lt;code&gt;ResultSet&lt;/code&gt; has it&#39;s own implementation of those properties...&lt;/p&gt;

&lt;p&gt;Basically, all I did here was to assert that the properties of the 
&lt;code&gt;ResultSet&lt;/code&gt; class and &lt;code&gt;IsResultSet&lt;/code&gt; interface were the same - 
so these will start breaking as soon as &lt;code&gt;ResultSet&lt;/code&gt; has local 
implementation of those properties.&lt;/p&gt;

&lt;p&gt;And I think that&#39;s enough for today. I&#39;m not sure whether I&#39;ll go and implement 
&lt;code&gt;TypedList&lt;/code&gt; next, or progress on to the nominal abstract classes of 
&lt;code&gt;DataConnetors&lt;/code&gt;... I&#39;ll have to noodle on which makes the most sense.&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/918152372991651693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/dataconnectors-module-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/918152372991651693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/918152372991651693'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/dataconnectors-module-part-2.html' title='The DataConnectors module (part 2)'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTgnr12oLVKW0bbY8mlWYiCcWmfZHWOdD5nNZe6w_12o3_vgUf3OL2A5lUA12rI4xYQwlr0Eu4RVguWdXIxTQ5DMA2rNSLh0dCUqjw22hbBiS0QUJwlFbIK_-ihfSKPMui7xB6jqeXXM/s72-c/DataConnectors-uml.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-6124072756114377004</id><published>2011-11-28T04:30:00.001-08:00</published><updated>2011-11-28T04:30:00.360-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="class diagram"/><category scheme="http://www.blogger.com/atom/ns#" term="database connection"/><category scheme="http://www.blogger.com/atom/ns#" term="DataConnectors"/><category scheme="http://www.blogger.com/atom/ns#" term="DBFSpy"/><category scheme="http://www.blogger.com/atom/ns#" term="interface"/><category scheme="http://www.blogger.com/atom/ns#" term="IsDataConnector"/><title type='text'>The DataConnectors module (part 1), and some ruminations on interfaces</title><content type='html'>&lt;p&gt;Perhaps one of the reasons that I&#39;ve been struggling with picking &lt;code&gt;DBFSpy&lt;/code&gt; 
back up is that it has a lot of code in it that&#39;s only secondarily related to it&#39;s 
core purpose. That may sound kind of strange on the surface (I know I struggled 
with coming to the conclusion, and I&#39;m not completely sure that it&#39;s correct, 
but for now, that&#39;s my story and I&#39;m sticking to it).&lt;/p&gt;

&lt;p&gt;Consider, for example, that ultimately, it should be something configurable 
(which I have accounted for with my last post), and that it has database-connectivity 
functionality. Neither of those is directly related to the FUSE-based-filesystem 
aspect of the project, though, to be fair, it couldn&#39;t do what it&#39;s intended to 
do without &lt;i&gt;at least&lt;/i&gt; having the database-connection capabilities. That does 
&lt;i&gt;not&lt;/i&gt; mean that the database-connectivity functionality needs to be a part 
of the core &lt;code&gt;DBFSpy&lt;/code&gt; code, though. So, in the interests of both keeping 
the code modular, and of simplifying my future efforts on &lt;code&gt;DBFSpy&lt;/code&gt;, 
I&#39;m going to break the database-functionality out into it&#39;s own module: 
&lt;code&gt;DataConnectors&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Code-share:&lt;/b&gt; &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/DataConnectors.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/DataConnectors.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let&#39;s start with some design first. By my reckoning, it needs to have the ability 
to connect to, query against, and return results from a back-end database. Since 
my database-focus (for now) is centered around MySQL, I&#39;ll stop when I get to a 
functional MySQL connection with querying capabilities, though I want to make sure 
that it&#39;s extensible enough to allow use with, say, ODBC data-sources, PostgreSQL, 
or whatever else is technically possible. Additionally, there needs to be some 
sort of common mechanism to allow objects to persist their state-data as needed, 
and to track that their state data has changed or has been created, and thus 
needs to be updated or created in the database. In my mind, then, the class-structure 
will look something like the class diagram below.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;The Class Diagram&lt;/b&gt;&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
    &lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTgnr12oLVKW0bbY8mlWYiCcWmfZHWOdD5nNZe6w_12o3_vgUf3OL2A5lUA12rI4xYQwlr0Eu4RVguWdXIxTQ5DMA2rNSLh0dCUqjw22hbBiS0QUJwlFbIK_-ihfSKPMui7xB6jqeXXM/s1600/DataConnectors-uml.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;
        &lt;img border=&quot;0&quot; height=&quot;221&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTgnr12oLVKW0bbY8mlWYiCcWmfZHWOdD5nNZe6w_12o3_vgUf3OL2A5lUA12rI4xYQwlr0Eu4RVguWdXIxTQ5DMA2rNSLh0dCUqjw22hbBiS0QUJwlFbIK_-ihfSKPMui7xB6jqeXXM/s320/DataConnectors-uml.png&quot; width=&quot;320&quot; /&gt;
    &lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;In today&#39;s post, I&#39;m only going to cover one of the nominal interfaces of this 
module. There&#39;s a substantial amount of stuff going on even there, and I also wanted 
to share some of my thoughts (dare I call them insights?) on the creation of explicit 
interfaces.&lt;/p&gt;

&lt;p&gt;In most languages that directly support explicit interfaces, those interface 
definitions do not allow the specification of anything other than abstract methods. 
They don&#39;t allow properties to be specified, nor do they allow any sort of true 
functionality to be defined for any methods - everything is an abstract method-stub.&lt;/p&gt;

&lt;p&gt;That seems pretty straightforward.&lt;/p&gt;

&lt;p&gt;But taking a step back, a question occurs to me. Let me lead into it with some 
other thoughts/questions, though. First off, what purposes are served by specifying 
an interface? One of them, certainly, is to provide a &lt;em&gt;contract&lt;/em&gt;, right? 
When a class is defined that implements a particular interface, it&#39;s promising 
that it will provide the functionality specified by that interface. Somewhere along 
the line, whether as part of a compilation process, or at run-time, a failure of 
a class to make good on that promise should raise some kind of error (at run-time 
is, perhaps, less than optimal, but can be worked around with discipline, I believe).
A secondary, but related purpose is to provide what I&#39;ve been calling &quot;type-identity&quot; 
in my class&#39; doc-strings. If a class implements an interface, and assuming that the 
contract associated with it is enforced in &lt;em&gt;any&lt;/em&gt; fashion, the derivation 
from that interface allows our code to know that any given instance is of a given 
type.&lt;/p&gt;

&lt;p&gt;My question, then, is &quot;why &lt;em&gt;not&lt;/em&gt; allow interfaces to specify properties?&quot;
Properties, even in a language like Python, where they have no set type, and can 
be defined on the fly, could just as easily be part of a contract, couldn&#39;t they?&lt;/p&gt;

&lt;p&gt;The only arguments I&#39;ve been able to find thus far to justify properties not 
being part of interface definitions all seem to be variations on &quot;properties 
represent specific implementations of data representations, and would break 
encapsulation.&quot; I think I&#39;d argue that, provided that properties didn&#39;t specify 
anything more than that they exist, and &lt;em&gt;maybe&lt;/em&gt; what type they are (if 
applicable), that I&#39;d disagree. But maybe I&#39;m missing something.&lt;/p&gt;

&lt;p&gt;The reason for most of the pondering above is that as I was thinking out some 
of the details for the interfaces in the &lt;code&gt;DataConnectors&lt;/code&gt; module, it 
occurred to me that some of my nominal interfaces could easily specify/require 
that specific properties would at least &lt;em&gt;exist&lt;/em&gt; for derived classes. It 
would even have been possible to force specific types, perhaps, though I suspect 
that it would&#39;ve been more trouble than it would be worth (and may not have been 
possible without a &lt;em&gt;lot&lt;/em&gt; of nominal interface clutter).&lt;/p&gt;

&lt;p&gt;As a case in point, consider the &lt;code&gt;IsDataConnector&lt;/code&gt; nominal interface.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;IsDataConnector&lt;/b&gt; (Nominal interface):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class IsDataConnector( object ):
    &quot;&quot;&quot;Provides interface definition and type-identity for objects that can represent connectors to a back-end data-source (typically a relational database engine).&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    Connection = property( None, None, None, None )
    Database = property( None, None, None, None )
    Host = property( None, None, None, None )
    Password = property( None, None, None, None )
    User = property( None, None, None, None )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __init__( self ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == IsDataConnector:
            raise NotImplementedError( &#39;IsDataConnector is (nominally) an interface, and is not intended to be instantiated.&#39; )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def Connect( self ):
        &quot;&quot;&quot;Connects to the database specified by Database, residing on Host, using User and Password to connect.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s.Connect is not implemented as required by IsDataConnector.&#39; % ( self.__class__.__name__ ) )

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;query&#39;, None, &#39;The IsQuery object that will be executed against the object\&#39;s Connection.&#39; )
    def Query( self, query ):
        &quot;&quot;&quot;Executes the provided IsQuery object&#39;s query against the object&#39;s connection, returning one or more results.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s.Query is not implemented as required by IsDataConnector.&#39; % ( self.__class__.__name__ ) )

__all__ += [ &#39;IsDataConnector&#39; ]&lt;/pre&gt;

&lt;p&gt;Commentary&lt;/p&gt;

&lt;dl&gt;
    &lt;dt&gt;Line(s)&lt;/dt&gt;

    &lt;dt&gt;24-28&lt;/dt&gt;
    &lt;dd&gt;Here is where I&#39;ve defined the &quot;required&quot; properties for classes implementing 
        the interface.&lt;/dd&gt;
    &lt;dd&gt;The practical upshot of these is that each property has been defined, but 
        has no implementation associated with them (they have no getter, setter 
        or deleter methods). The more I think about this approach, the more I 
        like it. Consider:
        &lt;ul&gt;
            &lt;li&gt;Each property is now defined here, but has no implementation, and 
                will raise errors when tested. In combination with the normal 
                unit-testing processes (from previous posts) that requires a 
                test-method for each property, this feels pretty safe.&lt;/li&gt;
            &lt;li&gt;That effectively requires (provided that discipline is maintained 
                at the unit-testing level) that all derived classes will &lt;em&gt;have&lt;/em&gt; 
                to implement the properties defined here.&lt;/li&gt;
        &lt;/ul&gt;
        I also didn&#39;t specify a documentation-string for them, though I&#39;m toying 
        with the idea of putting the documentation string in at this level in the 
        object/interface tree, which is why they aren&#39;t just &lt;code&gt;property()&lt;/code&gt; 
        calls. This also feels like another justification for explicitly defining 
        properties using the &lt;code&gt;property()&lt;/code&gt; function, rather than using 
        the decorator-structure.
    &lt;/dd&gt;

    &lt;dt&gt;24&lt;/dt&gt;
    &lt;dd&gt;The &lt;code&gt;Connection&lt;/code&gt; property will contain the actual database-connector object in a derived instance.&lt;/dd&gt;

    &lt;dt&gt;25&lt;/dt&gt;
    &lt;dd&gt;&lt;code&gt;Database&lt;/code&gt; will contain the name of the database being connected to.&lt;/dd&gt;

    &lt;dt&gt;26&lt;/dt&gt;
    &lt;dd&gt;&lt;code&gt;Host&lt;/code&gt; will contain the name or IP-address of the host machine that the &lt;code&gt;Database&lt;/code&gt;-database resides on.&lt;/dd&gt;

    &lt;dt&gt;27&lt;/dt&gt;
    &lt;dd&gt;&lt;code&gt;Password&lt;/code&gt; will contain the password used to connect to the database.&lt;/dd&gt;

    &lt;dt&gt;28&lt;/dt&gt;
    &lt;dd&gt;&lt;code&gt;User&lt;/code&gt; will contain the user-name used to connect to the database.&lt;/dd&gt;

    &lt;dt&gt;49-52&lt;/dt&gt;
    &lt;dd&gt;Requires a common method/mechanism to connect to the database that the derived object represents a connection to.&lt;/dd&gt;

    &lt;dt&gt;55-60&lt;/dt&gt;
    &lt;dd&gt;Requires a common method/mechanism to execute a query against the database.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Looking at this definition, then, and applying it to &lt;code&gt;BaseDataConnector&lt;/code&gt; 
and &lt;code&gt;MySQLConnector&lt;/code&gt; (and any other connectors that may be derived from 
&lt;code&gt;BaseDataConector&lt;/code&gt;), it should hopefully be obvious that they will have:
&lt;ul&gt;
    &lt;li&gt;A &lt;code&gt;Connection&lt;/code&gt; property;&lt;/li&gt;
    &lt;li&gt;A &lt;code&gt;Database&lt;/code&gt; property;&lt;/li&gt;
    &lt;li&gt;A &lt;code&gt;Host&lt;/code&gt; property;&lt;/li&gt;
    &lt;li&gt;A &lt;code&gt;Password&lt;/code&gt; property;&lt;/li&gt;
    &lt;li&gt;A &lt;code&gt;User&lt;/code&gt; property;&lt;/li&gt;
    &lt;li&gt;A &lt;code&gt;Connect&lt;/code&gt; method;&lt;/li&gt;
    &lt;li&gt;A &lt;code&gt;Query&lt;/code&gt; method; and&lt;/li&gt;
&lt;/ul&gt;
There are no implementation details, and it&#39;s not possible to determine (yet) where 
these properties and methods will actually be realized. I expect that the items 
that aren&#39;t specific to a given database-engine - most of the properties, possibly 
all of them, and the &lt;code&gt;AppendToQueue&lt;/code&gt; method - will be implemented in 
&lt;code&gt;BaseDataConenctor&lt;/code&gt;. That&#39;s the lowest common denominator for actual 
implementations of various database-connector objects, at least, so that would 
make the most sense, but we&#39;ll have to see how that works out later.&lt;/p&gt;

&lt;p&gt;I&#39;m going to stop here for today, while this post sinks in, but before I do, 
here are the unit-tests for the interface.&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class IsDataConnectorDerived( IsDataConnector ):
        def __init__( self ):
            pass
    
    class testIsDataConnector( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the IsDataConnector class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testAbstract( self ):
            &quot;&quot;&quot;Testing abstract nature of the IsDataConnector class.&quot;&quot;&quot;
            try:
                testObject = IsDataConnector()
                self.fail( &#39;IsDataConnector is a nominal interface, and should not be instantiable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to instantiate an IsDataConnector should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = IsDataConnectorDerived()
            self.assertTrue( isinstance( testObject, IsDataConnector ), &#39;Objects derived from IsDataConnector should be instances of it.&#39; )
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the IsDataConnector class.&quot;&quot;&quot;
            items = getMemberNames( IsDataConnector )[0]
            actual = len( items )
            expected = 5
            self.assertEquals( expected, actual, &#39;IsDataConnector is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the IsDataConnector class.&quot;&quot;&quot;
            items = getMemberNames( IsDataConnector )[1]
            actual = len( items )
            expected = 2
            self.assertEquals( expected, actual, &#39;IsDataConnector is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Unit-test properties

        def testConnection( self ):
            &quot;&quot;&quot;Unit-tests the Connection property of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.Connection = None
                self.fail( &#39;The Connection property of IsDataConnector is nominally abstract, and should not be settable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to set the Connection property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testValue = testObject.Connection
                self.fail( &#39;The Connection property of IsDataConnector is nominally abstract, and should not be gettable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to get the Connection property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.Connection
                self.fail( &#39;The Connection property of IsDataConnector is nominally abstract, and should not be deletable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to delete the Connection property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testDatabase( self ):
            &quot;&quot;&quot;Unit-tests the Database property of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.Database = None
                self.fail( &#39;The Database property of IsDataConnector is nominally abstract, and should not be settable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to set the Database property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testValue = testObject.Database
                self.fail( &#39;The Database property of IsDataConnector is nominally abstract, and should not be gettable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to get the Database property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.Database
                self.fail( &#39;The Database property of IsDataConnector is nominally abstract, and should not be deletable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to delete the Database property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testHost( self ):
            &quot;&quot;&quot;Unit-tests the Host property of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.Host = None
                self.fail( &#39;The Host property of IsDataConnector is nominally abstract, and should not be settable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to set the Host property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testValue = testObject.Host
                self.fail( &#39;The Host property of IsDataConnector is nominally abstract, and should not be gettable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to get the Host property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.Host
                self.fail( &#39;The Host property of IsDataConnector is nominally abstract, and should not be deletable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to delete the Host property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testPassword( self ):
            &quot;&quot;&quot;Unit-tests the Password property of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.Password = None
                self.fail( &#39;The Password property of IsDataConnector is nominally abstract, and should not be settable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to set the Password property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testValue = testObject.Password
                self.fail( &#39;The Password property of IsDataConnector is nominally abstract, and should not be gettable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to get the Password property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.Password
                self.fail( &#39;The Password property of IsDataConnector is nominally abstract, and should not be deletable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to delete the Password property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testUser( self ):
            &quot;&quot;&quot;Unit-tests the User property of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.User = None
                self.fail( &#39;The User property of IsDataConnector is nominally abstract, and should not be settable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to set the User property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                testValue = testObject.User
                self.fail( &#39;The User property of IsDataConnector is nominally abstract, and should not be gettable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to get the User property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.User
                self.fail( &#39;The User property of IsDataConnector is nominally abstract, and should not be deletable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Trying to delete the User property of IsDataConnector should raise an Attribute error, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        # Unit-test methods

        def testConnect( self ):
            &quot;&quot;&quot;Unit-tests the Connect method of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.Connect()
                self.fail( &#39;Connect should raise a NotImplementedError.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Connect should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__mame__, error ) )

        def testQuery( self ):
            &quot;&quot;&quot;Unit-tests the Query method of the IsDataConnector interface.&quot;&quot;&quot;
            testObject = IsDataConnectorDerived()
            try:
                testObject.Query( None )
                self.fail( &#39;Query should raise a NotImplementedError.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Query should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__mame__, error ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testIsDataConnector ) )&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/6124072756114377004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/dataconnectors-module-part-1-and-some.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6124072756114377004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6124072756114377004'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/dataconnectors-module-part-1-and-some.html' title='The DataConnectors module (part 1), and some ruminations on interfaces'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTgnr12oLVKW0bbY8mlWYiCcWmfZHWOdD5nNZe6w_12o3_vgUf3OL2A5lUA12rI4xYQwlr0Eu4RVguWdXIxTQ5DMA2rNSLh0dCUqjw22hbBiS0QUJwlFbIK_-ihfSKPMui7xB6jqeXXM/s72-c/DataConnectors-uml.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-1796231103021486345</id><published>2011-11-25T04:30:00.001-08:00</published><updated>2011-11-25T04:30:00.652-08:00</updated><title type='text'>Not posting today...</title><content type='html'>It&#39;s Thanksgiving here in the US, so I&#39;m taking the day off.&lt;br /&gt;
&lt;br /&gt;
Come back on Monday for the next installment, when I&#39;ll start looking at a data-connectors module.</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/1796231103021486345/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/not-posting-today.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/1796231103021486345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/1796231103021486345'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/not-posting-today.html' title='Not posting today...'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-4446754003990872478</id><published>2011-11-23T04:30:00.000-08:00</published><updated>2011-11-23T04:30:00.536-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="configuration"/><category scheme="http://www.blogger.com/atom/ns#" term="DBFSpy"/><category scheme="http://www.blogger.com/atom/ns#" term="HasConfiguration"/><category scheme="http://www.blogger.com/atom/ns#" term="IsConfigurable"/><category scheme="http://www.blogger.com/atom/ns#" term="MiniAppServer"/><title type='text'>The Configuration module</title><content type='html'>&lt;p&gt;One of the requirements for the project I mentioned that spawned the idea of 
the &lt;code&gt;MiniAppServer&lt;/code&gt; project was the need to be &quot;configurable,&quot; at 
least for some value of &quot;configurable.&quot; In pondering what I&#39;ve posted over the 
past couple of days for &lt;code&gt;MiniAppServer&lt;/code&gt;, it occurred to me that the 
original &quot;configurable&quot; requirement would, logically, carry through here as well, 
and that I hadn&#39;t addressed it &lt;i&gt;at all&lt;/i&gt; thus far.&lt;/p&gt;

&lt;p&gt;And, whether I proceed with &lt;code&gt;DBFSpy&lt;/code&gt;, restart it, or shelve it in 
favor of &lt;code&gt;MiniAppServer&lt;/code&gt; for the time being, I&#39;ll likely want 
configuration capabilities.&lt;/p&gt;

&lt;p&gt;So. Python&#39;s got at least one configuration-related module in it&#39;s default 
installation: &lt;a href=&quot;http://docs.python.org/library/configparser.html&quot;&gt;
&lt;code&gt;ConfigParser&lt;/code&gt;&lt;/a&gt;. It&#39;s aimed at providing configuration-capabilities 
based on what I think of as a typical configuration-file format (I&#39;m pretty sure 
I&#39;ve seen it in Windows and Linux environments over the years, at least), that 
looks something like this:&lt;/p&gt;

&lt;pre&gt;
#########################################################
# &quot;#&quot; indicates the beginning of a comment that         #
# runs to the end of the line                           #
#########################################################
[Section Name]                # Configuration-section name. 
                              # Spaces are allowed!
keyname:            value     # keynames can be mixed-case, 
                              # but come back as lower-case
keyname2:           value2    # values are returned as 
                              # strings by default.
&lt;/pre&gt;

&lt;p&gt;Since the configuration types, section-names, values, etc., will all vary 
significantly from one configurable type to another, there&#39;s not a whole lot that 
needs to be coded: An interface and an abstract class, to my thinking, cover most 
of it, since the specific implementations will have to dig out and format and/or 
convert the sections, key-names and values. The classes and their unit-tests are 
pretty simple as a result.&lt;/p&gt;

&lt;p&gt;The complete code is shared at &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/Template.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/Template.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stripping the items in question down to just the classes and their unit-tests, 
I get:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;IsConfigurable&lt;/b&gt; (Nominal interface):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class IsConfigurable( object ):
    &quot;&quot;&quot;Provides interface requirements and type-identoty for objects that can retrieve 
state-data from a configuration file (at present, only ConfigParser is supported).&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ConfigurationTypes = [ ConfigParser.ConfigParser ]

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __init__( self ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == IsConfigurable:
            raise NotImplementedError( &#39;IsConfigurable is (nominally) an interface, and is not intended to be instantiated.&#39; )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;configProvider&#39;, None, &#39;A configuration provider object, an instance of one of IsConfigurable.ConfigurationTypes&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;configSection&#39;, None, &#39;String, required) The configuration-section name that the object\&#39;s configuration-state should be retrieved from.&#39; )
    @DocumentConfiguration( &#39;Section Name&#39;, &#39;keyname&#39;, &#39;value&#39; )
    @DocumentConfiguration( &#39;Section Name&#39;, &#39;keyname2&#39;, &#39;value2&#39; )
    def Configure( self, configProvider, configSection ):
        &quot;&quot;&quot;Configures the object using configuration data from the specified section of the specified provider.&quot;&quot;&quot;
        raise NotImplementedError( &#39;%s.Configure has not been implemented&#39; % ( self.__class__.__name__ ) )

__all__ += [ &#39;IsConfigurable&#39; ]&lt;/pre&gt;

&lt;p&gt;Note the use of the &lt;code&gt;@DocumentConfiguration&lt;/code&gt; decorator on lines 49 and 50. Documentation of this sort of configuration information is what that decorator was designed for, and will provide documentation that looks like this:
&lt;pre&gt;Configure(self, configProvider, configSection)
    Configures the object using configuration data from the specified 
    section of the specified provider.
     
    ###################
    ## Arguments     ##
    ###################
     + self ................ The object-instance that the method is called against. 
     + configProvider ...... A configuration provider object, an instance of one of 
                             IsConfigurable.ConfigurationTypes 
     + configSection ....... String, required) The configuration-section name that 
                             the object&#39;s configuration-state should be retrieved 
                             from. 
     
    ###################
    ## Configuration ##
    ###################
     
    The relevant configuration-file section(s) and values(s) are:
    [Section Name]
    keyname ..................... value 
    keyname2 .................... value2&lt;/pre&gt;
&lt;/p&gt;

&lt;p&gt;The relevant unit-tests:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class IsConfigurableDerived( IsConfigurable ):
        def __init__( self ):
            IsConfigurable.__init__( self )
    
    class testIsConfigurable( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the IsConfigurable class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testDerived( self ):
            &quot;&quot;&quot;Testing abstract nature of the IsConfigurable class.&quot;&quot;&quot;
            try:
                testObject = IsConfigurable()
                self.fail( &#39;IsConfigurable is nominally an interface, and should not be instantiable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Instantiating IsConfigurable should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = IsConfigurableDerived()
            self.assertTrue( isinstance( testObject, IsConfigurable ), &#39;Objects implementing IsConfigurable should be instances of IsConfigurable.&#39; )
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the IsConfigurable class.&quot;&quot;&quot;
            items = getMemberNames( IsConfigurable )[0]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;IsConfigurable is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the IsConfigurable class.&quot;&quot;&quot;
            items = getMemberNames( IsConfigurable )[1]
            actual = len( items )
            expected = 1
            self.assertEquals( expected, actual, &#39;IsConfigurable is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Unit-test properties

        # Unit-test methods

        def testConfigure( self ):
            &quot;&quot;&quot;Unit-tests the Configure method of the IsConfigurable class.&quot;&quot;&quot;
            testObject = IsConfigurableDerived()
            try:
                testObject.Configure( ConfigParser.ConfigParser(), &#39;configSection&#39; )
                self.fail( &#39;An IsConfigurable instance that has not overridden Configure should raise a NotImplementedError when it is called.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;An IsConfigurable instance that has not overridden Configure should raise a NotImplementedError when it is called, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testIsConfigurable ) )&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;HasConfiguration&lt;/b&gt; (Nominal abstract class):&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;class HasConfiguration( object ):
    &quot;&quot;&quot;Provides a configuration property (allowing an instance of any of the types in 
IsConfigurable.ConfigurationTypes).&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetConfiguration( self ):
        &quot;&quot;&quot;Gets the object&#39;s associated configuration object.&quot;&quot;&quot;
        return self._configuration

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(Any of %s, required) The configuration-provider object that will contain the object\&#39;s configuration-state data.&#39; % ( IsConfigurable.ConfigurationTypes ) )
    def _SetConfiguration( self, value ):
        &quot;&quot;&quot;Sets the object&#39;s associated configuration object.&quot;&quot;&quot;
        if not value.__class__ in IsConfigurable.ConfigurationTypes:
            raise TypeError( &#39;%s.Configuration expects an instance of one of %s&#39; % ( self.__class__.__name__, IsConfigurable.ConfigurationTypes ) )
        self._configuration = value

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelConfiguration( self ):
        &quot;&quot;&quot;Deletes the object&#39;s associated configuration object.&quot;&quot;&quot;
        self._configuration = None

    ##################################
    # Class Properties               #
    ##################################

    Configuration = property ( _GetConfiguration, None, None, &#39;Gets the object\&#39;s associated configuration object.&#39; )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @ToDo( &#39;Document class.&#39; )
    def __init__( self ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == HasConfiguration:
            raise NotImplementedError( &#39;HasConfiguration is (nominally) an abstract class, and is not intended to be instantiated.&#39; )
        # Set default property value(s)
        self._DelConfiguration()

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

__all__ += [ &#39;HasConfiguration&#39; ]&lt;/pre&gt;

&lt;p&gt;The relevant unit-tests:&lt;/p&gt;

&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;    class HasConfigurationDerived( HasConfiguration ):
        def __init__( self ):
            HasConfiguration.__init__( self )
    
    class testHasConfiguration( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the HasConfiguration class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testDerived( self ):
            &quot;&quot;&quot;Testing abstract nature of the HasConfiguration class.&quot;&quot;&quot;
            try:
                testObject = HasConfiguration()
                self.fail( &#39;HasConfiguration is nominally an abstract class, and should not be instantiable.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Instantiating HasConfiguration should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            testObject = HasConfigurationDerived()
            self.assertTrue( isinstance( testObject, HasConfiguration ), &#39;Objects implementing HasConfiguration should be instances of HasConfiguration.&#39; )
            self.assertEquals( testObject.Configuration, None, &#39;A default instance of HasConfiguration should have a None value for it\&#39;s Configuration property.&#39; )
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the HasConfiguration class.&quot;&quot;&quot;
            pass
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the HasConfiguration class.&quot;&quot;&quot;
            items = getMemberNames( HasConfiguration )[0]
            actual = len( items )
            expected = 1
            self.assertEquals( expected, actual, &#39;HasConfiguration is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the HasConfiguration class.&quot;&quot;&quot;
            items = getMemberNames( HasConfiguration )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;HasConfiguration is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

        # Unit-test properties

        def testConfiguration( self ):
            &quot;&quot;&quot;Unit-tests the Configuration property of the HasConfiguration class.&quot;&quot;&quot;
            testObject = HasConfigurationDerived()
            goodValues = IsConfigurable.ConfigurationTypes
            badValues = [ None, &#39;string&#39;, 1, 1L, 1.0, True ]
            for testClass in goodValues:
                testValue = (testClass)()
                testObject._SetConfiguration( testValue )
                self.assertEquals( testObject.Configuration, testValue, &#39;The property set (%s) should be retrievable by a get.&#39; % ( testValue ) )
            for testValue in badValues:
                try:
                    testObject._SetConfiguration( testValue )
                    self.fail( &#39;Setting the configuration property to an item that is not an instance of %s (%s) should raise a TypeError&#39; % ( IsConfigurable.ConfigurationTypes, testValue ) )
                except TypeError:
                    pass
                except Exception, error:
                    self.fail( &#39;Setting the configuration property to an item that is not an instance of %s (%s) should raise a TypeError, but %s was raised instead:\n  %s&#39; % ( IsConfigurable.ConfigurationTypes, testValue, error.__class__.__name__, error ) )

        # Unit-test methods

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testHasConfiguration ) )&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/4446754003990872478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/configuration-module.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4446754003990872478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4446754003990872478'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/configuration-module.html' title='The Configuration module'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-3743661774630081072</id><published>2011-11-21T04:30:00.000-08:00</published><updated>2011-11-21T04:30:02.151-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="design"/><category scheme="http://www.blogger.com/atom/ns#" term="MiniAppServer"/><category scheme="http://www.blogger.com/atom/ns#" term="web-applications"/><title type='text'>Design for the MiniAppServer idea</title><content type='html'>The &lt;i&gt;design&lt;/i&gt; for &lt;code&gt;MiniAppServer&lt;/code&gt; feels like it ought 
to be fairly simple, given what I&#39;m thinking is a typical development-case:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;MiniAppServer&lt;/code&gt; instance, with various properties, including:
        &lt;ul&gt;
&lt;li&gt;Host name;&lt;/li&gt;
&lt;li&gt;Port;&lt;/li&gt;
&lt;li&gt;A document-root, if desired;&lt;/li&gt;
&lt;li&gt;A top-level request-routing mechanism (something that can 
                handle application-ish endpoints and/or static-file 
                requests/responses);&lt;/li&gt;
&lt;li&gt;Any application-ish endpoint-handlers needed;&lt;/li&gt;
&lt;li&gt;Any error-handling endpoint-handlers needed;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If necessary, add/register any non-standard endpoint-handlers;&lt;/li&gt;
&lt;li&gt;If necessary, add/register any standard handlers for errors, etc.;&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;MiniAppServer.serve_forever()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
When a request is recieved by the &lt;code&gt;MiniAppServer&lt;/code&gt; instance, 
it gets handled as follows:
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;The request (plus it&#39;s socket and whatever other information/objects 
        are needed by a &lt;code&gt;BaseRequestHandler&lt;/code&gt; instance) are passed 
        to the application&#39;s top-level routing object.&lt;/li&gt;
&lt;li&gt;That routing object examines the request made, looking for a match 
        based on:
        &lt;ol&gt;
&lt;li&gt;Registered application-ish endpoints;&lt;/li&gt;
&lt;li&gt;Static-file endpoints;&lt;/li&gt;
&lt;li&gt;Other endpoints? I can&#39;t think of any that make sense, but 
                I can&#39;t rule out the possibility...&lt;/li&gt;
&lt;/ol&gt;
If a match is found, the request (and everything associated) is 
        passed to a new instance of the appropriate endpoint class.
    &lt;/li&gt;
&lt;li&gt;The endpoint-handler does whatever it needs to do, generating 
        whatever output it needs to generate, and returns that response.
        &lt;ul&gt;
&lt;li&gt;If an error is encountered, then the request (etc.) should 
                be passed to the registered error-handling endpoint-handler, 
                if there is one;&lt;/li&gt;
&lt;li&gt;If there is not a specific error-handling endpoint, a simple, 
                raw response should be returned with the appropriate HTTP 
                code);&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
Given all of this, what I&#39;m expecting (at least as of this writing) is 
an object-structure something like so:&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;IsApplication &lt;span style=&quot;font-weight: normal;&quot;&gt;(nominal interface)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Provides interface requirements and type-identioty for objects 
        that can provide &lt;code&gt;MiniAppServer&lt;/code&gt;-like functionality. 
        This is specifically called out becuse there would otherwise be 
        circular references between &lt;code&gt;MiniAppServer&lt;/code&gt; and 
        &lt;code&gt;HasApplication&lt;/code&gt;.&lt;/dd&gt;
&lt;dt&gt;HasApplication &lt;span style=&quot;font-weight: normal;&quot;&gt;(nominal 
        abstract class)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Provides an &lt;code&gt;Application&lt;/code&gt; property for derived objects, 
        which should be an instance implementing &lt;code&gt;IsApplication&lt;/code&gt;.&lt;/dd&gt;
&lt;dt&gt;IsEndpoint &lt;span style=&quot;font-weight: normal;&quot;&gt;(nominal abstract class)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Provides baseline functionality, interface requirements and type-identity 
        for objects that can act as endpoints for a MiniAppServer instance. 
        Endpoint definitions should be derived from this and 
        &lt;code&gt;SocketServer.BaseRequestHandler&lt;/code&gt;, though I&#39;m not sure 
        whether I want to make &lt;code&gt;IsEndpoint&lt;/code&gt; a subclass of 
        &lt;code&gt;SocketServer.BaseRequestHandler&lt;/code&gt; directly, or just have 
        it check to make sure that instances derived from it are &lt;i&gt;also&lt;/i&gt; 
        derived from &lt;code&gt;SocketServer.BaseRequestHandler&lt;/code&gt;.&lt;/dd&gt;
&lt;dt&gt;BaseRouter &lt;span style=&quot;font-weight: normal;&quot;&gt;(nominal abstract class), or&lt;/span&gt;&lt;/dt&gt;
&lt;dt&gt;Router &lt;span style=&quot;font-weight: normal;&quot;&gt;(class, extensible)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;Ultimately, I&#39;m expecting that a running &lt;code&gt;MiniAppServer&lt;/code&gt; 
        instance will require a router-object: something that the recieved 
        requests are handed off to, that then spins off an appropriate 
        &lt;code&gt;IsEndpoint&lt;/code&gt;-derived instance to handle the request and 
        return results to the client. Whether this should be a class in and 
        of itself, or a nominal abstract class that developers should derive 
        their own classes from, I&#39;m not sure yet. There are potential 
        advantages to both, and I&#39;m very much on the fence as to which way 
        to go with it.&lt;/dd&gt;
&lt;dt&gt;ErrorEndpoint &lt;span style=&quot;font-weight: normal;&quot;&gt;(class, extensible, 
        derives from &lt;code&gt;IsEndpoint&lt;/code&gt; and &lt;code&gt;HasApplication&lt;/code&gt;)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;An &lt;code&gt;IsEndpoint&lt;/code&gt;-derived class that will be handed control 
        over an incoming request when/if an error occurs.&lt;/dd&gt;
&lt;dt&gt;FileEndpoint &lt;span style=&quot;font-weight: normal;&quot;&gt;(class, final, 
        derives from &lt;code&gt;IsEndpoint&lt;/code&gt; and &lt;code&gt;HasApplication&lt;/code&gt;)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;An &lt;code&gt;IsEndpoint&lt;/code&gt;-derived class that will handle looking 
        up static files for responses to requests that don&#39;t get caught by 
        a functional &lt;code&gt;IsEndpoint&lt;/code&gt; instance first.&lt;/dd&gt;
&lt;dt&gt;MiniAppServer &lt;span style=&quot;font-weight: normal;&quot;&gt;(class, extensible, 
        implements &lt;code&gt;IsApplication&lt;/code&gt;)&lt;/span&gt;&lt;/dt&gt;
&lt;dd&gt;The class that&#39;s actually responsible for generating and starting 
        a server instance.&lt;/dd&gt;

&lt;/dl&gt;
Working my way from the top of this list down seems a rational approach, at least for now...</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/3743661774630081072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/design-for-miniappserver-idea.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3743661774630081072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3743661774630081072'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/design-for-miniappserver-idea.html' title='Design for the MiniAppServer idea'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-4308707154817233586</id><published>2011-11-18T04:30:00.000-08:00</published><updated>2011-11-18T04:30:02.035-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="local applications"/><category scheme="http://www.blogger.com/atom/ns#" term="MiniAppServer"/><category scheme="http://www.blogger.com/atom/ns#" term="requirements"/><category scheme="http://www.blogger.com/atom/ns#" term="web-applications"/><title type='text'>Requirements for the MiniAppServer idea</title><content type='html'>So, while I&#39;m trying to re-familiarize myself with the DBFSpy stack, I&#39;m gonna 
capture some of the requirements ideas for the free-standing, Python-based mini-web-app-server 
idea that I mentioned in my last post. That entire string (free-standing, Python-based 
mini-web-app-server) is a bit much to type over and over again, it doesn&#39;t lend 
itself well to a meaningful acronym or abbreviation, and all of the logical names 
I can think of are already taken (Servelets, Applets, etc.), so I&#39;m going to call 
it MiniAppServer, at least for now...&lt;br /&gt;
&lt;br /&gt;
I mentioned that the idea came to me while I was working on a Python project at 
work. Without going into too much detail on that project (since I&#39;m not sure if I 
can, legally), it basically boiled down to a free-standing Python-based service that 
would periodically query information from a remote logging service, potentially 
perform some sort of examination of the data returned, and respond if certain 
criteria were met (sending an email if our 404-count exceeded some threshold, 
for example).&lt;br /&gt;
&lt;br /&gt;
One of the requirements for that project was that it be &quot;configurable.&quot; There 
wasn&#39;t much in the way of definition for what &quot;configurable&quot; meant, so I started 
with the default of using the &lt;code&gt;&lt;a href=&quot;http://docs.python.org/Flibrary/configparser.html&quot;&gt;ConfigParser&lt;/a&gt;&lt;/code&gt; 
module&#39;s offerings to retrieve configuration information from a fairly typical 
configuration-file. While on the bus back home one night, it suddently occurred 
to me that if configuration-changes needed to be made, that would entail stopping 
the service, changing the configuration, and re-starting it. Admittedly, it might 
not happen in that order; given the shape of the code I came up with, altering the 
config-file, &lt;i&gt;then&lt;/i&gt; restarting the service would have worked as well. But that 
started me thinking &quot;what if there were a way to have the service &lt;i&gt;also&lt;/i&gt; 
generate a web-based configuration-interface. Something that would allow IT staff 
to simply hit some URL and make changes to the configured values in real-time.&lt;br /&gt;
&lt;br /&gt;
That was the impetus for MiniAppServer.&lt;br /&gt;
&lt;br /&gt;
Whether the original idea for it&#39;s use becomes valid or not (the project at work 
was effectively cancelled, and I mothballed all of the code for it, following my 
previous recommendations as much as I could), I can &lt;i&gt;certainly&lt;/i&gt; come up with 
some uses for such a thing for my own ends, so I&#39;m adding it to my stack of 
projects.&lt;br /&gt;
&lt;br /&gt;
So, what are &lt;code&gt;MiniAppServer&lt;/code&gt;&#39;s requirements?
    &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;It needs to be able to run on any machine (with a full Python 
            installation being the only requirement);&lt;/li&gt;
&lt;li&gt;URLs should be (for anyting that isn&#39;t a static-file request/response) something along the lines of http://host:port/ObjectType/Action[/Format], where:
&lt;dl&gt;
&lt;dt&gt;ObjectType&lt;/dt&gt;
&lt;dd&gt;is the name of an object-type (perhaps a class, perhaps not)&lt;/dd&gt;
&lt;dt&gt;Action&lt;/dt&gt;
&lt;dd&gt;is some sort of action to be taken with the object-type. Some likely candidates include:
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;List&lt;/b&gt; - show a list of available items of the applicable object-type, probably as links to a &lt;b&gt;View&lt;/b&gt; for the individual items;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;View&lt;/b&gt; - Show a detailed view of a specific instance of an object-type;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Edit&lt;/b&gt; or &lt;b&gt;Update&lt;/b&gt; - make and submit changes to one instance of a given object-type;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Create&lt;/b&gt; or &lt;b&gt;New&lt;/b&gt; - create a new instance of an object-type (which may or may not be substantially different that the &lt;b&gt;Edit&lt;/b&gt; form/view);&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Delete&lt;/b&gt; - Delete a single instance of an object-type&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;dt&gt;Format&lt;/dt&gt;
&lt;dd&gt;is (maybe) a format to return - XML, JSON, or whatever else. I cannot come up with a real use for this that justifies it to my satisfaction, and perhaps allowing a query-string (e.g., ?format=xml) instead would be better. I&#39;ll noodle on this for a while, I think;&lt;/dd&gt;
&lt;/dl&gt;
Some examples might include (for a User object-type):
&lt;ul&gt;
&lt;li&gt;http://localhost:8192/User/List&lt;/li&gt;
&lt;li&gt;http://localhost:8192/User/View (this would need some sort of ID to be passed, maybe as an additional path element [.../View/USERID] or as a query-string)&lt;/li&gt;
&lt;li&gt;http://localhost:8192/User/Edit (same comment as above)&lt;/li&gt;
&lt;li&gt;http://localhost:8192/User/Create&lt;/li&gt;
&lt;li&gt;http://localhost:8192/User/Delete&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;It has to handle GET and POST requests, in order to support complete HTML form-interaction;
            &lt;ul&gt;
&lt;li&gt;It needs to be able to handle &quot;simple&quot; POST submissions (e.g., text-only);&lt;/li&gt;
&lt;li&gt;It will likely need to be able to handle binary upload-capable POST submissions as well, though that may not be an immediate requirement.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;It needs to be as small as possible - there&#39;s no point in having a whole lot of overhead for an application-server of this sort when a LAMP stack could just as easily be built with mod-python;&lt;/li&gt;
&lt;li&gt;At the same time, it needs to be highly extensible;&lt;/li&gt;
&lt;li&gt;It must be able to serve static files as well (for things like images, stylesheets and JavaScript);&lt;/li&gt;
&lt;li&gt;It should be something that could be set up to run as a service on a machine, &lt;i&gt;or&lt;/i&gt; used as a local instance of an application.&lt;/li&gt;
&lt;/ul&gt;
Some &quot;nice-to-have&quot; features include:
    &lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;It should support as much of a &lt;a href=&quot;http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&quot;&gt;Model-view-controller&lt;/a&gt; architecture/structure as is feasible;&lt;/li&gt;
&lt;li&gt;It would be neat if the entire application-site (if there are any non-Python files associated, at least) could be packaged up as a single zip-file or tarball;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/4308707154817233586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/requirements-for-miniappserver-idea.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4308707154817233586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/4308707154817233586'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/requirements-for-miniappserver-idea.html' title='Requirements for the MiniAppServer idea'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-2895256589281739314</id><published>2011-11-16T04:30:00.000-08:00</published><updated>2011-11-16T04:30:01.105-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="DBFSpy"/><category scheme="http://www.blogger.com/atom/ns#" term="IFilesystem"/><category scheme="http://www.blogger.com/atom/ns#" term="IFilesystemItem"/><category scheme="http://www.blogger.com/atom/ns#" term="mothball"/><category scheme="http://www.blogger.com/atom/ns#" term="UML"/><title type='text'>How to mothball a project</title><content type='html'>Part of the reason that the last several posts I&#39;ve made since I returned from my hiatus have not been obviously related to the DBFSpy project that I started this blog with is that in the intervening couple of months, I&#39;ve forgotten a lot of what I&#39;d intended to &lt;i&gt;do&lt;/i&gt; with it.&lt;br /&gt;
&lt;br /&gt;
Fortunately, I had documented at least a sequence of classes, interfaces, etc., that I&#39;d expected to cover, so it&#39;s not like I&#39;m completely lost. At the same time, though, I hadn&#39;t gotten even as far as stubbing out properties and methods for the remaining items before things got out of control, and I had no time to continue posting.&lt;br /&gt;
&lt;br /&gt;
When projects are in danger of needing to be &lt;a href=&quot;http://en.wikipedia.org/wiki/Mothball&quot;&gt;mothballed&lt;/a&gt;, I like to try and spend as much time as I can get away with making sure that I capture as much of my thought-process as I can before I have to set it aside. I don&#39;t always get as much of this kind of time as I&#39;d like, though, even when the project in question is my own.&lt;br /&gt;
&lt;br /&gt;
Case in point: All I &lt;i&gt;really&lt;/i&gt; have on DBFSpy is a list of classes and interfaces I was expecting, and some indication that they were nominally complete (though maybe not with full unit-tests, etc.):&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;BaseDatabaseConnection (abstract class, complete)&lt;/li&gt;
&lt;li&gt;BaseDataObject (abstract class, complete)&lt;/li&gt;
&lt;li&gt;BaseFileSystemItemCache (abstract class, complete)&lt;/li&gt;
&lt;li&gt;IFilesystem (interface, complete)&lt;/li&gt;
&lt;li&gt;IFilesystemItem (interface)&lt;/li&gt;
&lt;li&gt;IFileystemOperators (interface)&lt;/li&gt;
&lt;li&gt;MySQLDatasource (class)&lt;/li&gt;
&lt;li&gt;MySQLFileItem (class)&lt;/li&gt;
&lt;li&gt;MySQLFilesystem (class)&lt;/li&gt;
&lt;li&gt;Unit-tests for classes, etc.&lt;/li&gt;
&lt;li&gt;dbfspy (executable)&lt;/li&gt;
&lt;/ul&gt;
Looking at older posts here, it &lt;i&gt;looks&lt;/i&gt; like I was about on track with this list. I also managed to create and post the &lt;a href=&quot;http://my-brain-on-python.blogspot.com/2011/08/high-level-dbfspy-uml-diagram.html&quot;&gt;UML diagram&lt;/a&gt;, which I hope will refresh my memory somewhat as I start digging into that project again. Though now I suspect I&#39;ll need to review my progress (it looks like I had to break off after IFilesystem, bu before IFilesystemItem). With some luck, given my new insights into testing, and the discovery of properties and methods that it facilitates, I&#39;ll be able to recapture a lot of my previous momentum, and bang things out quickly. If not, I guess I&#39;ll have to think about either setting it aside again (with the expectation that it&#39;ll be so full of holes by the time I pick it up again that I&#39;ll be starting from scratch), or just biting that bullet &lt;i&gt;now&lt;/i&gt;, and re-starting.&lt;br /&gt;
&lt;br /&gt;
So, chalk this up to another lesson learned - &lt;i&gt;take the time to document and comment as you go&lt;/i&gt;. You never know when you may have to set a project aside, and if there isn&#39;t time to gracefully (and thoroughly) mothball it, it may very well be full of holes by the time you get back to it.&lt;br /&gt;
&lt;br /&gt;
What I think I&#39;d advocate is (at the &lt;i&gt;very&lt;/i&gt; least):&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Make sure all decisions are documented;&lt;/li&gt;
&lt;li&gt;If possible, make sure that any class-diagrams are complete as of the time the project had to be stopped;&lt;/li&gt;
&lt;li&gt;Make sure that the design is thorough, documented, and annotated as needed to keep track of what the design intended to achieve, and what modifications had to be made, if any.&lt;/li&gt;
&lt;/ul&gt;
There may be other items as well, but these are, I hope, the major items... &lt;br /&gt;
&lt;br /&gt;
Another part of the reason I spent the last few posts going over new stuff (apart from my enthusiasm for the topic therein), was that as I was working through a Python project at work, it occurred to me that it might be a cool idea to generate a small, freestanding, Python-based web-application server framework. Nothing huge, you understand, but rather something that could be incorporated into other Python projects, to allow for things like administrative interfaces for services and the like.&lt;br /&gt;
&lt;br /&gt;
More on that topic later, I think...</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/2895256589281739314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/how-to-mothball-project.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/2895256589281739314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/2895256589281739314'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/how-to-mothball-project.html' title='How to mothball a project'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-3915925656866838305</id><published>2011-11-14T04:30:00.000-08:00</published><updated>2011-11-14T04:30:02.480-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="abstract class"/><category scheme="http://www.blogger.com/atom/ns#" term="container"/><category scheme="http://www.blogger.com/atom/ns#" term="dictionary"/><category scheme="http://www.blogger.com/atom/ns#" term="list"/><category scheme="http://www.blogger.com/atom/ns#" term="strongly typed"/><category scheme="http://www.blogger.com/atom/ns#" term="tuple"/><category scheme="http://www.blogger.com/atom/ns#" term="utilities"/><title type='text'>The TypedContainers Module</title><content type='html'>&lt;b&gt;Code-sharing:&lt;/b&gt; &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/TypedContainers.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/TypedContainers.py&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
One of the other items I came up with during my month-or-so-long hiatus from blogging about My Brain on Python was spurred by a perceived need to have some container objects that were, for lack of a better term, strongly typed as far as it&#39;s values were concerned. Python (relying on duck-typing, as previously noted) doesn&#39;t provide such things out of the box, but since it&#39;s a very capable dynamic language, it proved to be fairly simple to implement these.&lt;br /&gt;
&lt;br /&gt;
My initial item in this realm was a strongly-typed Dictionary equivalent - something that would allow a developer to define a dictionary-like data-structure that would allow &lt;i&gt;only&lt;/i&gt; items that were of a give type or one of a group of types to be added to it. It&#39;s certainly possible to perform that kind of type-checking through a class-method before adding an item to a dictionary property, but if the dictionary itself is publicly accessible, there&#39;s no good way that I can see to prevent code from being written that would skip any such type-checking, and, for example, add a string member to a dictionary that was intended to contain only numbers.&lt;br /&gt;
&lt;br /&gt;
A Python purist might very well (and with some justification, perhaps) say that this isn&#39;t really an issue - in the case of an error arising at runtime, an exception would be raised, and the maintainer of the code would have to track that down and fix it. I maintain that, while that is certainly true enough, it&#39;d be easier to track the &lt;i&gt;source&lt;/i&gt; of the error down if the simple act of &lt;i&gt;trying&lt;/i&gt; to insert the errant value into the container caused the error, rather than waiting until it caused some other issue later in the execution of the code. Maybe a &lt;b&gt;lot&lt;/b&gt; later.&lt;br /&gt;
&lt;br /&gt;
So, a TypedDictionary. At first, it was free-standing, but as I thought through the potential for needing similar strongly-typed containers based off of lists or tuples, or whatever else might arise, I decided to abstract some of the functionality out into a nominal abstract class. The results (minus the stub-code for TypedList and TypedTuple, which I haven&#39;t dug into yet) look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;# TypedCollections.py
&quot;&quot;&quot;Provides strongly-typed extensions to Python collection-types (lists, dictionaries, tuples, etc.).&quot;&quot;&quot;

__author__  = &#39;Brian D. Allbee&#39;
__licence__ = &#39;http://creativecommons.org/licenses/by-sa/3.0/&#39;
__version__ = &#39;0.2&#39;
__copyright__ = &quot;Copyright 2011, Brian D. Allbee&quot;
__credits__ = [&quot;Brian D. Allbee&quot;]
__maintainer__ = &quot;Brian D. Allbee&quot;
__email__ = &quot;brian.allbee@gmail.com&quot;
__status__ = &quot;Development&quot;

#####################################
# Build an &quot;__all__&quot; list to        #
# support                           #
# &quot;from Namespace.blah import *&quot;    #
# syntax                            #
#####################################
__all__ = []

#####################################
# Required imports                  #
#####################################

from DocMeta import *
import types

#####################################
# Constants defined in the module   #
#####################################

AllTypes = []
for typeName in dir(types):
    if not typeName in [ &#39;ClassType&#39;, &#39;TypeType&#39;, &#39;InstanceType&#39;, &#39;ObjectType&#39; ] and not typeName[0] == &#39;_&#39;:
        AllTypes.append( eval( &#39;types.%s&#39; % ( typeName ) ) )

__all__ += [ &#39;AllTypes&#39; ]

#####################################
# Functions defined in the module   #
#####################################

#####################################
# Interfaces defined in the module  #
#####################################

#####################################
# Abstract Classes defined in the   #
# module                            #
#####################################

class BaseTypedCollection( object ):
    &quot;&quot;&quot;Provides baseline functionality, interface requirements, and type-identity for derived items that can have a MemberTypes property.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _GetMemberTypes( self ):
        &quot;&quot;&quot;Gets the set of object-types allowed as members in the collection.&quot;&quot;&quot;
        return self._memberTypes

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;(Iterable, required) An iterable collection of items to replace the current member-types with&#39; )
    def _SetMemberTypes( self, value ):
        &quot;&quot;&quot;Sets the set of object-types allowed as members in the collection.&quot;&quot;&quot;
        self._memberTypes = []
        for item in value:
            # Basic types, excluding ClassType, TypeType, etc.
            if item in AllTypes:
                self._memberTypes.append( item )
            # Classic classes
            elif type( item ) == types.ClassType:
                raise ValueError( &#39;%s.MemberTypes cannot accept classic classes (%s) as member-types&#39; % ( self.__class__.__name__, item ) )
            # New-style classes
            elif type( item ) == types.TypeType:
                self._memberTypes.append( item )
            # Unrecognized!
            else:
                raise ValueError( &#39;%s.MemberTypes cannot accept %s as a member-type&#39; % ( self.__class__.__name__, item ) )

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def _DelMemberTypes( self ):
        &quot;&quot;&quot;Deletes the set of object-types allowed as members in the collection.&quot;&quot;&quot;
        self._memberTypes = []

    ##################################
    # Class Properties               #
    ##################################

    MemberTypes = property( _GetMemberTypes, None, None, &#39;Gets the set of object-types allowed as members in the collection.&#39; )

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __init__( self ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally abstract: Don&#39;t allow instantiation of the class
        if self.__class__ == BaseTypedCollection:
            raise NotImplementedError( &#39;BaseTypedCollection is (nominally) an abstract class, and is not intended to be instantiated.&#39; )
        self._DelMemberTypes()

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The value to check.&#39; )
    def IsValidMemberType( self, value ):
        &quot;&quot;&quot;Determines whether the supplied value is a valid type to be allowed in the collection-object.&quot;&quot;&quot;
        if hasattr( value.__class__, &#39;__mro__&#39; ):
            compareTypes = set( value.__class__.__mro__ )
            return ( len( set( self._memberTypes ).intersection( compareTypes ) ) &amp;gt; 0 )
        if hasattr( value.__class__, &#39;__bases__&#39; ):
            compareTypes = set( value.__class__.__bases__ )
            return ( len( set( self._memberTypes ).intersection( compareTypes ) ) &amp;gt; 0 )
        raise TypeError( &#39;%s.IsValidMemberType could not determine either an MRO for, or the bases of the %s value&#39; % ( self.__class__.__name__, value ) )

__all__ += [ &#39;BaseTypedCollection&#39; ]

#####################################
# Classes defined in the module     #
#####################################

class TypedDictionary( BaseTypedCollection, dict ):
    &quot;&quot;&quot;Represents a strongly-typed Dictionary.&quot;&quot;&quot;

    #################################
    # Class Attributes              #
    #################################

    #################################
    # Class Property-Getter Methods #
    #################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;memberTypes&#39;, None, &#39;(Iterable, required) An iterable collection of item-types to be allowed as members in the dictionary.&#39; )
    def __init__( self, memberTypes ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
        if self.__class__ != TypedDictionary:
            raise NotImplementedError( &#39;TypedDictionary is (nominally) a final class, and is not intended to be derived from.&#39; )
        BaseTypedCollection.__init__( self )
        self._SetMemberTypes( memberTypes )

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    @DocumentArgument( &#39;argument&#39;, &#39;name&#39;, None, &#39;The key-name for the element once it\&#39;s added to the dictionary.&#39; )
    @DocumentArgument( &#39;argument&#39;, &#39;value&#39;, None, &#39;The element-name to add to the dictionary.&#39; )
    def __setitem__( self, name, value ):
        &quot;&quot;&quot;Wraps the default __setitem__ in order to type-check the value before allowing it to be added.&quot;&quot;&quot;
        if not self.IsValidMemberType( value ):
            raise TypeError( &#39;TypedDictionary instance expects an instance of any of %s.&#39; % ( self._memberTypes ) )
        dict.__setitem__( self, name, value )

__all__ += [ &#39;TypedDictionary&#39; ]

#####################################
# Package sub-modules and -packages #
#####################################

#####################################
# Unit-test the module on main      #
#####################################

if __name__ == &#39;__main__&#39;:
    import inspect, os, sys, unittest
    from UnitTestUtilities import *

    testSuite = unittest.TestSuite()
    testResults = unittest.TestResult()

    ##################################
    # Unit-test Constants            #
    ##################################

    class testConstants( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the constants defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testConstants ) )

    ##################################
    # Unit-test Functions            #
    ##################################

    ##################################
    # Unit-test Interfaces           #
    ##################################

    ##################################
    # Unit-test Abstract Classes     #
    ##################################

    class BaseTypedCollectionDerived( BaseTypedCollection ):
        def __init__( self ):
            BaseTypedCollection.__init__( self )

    class AClassicMember: pass

    class AnotherClassicMember: pass

    class ADerivedClassicMember( AClassicMember ): pass

    class ATypedMember( object ): pass

    class AnotherTypedMember( object ): pass
    
    class ADerivedTypedMember( ATypedMember, object ): pass

    class testBaseTypedCollection( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the BaseTypedCollection class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testAbstract( self ):
            &quot;&quot;&quot;Testing abstract nature of the BaseTypedCollection class.&quot;&quot;&quot;
            try:
                testObject = BaseTypedCollection()
                self.fail( &#39;BaseTypedCollection is nominally an abstract class and should not allow instantiation.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to instantiate a BaseTypedCollection should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )

        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the BaseTypedCollection class.&quot;&quot;&quot;
            # Single type
            testObject = BaseTypedCollectionDerived()
            self.assertTrue( isinstance( testObject, BaseTypedCollection ), &#39;Constructed BaseTypedCollection-derived objects should be instances of BaseTypedCollection.&#39; )

        def testSimpleTypes( self ):
            &quot;&quot;&quot;Testing the use of simple value-types as member-types.&quot;&quot;&quot;
            testTypesList = [
                [ types.StringType, ],
                [ types.IntType, types.FloatType, types.LongType ],
                ]
            for testTypes in testTypesList:
                testObject = BaseTypedCollectionDerived()
                testObject._SetMemberTypes( testTypes )
                self.assertEquals( testObject.MemberTypes, testTypes, &#39;Getting member-types %s should return the types set %s&#39; % ( testObject.MemberTypes, testTypes ) )

        def testSimpleInstances( self ):
            &quot;&quot;&quot;Testing the use of simple value-types as member-types.&quot;&quot;&quot;
            testTypesList = [
                [ &#39;ook&#39;, ],
                [ 1, 1L, 1.0 ],
                ]
            for testTypes in testTypesList:
                testObject = BaseTypedCollectionDerived()
                try:
                    testObject._SetMemberTypes( testTypes )
                    self.fail( &#39;Simple values %s should not be allowed as member-types&#39; % ( testTypes ) )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Attempting to use %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( testTypes, error.__class__.__name__, error ) )

        def testClassicClassTypes( self ):
            &quot;&quot;&quot;Testing the use of classic classes as member-types.&quot;&quot;&quot;
            testTypesList = [
                [ AClassicMember, ],
                [ AClassicMember, AnotherClassicMember ],
                ]
            for testTypes in testTypesList:
                testObject = BaseTypedCollectionDerived()
                try:
                    testObject._SetMemberTypes( testTypes )
                    self.fail( &#39;Classic classes %s should not be allowed as member-types&#39; % ( testTypes ) )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Attempting to use %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( testTypes, error.__class__.__name__, error ) )

        def testClassicClassInstances( self ):
            &quot;&quot;&quot;Testing the use of classic classes as member-types.&quot;&quot;&quot;
            testTypesList = [
                [ AClassicMember(), ],
                [ AClassicMember(), AnotherClassicMember() ],
                ]
            for testTypes in testTypesList:
                testObject = BaseTypedCollectionDerived()
                try:
                    testObject._SetMemberTypes( testTypes )
                    self.fail( &#39;Simple values %s should not be allowed as member-types&#39; % ( testTypes ) )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Attempting to use %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( testTypes, error.__class__.__name__, error ) )

        def testNewClassTypes( self ):
            &quot;&quot;&quot;Testing the use of classic classes as member-types.&quot;&quot;&quot;
            testTypesList = [
                [ ATypedMember, ],
                [ ATypedMember, AnotherTypedMember ],
                ]
            for testTypes in testTypesList:
                testObject = BaseTypedCollectionDerived()
                testObject._SetMemberTypes( testTypes )
                self.assertEquals( testObject.MemberTypes, testTypes, &#39;Getting member-types %s should return the types set %s&#39; % ( testObject.MemberTypes, testTypes ) )

        def testNewClassInstances( self ):
            &quot;&quot;&quot;Testing the use of classic classes as member-types.&quot;&quot;&quot;
            testTypesList = [
                [ ATypedMember(), ],
                [ ATypedMember(), AnotherTypedMember() ],
                ]
            for testTypes in testTypesList:
                testObject = BaseTypedCollectionDerived()
                try:
                    testObject._SetMemberTypes( testTypes )
                    self.fail( &#39;Instance-values %s should not be allowed as member-types&#39; % ( testTypes ) )
                except ValueError:
                    pass
                except Exception, error:
                    self.fail( &#39;Attempting to use %s should raise a ValueError, but %s was raised instead:\n  %s&#39; % ( testTypes, error.__class__.__name__, error ) )

        def testProperties( self ):
            &quot;&quot;&quot;Testing the properties of the BaseTypedCollection class.&quot;&quot;&quot;
            testObject = BaseTypedCollectionDerived()
            # MemberTypes (get)
            self.assertEquals( testObject.MemberTypes, [], &#39;The default MemberTypes value should be an empty list.&#39; )
            try:
                testObject.MemberTypes = None
                self.fail( &#39;The abstract property MemberTypes should not be settable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to set the MemberTypes property should raise a AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
            try:
                del testObject.MemberTypes
                self.fail( &#39;The abstract property MemberTypes should not be deletable&#39; )
            except AttributeError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to delete the MemberTypes property should raise a AttributeError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
    
        def testMethods( self ):
            &quot;&quot;&quot;Testing the methods of the BaseTypedCollection class.&quot;&quot;&quot;
            testObject = BaseTypedCollectionDerived()
            # IsValidMemberType
            # - Simple type
            testObject._SetMemberTypes( [ int ] )
            goodValues = [ -1, 0, 1, 2 ]
            for value in goodValues:
                self.assertTrue( testObject.IsValidMemberType( value ), &#39;The value %s should be a good value.&#39; % ( value ) )
            badValues = [ None, &#39;ook&#39;, 1L, 1.0 ]
            for value in badValues:
                self.assertFalse( testObject.IsValidMemberType( value ), &#39;The value %s should be a bad/invalid value.&#39; % ( value ) )
            # - Multiple simple types
            testObject._SetMemberTypes( [ int, float, long ] )
            goodValues = [ -1, 0, 1, 2, -1.0, 0.0, 1.0, 2.0, -1L, 0L, 1L, 2L ]
            for value in goodValues:
                self.assertTrue( testObject.IsValidMemberType( value ), &#39;The value %s (%s) should be a good value.&#39; % ( value, type( value ) ) )
            badValues = [ None, &#39;ook&#39;, list(), dict(), tuple() ]
            for value in badValues:
                self.assertFalse( testObject.IsValidMemberType( value ), &#39;The value %s (%s) should be a bad/invalid value.&#39; % ( value, type( value ) ) )
            # - New-style class type
            testObject._SetMemberTypes( [ ATypedMember ] )
            goodValues = [ ATypedMember(), ADerivedTypedMember() ]
            for value in goodValues:
                self.assertTrue( testObject.IsValidMemberType( value ), &#39;The value %s (%s) should be a good value.&#39; % ( value, type( value ) ) )
            badValues = [ None, &#39;ook&#39;, 1, list(), tuple(), dict(), AClassicMember(), AnotherTypedMember() ]
            for value in badValues:
                self.assertFalse( testObject.IsValidMemberType( value ), &#39;The value %s (%s) should be a bad/invalid value.&#39; % ( value, type( value ) ) )
            # - Multiple new-style class type
            testObject._SetMemberTypes( [ ATypedMember, AnotherTypedMember ] )
            goodValues = [ ATypedMember(), ADerivedTypedMember(), AnotherTypedMember() ]
            for value in goodValues:
                self.assertTrue( testObject.IsValidMemberType( value ), &#39;The value %s (%s) should be a good value.&#39; % ( value, type( value ) ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testBaseTypedCollection ) )

    ##################################
    # Unit-test Classes              #
    ##################################

    class TypedDictionaryDerived( TypedDictionary ):
        def __init__( self, memberTypes ):
            TypedDictionary.__init__( self, memberTypes )
    
    class testTypedDictionary( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the TypedDictionary class.&quot;&quot;&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
        def testFinal( self ):
            &quot;&quot;&quot;Testing final nature of the TypedDictionary class.&quot;&quot;&quot;
            try:
                testObject = TypedDictionaryDerived( [ types.StringType, ] )
                self.fail( &#39;TypedDictionary is nominally a final class and should not allow derivation.&#39; )
            except NotImplementedError:
                pass
            except Exception, error:
                self.fail( &#39;Attempting to derive from TypedDictionary should raise a NotImplementedError, but %s was raised instead:\n  %s&#39; % ( error.__class__.__name__, error ) )
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the TypedDictionary class.&quot;&quot;&quot;
            testObject = TypedDictionary( [ int, long ] )
            self.assertTrue( isinstance( testObject, TypedDictionary ), &#39;Tautological imperative: A TypedDictionary is a TypedDictionary...!&#39; )
    
        def testDictionary( self ):
            &quot;&quot;&quot;Testing the dictionary capabilities of the TypedDictionary class.&quot;&quot;&quot;
            # PropertyName
            testObject = TypedDictionary( [ int, long ] )
            goodNames = [ &#39;ook&#39;, &#39;1&#39;, 1, &#39;&#39;, None, TypedDictionaryDerived ]
            goodValues = [ -1, 0, 1, 2, -1L, 0L, 1L, 2L ]
            for name in goodNames:
                for value in goodValues:
                    testObject[ name ] = value
                    self.assertEquals( testObject[ name ], value, &#39;The value set (%s) should be returned at the name (%s)&#39; % ( value, name ) )
            badValues = [ None, -1.0, 0.0, 1.0, 2.0, &#39;ook&#39; ]
            for name in goodNames:
                for value in badValues:
                    try:
                        testObject[ name ] = value
                        self.fail( &#39;The value %s should not be allowed in the dictionary&#39; % ( value ) )
                    except TypeError:
                        pass
                    except Exception, error:
                        self.fail( &#39;Setting an invalid value (%s) in a TypedDictionary should raise a TypeError, but %s was raised.\n  %s&#39; % ( value, error.__class__.__name__, error ) )
    
        def testProperties( self ):
            &quot;&quot;&quot;Testing the properties of the TypedDictionary class.&quot;&quot;&quot;
            # No local properties to test (see BaseTypedCollection)
            pass
    
        def testMethods( self ):
            &quot;&quot;&quot;Testing the methods of the TypedDictionary class.&quot;&quot;&quot;
            # No local methods to test (see BaseTypedCollection)
            pass

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testTypedDictionary ) )

    # Run unit-tests and install
    print &#39;#&#39;*79
    print &#39;Running unit-tests&#39;
    testSuite.run( testResults )
    print &#39;#&#39;*79
    if testResults.errors == [] and testResults.failures == []:
        print &#39;All unit-tests passed! Ready for build/copy!&#39;
        import shutil
        sitePackagesDirs = []
        for path in sys.path:
            if path.split( os.sep )[-1].lower() == &#39;site-packages&#39;:
                sitePackagesDirs.append( path )
        for path in sitePackagesDirs:
            destinationFile = path + os.sep + __file__
            try:
                shutil.copyfile( &#39;./%s&#39; % __file__, destinationFile )
                print &#39;Copied %s to %s&#39; % ( __file__, path )
            except:
                print &#39;Could not copy file %s to location %s&#39; % ( __file__, path )
    else:
        print &#39;Unit-tests failed (see items below)&#39;
        print &#39;#&#39;*79
        if testResults.errors:
            print &#39;Errors&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for error in testResults.errors:
                print error[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
        if testResults.failures:
            if testResults.errors:
                print &#39;#&#39;*79
            print &#39;Failures&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for failure in testResults.failures:
                print failure[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
    print &#39;#&#39;*79&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;Commentary:&lt;/b&gt;
&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;Line(s)&lt;/dt&gt;
&lt;dt&gt;26, 32-37&lt;/dt&gt;
&lt;dd&gt;I&#39;m using the &quot;public&quot; members of the &lt;code&gt;types&lt;/code&gt; module to generate a list of all of the available public types, &lt;i&gt;except&lt;/i&gt; those that can be defined/re-defined by the developer (e.g., &lt;code&gt;ClassType&lt;/code&gt; and &lt;code&gt;TypeType&lt;/code&gt;, for classic- and new-style-classes, respectively, plus &lt;code&gt;InstanceType&lt;/code&gt; and &lt;code&gt;ObjectType&lt;/code&gt; so that we don&#39;t later try to use instances of either class-type as member-types). This code feels a bit strange to me, now that I look at it again, and though I &lt;i&gt;know&lt;/i&gt; I had a reason for this approach, I cannot, for the life of me, remember what that reason was now. I&#39;ll have to ponder on it, but at the very least, it&#39;s functional.&lt;/dd&gt;
&lt;dt&gt;52-138&lt;/dt&gt;
&lt;dd&gt;The nominal abstract class that &lt;code&gt;TypedDictionary&lt;/code&gt; and (eventually) the other typed containers are based on. It&#39;s primary purpose is to provide a consistent &lt;code&gt;MemberTypes&lt;/code&gt; property for all of the derived container-classes it&#39;ll be a subclass of, but it &lt;i&gt;also&lt;/i&gt; provides a mechanism (&lt;code&gt;IsValidmemberType&lt;/code&gt;) to detect whether a given item is legitimately a member of the container.
  &lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;Line(s)&lt;/dt&gt;
&lt;dt&gt;63-66, 72-89, 95-98, 104&lt;/dt&gt;
&lt;dd&gt;Property getter, setter, and deleter method definitions, and the formal property declaration for the MemberTypes property.&lt;/dd&gt;
   &lt;dd&gt;Note that &lt;code&gt;_SetMemberTypes&lt;/code&gt; checks for basic types (lines 79-80), classic-class types (82-83), which aren&#39;t allowed at present because I haven&#39;t found a reliable way to detect whether their classes are subclasses of other (classic) classes, and new-style classes (85-86). Although I cannot imagine that there would be any other types that would come up (other than the instance types noted above), I&#39;ve assured that the code will raise an error (88-89) if such a case occurs.&lt;/dd&gt;
&lt;dt&gt;126-136&lt;/dt&gt;
&lt;dd&gt;The mechanism mentioned for determining whether an item is a valid member of the container, by checking against it&#39;s defined member-types using Python&#39;s &lt;code&gt;set&lt;/code&gt; data-types.&lt;/dd&gt;
  &lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;144-198&lt;/dt&gt;
&lt;dd&gt;The actual &lt;code&gt;TypedDictionary&lt;/code&gt; definition. There really isn&#39;t much to it, apart from the constructor and the overload of &lt;code&gt;__setitem__&lt;/code&gt;:&lt;/dd&gt;
 &lt;dd&gt;For hopefully-obvious reasons, the constructor requires that an iterable argument of allowed member-types is supplied. It gets passed directly to the inherited &lt;code&gt;_SetMemberTypes&lt;/code&gt; method we&#39;ve already seen, which is &lt;i&gt;not&lt;/i&gt; exposed publicly.&lt;/dd&gt;
 &lt;dd&gt;&lt;code&gt;__setitem__&lt;/code&gt; &lt;i&gt;appeared&lt;/i&gt; to be the only method of the base &lt;code&gt;dict&lt;/code&gt; class that needed to be overridden to type-ceck the value to be added before actually adding it. Rather than muck about with re-writing the base method, it&#39;s simply called after the valid-member-type check passes.&lt;/dd&gt;
 &lt;dd&gt;Since the whole point of the class is to raise a type error when an element is added that isn&#39;t of a valid type, that gets raised as well.&lt;/dd&gt;

&lt;/dl&gt;
The unit-tests may look a bit convoluted (they felt that way to me when I was writing them, to be sure). Ultimately, all they are trying to do, though, is to assure that the member-type checks are tested with all of the logical permutations:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Simple value-types (singly or multiply);&lt;/li&gt;
&lt;li&gt;New-style class-types (both directly or as subclasses of member-types); and&lt;/li&gt;
&lt;li&gt;Classic-class types (which should, as noted above, all fail at this time).&lt;/li&gt;
&lt;/ul&gt;
Bearing that in mind, I think they&#39;re actually pretty straightforward...</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/3915925656866838305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/typedcontainers-module.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3915925656866838305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3915925656866838305'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/typedcontainers-module.html' title='The TypedContainers Module'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-3665288195861401467</id><published>2011-11-11T04:30:00.000-08:00</published><updated>2011-11-11T04:30:11.622-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="annotations"/><category scheme="http://www.blogger.com/atom/ns#" term="decorators"/><category scheme="http://www.blogger.com/atom/ns#" term="documentation"/><category scheme="http://www.blogger.com/atom/ns#" term="meta-data"/><title type='text'>The DocMeta Module</title><content type='html'>&lt;b&gt;Shared-code location:&lt;/b&gt; &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/DocMeta.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/DocMeta.py&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Back on Monday, I promised I&#39;d share my &lt;code&gt;UnitTestUtilities&lt;/code&gt; and &lt;code&gt;DocMeta&lt;/code&gt; modules. Hopefully you&#39;ve already seen &lt;code&gt;UnitTestUtilities&lt;/code&gt;, since I posted that on Wednesday. Today&#39;s foray is into &lt;code&gt;DocMeta&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
I wrote &lt;code&gt;DocMeta&lt;/code&gt; quite some time back, in order to provide a simple, annotation-based (e.g., decorator-based) way of adding additional/more specific documentation-information to functions and methods. You&#39;ve already seen it in use in &lt;code&gt;UnitTestUtilities&lt;/code&gt;, at least from the code side of things, and if you&#39;ve tried to take a look at the pydoc output for it, it&#39;s probably barfed at you, saying that it couldn&#39;t find &lt;code&gt;DocMeta&lt;/code&gt;. &lt;br /&gt;
&lt;br /&gt;
What DocMeta does, is provide decorators that add information to the docstring of a function or method. Consider the &lt;code&gt;HasTestFor&lt;/code&gt; function from &lt;code&gt;UnitTestUtilities&lt;/code&gt;:

&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;@ToDo( &#39;Unit-test the function&#39; )
@ToDo( &#39;Document exceptions raised&#39; )
@ToDo( &#39;Value-check arguments&#39; )
@ToDo( &#39;Type-check arguments&#39; )
@DocumentArgument( &#39;argument&#39;, &#39;testCase&#39;, None, &#39;(unittest.TestCase, required) The TestCase instance to check for the named test-method in.&#39; )
@DocumentArgument( &#39;argument&#39;, &#39;itemName&#39;, None, &#39;(Class, required) The name of the property or method to look for a test-method for (&quot;test[itemName]&quot;).&#39; )
@DocumentReturn( types.BooleanType )
def HasTestFor( testCase, itemName ):
    &quot;&quot;&quot;Returns true if the testCase provided has a test-method named &#39;test[itemName]&#39;.&quot;&quot;&quot;&lt;/pre&gt;
&lt;br /&gt;
Without the decorators, the pydoc documentation for the function would look something like so:
&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.blogger.com/blogger.g?blogID=1660699321100518149&quot; name=&quot;-HasTestFor&quot;&gt;&lt;b&gt;HasTestFor&lt;/b&gt;&lt;/a&gt;(testCase, itemName)&lt;/dt&gt;
&lt;dd&gt;&lt;pre&gt;Returns true if the testCase provided has a test-method named &#39;test[itemName]&#39;.&lt;/pre&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
&lt;b&gt;With&lt;/b&gt; the decorators, the this is what it looks like:
&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;a href=&quot;http://www.blogger.com/blogger.g?blogID=1660699321100518149&quot; name=&quot;-HasTestFor&quot;&gt;&lt;b&gt;HasTestFor&lt;/b&gt;&lt;/a&gt;(testCase, itemName)&lt;/dt&gt;
&lt;dd&gt;&lt;pre&gt;Returns true if the testCase provided has a test-method named &#39;test[itemName]&#39;. 
  
################### 
## TO-DOs        ## 
################### 
 - Type-check arguments  
 - Value-check arguments  
 - Document exceptions raised  
 - Unit-test the function  
  
################### 
## Returns       ## 
################### 
bool 
  
################### 
## Arguments     ## 
################### 
 + testCase ............ (unittest.TestCase, required) The TestCase instance to  
                         check for the named test-method in.  
 + itemName ............ (Class, required) The name of the property or method  
                         to look for a test-method for (&quot;test[itemName]&quot;).&lt;/pre&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
The basic pattern that these decorators follow is to add the supplied meta-data to the item being decorated as protected attributes of the item (&lt;code&gt;_argumentDocumentation&lt;/code&gt;, &lt;code&gt;_configDocumentation&lt;/code&gt;, etc.), with helper functions to assure that the meta-data structure exists, and to re-generate the item&#39;s docstring as needed.&lt;br /&gt;
&lt;br /&gt;
As I contine working on this module, I&#39;m planning to add in classes and functionality to generate documentation as LaTeX/PDF, HTML and/or XML output from the additional meta-data, but for now, I&#39;m just going to share/show the code for the decorators...
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;# DocMeta.py
&quot;&quot;&quot;Provides common documentation-metadata decorators, that facilitate in-code 
documentation of functions, classes, their methods, etc.&quot;&quot;&quot;

__author__  = &#39;Brian D. Allbee&#39;
__licence__ = &#39;http://creativecommons.org/licenses/by-sa/3.0/&#39;
__version__ = &#39;0.2&#39;
__copyright__ = &quot;Copyright 2011, Brian D. Allbee&quot;
__credits__ = [&quot;Brian D. Allbee&quot;]
__maintainer__ = &quot;Brian D. Allbee&quot;
__email__ = &quot;brian.allbee@gmail.com&quot;
__status__ = &quot;Development&quot;

#####################################
# Build an &quot;__all__&quot; list to        #
# support                           #
# &quot;from Namespace.blah import *&quot;    #
# syntax                            #
#####################################
__all__ = []

#####################################
# Required imports                  #
#####################################

import inspect

#####################################
# Constants defined in the module   #
#####################################

SelfDocumentationString = &#39;The object-instance that the method is called against.&#39;

__all__ += [ &#39;SelfDocumentationString&#39; ]

#####################################
# Functions defined in the module   #
#####################################

def HangWrap( baseString, lineLength=80, indent=30 ):
    &quot;&quot;&quot;Helper/formatting function that wraps a string to the specified line-length, with the specified hanging indent.&quot;&quot;&quot;
    remainder = baseString.split( &#39; &#39; )
    result = &#39;&#39;
    line = &#39;&#39;
    while remainder:
        theWord = remainder[0]
        remainder = remainder[1:]
        if not theWord in [ &#39;\n&#39;, &#39;\r&#39; ]:
            if len( line ) + len( theWord ) + 1 &amp;gt; lineLength:
                result += line + &#39;\n&#39;
                line = &#39; &#39; * ( indent + 1)
            line += theWord + &#39; &#39;
    result += line + &#39;\n&#39;
    return result

__all__ += [ &#39;HangWrap&#39; ]

def InitializeDocMetadata( decoratedItem ):
    &quot;&quot;&quot;Helper function that initializes all of the decorated item&#39;s documentation-metadata structures.&quot;&quot;&quot;
    if not hasattr( decoratedItem, &#39;_configDocumentation&#39; ):
        setattr( decoratedItem, &#39;_configDocumentation&#39;, {} )
    if not hasattr( decoratedItem, &#39;_argumentDocumentation&#39; ):
        setattr( decoratedItem, &#39;_argumentDocumentation&#39;, { &#39;args&#39;:{}, &#39;arglist&#39;:{}, &#39;kwargs&#39;:{} } )
    if not hasattr( decoratedItem, &#39;_baseDocumentation&#39; ):
        setattr( decoratedItem, &#39;_baseDocumentation&#39;, decoratedItem.__doc__ )
        if not getattr( decoratedItem, &#39;_baseDocumentation&#39; ):
            setattr( decoratedItem, &#39;_baseDocumentation&#39;, &#39;&#39; )
    if not hasattr( decoratedItem, &#39;_exceptionDocumentation&#39; ):
        setattr( decoratedItem, &#39;_exceptionDocumentation&#39;, {} )
    if not hasattr( decoratedItem, &#39;_returnsDocumentation&#39; ):
        setattr( decoratedItem, &#39;_returnsDocumentation&#39;, [] )
    if not hasattr( decoratedItem, &#39;_todoDocumentation&#39; ):
        setattr( decoratedItem, &#39;_todoDocumentation&#39;, [] )

__all__ += [ &#39;InitializeDocMetadata&#39; ]

def RebuildDocumentation( decoratedItem ):
    &quot;&quot;&quot;Helper function that rebuilds documentation metadata and stuffs it into the item&#39;s __doc__.&quot;&quot;&quot;

    # Base documentation, if any, first...
    tmpDocs = getattr( decoratedItem, &#39;_baseDocumentation&#39; )
    if not tmpDocs:
        tmpDocs = &#39;No base documentation provided.&#39;
    tmpDocs += &#39;\n&#39;

    # TODO documentation next
    toDoDocs = getattr( decoratedItem, &#39;_todoDocumentation&#39; )
    if len( toDoDocs ) != 0:
        tmpDocs += &quot;&quot;&quot;
###################
## TO-DOs        ##
###################
&quot;&quot;&quot;
        for theToDo in toDoDocs:
            tmpDocs += HangWrap( &#39; - %s&#39; % ( theToDo ), 80, 6 )

    # Returns documentation next
    returnDocs = getattr( decoratedItem, &#39;_returnsDocumentation&#39; )
    tag = &#39;&#39;
    if len( returnDocs ) &amp;gt; 1:
        tmpDocs += &quot;&quot;&quot;
###################
## Returns       ##
###################
Any of %s
&quot;&quot;&quot; % ( returnDocs )
    elif len( returnDocs ) == 1:
        tmpDocs += &quot;&quot;&quot;
###################
## Returns       ##
###################
%s
&quot;&quot;&quot; % ( returnDocs[ 0 ] )

    # Arguments next
    argDocs = getattr( decoratedItem, &#39;_argumentDocumentation&#39; )
    argSpecs = inspect.getargspec( decoratedItem )
    if argSpecs[0] or argSpecs[1] or argSpecs[2]:
        tmpDocs += &quot;&quot;&quot;
###################
## Arguments     ##
###################
&quot;&quot;&quot;
    if argSpecs[0]:
        for theArg in argSpecs[0]:
            if theArg in argDocs[&#39;args&#39;].keys():
                tmpDocs += HangWrap( ( &#39; + %s ........................&#39; % ( theArg ) )[:24] + &#39; &#39; + argDocs[&#39;args&#39;][theArg], 80, 24 )
    if argSpecs[1]:
        tmpDocs += HangWrap( ( &#39; + %s ........................&#39; % ( argSpecs[1] ) )[:24] + &#39; &#39; + argDocs[&#39;arglist&#39;], 80, 24 )
    if argSpecs[2] and argDocs[&#39;kwargs&#39;]:
        tmpDocs += ( &#39; + %s ........................&#39; % ( argSpecs[2] ) )[:24] + &#39; Keyword-argument list. The following values are known/documented:\n&#39;
        theKeys = argDocs[&#39;kwargs&#39;].keys()
        theKeys.sort()
        for theKey in theKeys:
            tmpDocs += HangWrap( ( &#39;   - %s ........................&#39; % ( theKey ) )[:24] + &#39; &#39; + str( argDocs[&#39;kwargs&#39;][theKey] ), 80, 24 )

    exceptionDocs = getattr( decoratedItem, &#39;_exceptionDocumentation&#39; )
    if exceptionDocs:
        tmpDocs += &quot;&quot;&quot;
###################
## Exceptions    ##
###################
&quot;&quot;&quot;
    exceptions = exceptionDocs.keys()
    exceptions.sort()
    for theException in exceptions:
        tmpDocs += &#39;Raises %s when:\n&#39; % ( theException )
        for details in exceptionDocs[ theException ]:
            tmpDocs += HangWrap( &#39; - %s;&#39; % ( details ), 80, 6 )

    configDocs = getattr( decoratedItem, &#39;_configDocumentation&#39; )
    if configDocs:
        tmpDocs += &quot;&quot;&quot;
###################
## Configuration ##
###################

The relevant configuration-file section(s) and values(s) are:
&quot;&quot;&quot;
        sectionKeys = configDocs.keys()
        sectionKeys.sort()
        for theSection in sectionKeys:
            tmpDocs += &#39;[%s]\n&#39; % ( theSection )
            items = configDocs[ theSection ]
            itemKeys = items.keys()
            itemKeys.sort()
            for theItem in itemKeys:
                tmpDocs += HangWrap( ( &#39;%s .....................................&#39; % ( theItem ) )[:29] + &#39; %s&#39; % ( items[ theItem ] ), 80, 29 )

    setattr( decoratedItem, &#39;__doc__&#39;, tmpDocs )

__all__ += [ &#39;RebuildDocumentation&#39; ]

def DocumentArgument( argumentType, argumentName, keywordName, argumentDocumentation ):
    &quot;&quot;&quot;Decorator function that documents a function- or method-argument.&quot;&quot;&quot;
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        argSpecs = inspect.getargspec( decoratedItem )
        if argSpecs[0]:
            for theName in argSpecs[0]:
                if theName != &#39;self&#39;:
                    if not theName in decoratedItem._argumentDocumentation[ &#39;args&#39; ].keys():
                        decoratedItem._argumentDocumentation[ &#39;args&#39; ][ theName ] = &#39;Not documented.&#39;
                else:
                    decoratedItem._argumentDocumentation[ &#39;args&#39; ][ theName ] = SelfDocumentationString
        if argSpecs[1]:
            if not decoratedItem._argumentDocumentation[ &#39;arglist&#39; ]:
                decoratedItem._argumentDocumentation[ &#39;arglist&#39; ] = argSpecs[1]
        if argSpecs[2]:
            if not decoratedItem._argumentDocumentation[ &#39;kwargs&#39; ]:
                decoratedItem._argumentDocumentation[ &#39;kwargs&#39; ] = {}

        if argumentType == &quot;argument&quot;:
            argDocs = getattr( decoratedItem, &#39;_argumentDocumentation&#39; )[ &#39;args&#39; ]
            argDocs[ argumentName ] = argumentDocumentation

        elif argumentType == &quot;arglist&quot;:
            pass

        elif argumentType == &quot;keyword&quot;:
            argDocs = getattr( decoratedItem, &#39;_argumentDocumentation&#39; )[ &#39;kwargs&#39; ]
            if keywordName in argDocs.keys():
                raise AttributeError( &#39;&quot;%s&quot; has already been documented as a keyword value for the &quot;%s&quot; list of %s.&#39; % ( keywordName, argumentName, decoratedItem.__name__ ) )
            argDocs[ keywordName ] = argumentDocumentation

        else:
            raise AttributeError( &#39;&quot;%s&quot; is not a recognized argumentType (&quot;argument&quot;, &quot;arglist&quot;, or &quot;keyword&quot; are allowed).&#39; % ( argumentType ) )

        # Re-create the item&#39;s docstring with the argument information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ &#39;DocumentArgument&#39; ]

def DocumentConfiguration( configSection, configName, configDescription ):
    &quot;&quot;&quot;Decorator function that documents the configuration-file section(s) relevant to 
instances of the object. Intended for use with ConfigParser instances, but may 
be valid for other configuration mechanisms.&quot;&quot;&quot;
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        configDocs = getattr( decoratedItem, &#39;_configDocumentation&#39; )
        if not configSection in configDocs.keys():
            configDocs[ configSection ] = {}
        configDocs[ configSection ][ configName ] = configDescription

        # Re-create the item&#39;s docstring with the exception information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ &#39;DocumentConfiguration&#39; ]

def DocumentException( exceptionType, exceptionCase ):
    &quot;&quot;&quot;Decorator function that documents an exception that can be raised by a function or method.&quot;&quot;&quot;
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        exceptionDocs = getattr( decoratedItem, &#39;_exceptionDocumentation&#39; )
        if not exceptionType.__name__ in exceptionDocs.keys():
            exceptionDocs[ exceptionType.__name__ ] = []
        exceptionDocs[ exceptionType.__name__ ].append( exceptionCase )

        # Re-create the item&#39;s docstring with the exception information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ &#39;DocumentException&#39; ]

def DocumentReturn( *returnTypes ):
    &quot;&quot;&quot;Decorator function that documents the return type(s) of a function or method.&quot;&quot;&quot;
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        returnDocs = getattr( decoratedItem, &#39;_returnsDocumentation&#39; )
        for theType in returnTypes:
            if theType != None:
                if not theType.__name__ in returnDocs:
                    returnDocs.append( theType.__name__ )
            else:
                if not &#39;None&#39; in returnDocs:
                    returnDocs.append( &#39;None&#39; )
        returnDocs.sort()
        # Re-create the item&#39;s docstring with the return information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ &#39;DocumentReturn&#39; ]

def ToDo( todo ):
    &quot;&quot;&quot;Decorator function that documents &quot;To-do&quot; items.&quot;&quot;&quot;
    def decorator( decoratedItem ):
        InitializeDocMetadata( decoratedItem )

        toDoDocs = getattr( decoratedItem, &#39;_todoDocumentation&#39; )
        toDoDocs.append( todo )
        # Re-create the item&#39;s docstring with the todo information:
        RebuildDocumentation( decoratedItem )

        return decoratedItem

    return decorator

__all__ += [ &#39;ToDo&#39; ]

#####################################
# Decorate above functions          #
#####################################

# DocumentArgument
DocumentArgument( &#39;argument&#39;, &#39;argumentType&#39;, None, &#39;(\&#39;argument\&#39;, \&#39;arglist\&#39; or \&#39;keyword\&#39;) The argument-type for the argument.&#39; )( DocumentArgument )
DocumentArgument( &#39;argument&#39;, &#39;argumentName&#39;, None, &#39;(String, required) The name of the argument of the function or method being documented.&#39; )( DocumentArgument )
DocumentArgument( &#39;argument&#39;, &#39;keywordName&#39;, None, &#39;(String, required for keyword arguments, ignored for other types) The name of a keyword argument that is recognized by the function or method.&#39; )( DocumentArgument )
DocumentArgument( &#39;argument&#39;, &#39;argumentDocumentation&#39;, None, &#39;(String) The documentation for the argument.&#39; )( DocumentArgument )
ToDo( &#39;Type-check arguments.&#39; )( DocumentArgument )
ToDo( &#39;Value-check arguments(?).&#39; )( DocumentArgument )
ToDo( &#39;Document exceptions&#39; )( DocumentArgument )

# DocumentConfiguration
DocumentArgument( &#39;argument&#39;, &#39;configSection&#39;, None, &#39;(String, required) The name of the configuration-section that the configName argument will be found in.&#39; )( DocumentConfiguration )
DocumentArgument( &#39;argument&#39;, &#39;configName&#39;, None, &#39;(String, required) The name of the configuration-value.&#39; )( DocumentConfiguration )
DocumentArgument( &#39;argument&#39;, &#39;configDescription&#39;, None, &#39;(String) The documentation for the configuration name/value pair.&#39; )( DocumentConfiguration )
ToDo( &#39;Type-check arguments.&#39; )( DocumentConfiguration )
ToDo( &#39;Value-check arguments(?).&#39; )( DocumentConfiguration )
ToDo( &#39;Document exceptions&#39; )( DocumentConfiguration )

# DocumentException
DocumentArgument( &#39;argument&#39;, &#39;exceptionType&#39;, None, &#39;(Exception, required) The exception-type expected under the case in exceptionCase.&#39; )( DocumentException )
DocumentArgument( &#39;argument&#39;, &#39;exceptionCase&#39;, None, &#39;(String) The case that will raise the specified exception. Documentation will be built as a list of cases for each exception, in a format similar to &quot;Raises [exceptionType] when:&quot; followed by all exception-cases for that type.&#39; )( DocumentException )
ToDo( &#39;Type-check arguments.&#39; )( DocumentException )
ToDo( &#39;Value-check arguments(?).&#39; )( DocumentException )
ToDo( &#39;Document exceptions&#39; )( DocumentException )

# DocumentReturn
DocumentArgument( &#39;arglist&#39;, &#39;returnTypes&#39;, None, &#39;The types returned by the function or method.&#39; )( DocumentReturn )
ToDo( &#39;Type-check arguments.&#39; )( DocumentReturn )
ToDo( &#39;Value-check arguments(?).&#39; )( DocumentReturn )
ToDo( &#39;Document exceptions&#39; )( DocumentReturn )

# HangWrap
DocumentArgument( &#39;argument&#39;, &#39;baseString&#39;, None, &#39;(String, required) The base string to re-format.&#39; )( HangWrap )
DocumentArgument( &#39;argument&#39;, &#39;lineLength&#39;, None, &#39;(Integer, optional, defaults to 80) The maximum length of a line in the re-formatted string.&#39; )( HangWrap )
DocumentArgument( &#39;argument&#39;, &#39;indent&#39;, None, &#39;(Integer, optional, defaults to 30) The number of spaces to indent second and subsequent lines in the re-formatted string&#39; )( HangWrap )
ToDo( &#39;Type-check arguments.&#39; )( HangWrap )
ToDo( &#39;Value-check arguments(?).&#39; )( HangWrap )
ToDo( &#39;Document exceptions&#39; )( HangWrap )

# InitializeDocMetadata
DocumentArgument( &#39;argument&#39;, &#39;decoratedItem&#39;, None, &#39;The item to initialize documentation-meta-data structures on.&#39; )( InitializeDocMetadata )
ToDo( &#39;Type-check arguments.&#39; )( InitializeDocMetadata )
ToDo( &#39;Value-check arguments(?).&#39; )( InitializeDocMetadata )
ToDo( &#39;Document exceptions&#39; )( InitializeDocMetadata )

# RebuildDocumentation
DocumentArgument( &#39;argument&#39;, &#39;decoratedItem&#39;, None, &#39;The item to rebuild __doc__ on, using current documentation-meta-data structure.&#39; )( RebuildDocumentation )
ToDo( &#39;Type-check arguments.&#39; )( RebuildDocumentation )
ToDo( &#39;Value-check arguments(?).&#39; )( RebuildDocumentation )
ToDo( &#39;Document exceptions&#39; )( RebuildDocumentation )

# ToDo
DocumentArgument( &#39;argument&#39;, &#39;todo&#39;, None, &#39;(String, required) The &quot;To-do&quot; item to attach.&#39; )( ToDo )
ToDo( &#39;Type-check arguments.&#39; )( ToDo )
ToDo( &#39;Value-check arguments(?).&#39; )( ToDo )
ToDo( &#39;Document exceptions&#39; )( ToDo )

#####################################
# Interfaces defined in the module  #
#####################################

#####################################
# Abstract Classes defined in the   #
# module                            #
#####################################

#####################################
# Classes defined in the module     #
#####################################

#####################################
# Package sub-modules and -packages #
#####################################

#####################################
# Unit-test the module on main      #
#####################################

if __name__ == &#39;__main__&#39;:
    import inspect, os, sys, unittest
    from UnitTestUtilities import *

    testSuite = unittest.TestSuite()
    testResults = unittest.TestResult()

    ##################################
    # Unit-test Constants            #
    ##################################

    class testConstants( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the constants defined in the module.&quot;&quot;&quot;
        def testConstants( self ):
            self.assertEquals( SelfDocumentationString, &#39;The object-instance that the method is called against.&#39;, &#39;SelfDocumentationString should be a constant, meaningful string.&#39; )
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testConstants ) )

    ##################################
    # Unit-test Functions            #
    ##################################

    class testDocumentArgument( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the DocumentArgument function defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentArgument ) )

    class testDocumentConfiguration( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the DocumentConfiguration function defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentConfiguration ) )

    class testDocumentException( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the DocumentException function defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentException ) )

    class testDocumentReturn( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the DocumentReturn function defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testDocumentReturn ) )

    class testHangWrap( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the HangWrap function defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testHangWrap ) )

    class testInitializeDocMetadata( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the InitializeDocMetadata function defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testInitializeDocMetadata ) )

    class testRebuildDocumentation( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the RebuildDocumentation function defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testRebuildDocumentation ) )

    ##################################
    # Unit-test Interfaces           #
    ##################################

    ##################################
    # Unit-test Abstract Classes     #
    ##################################

    ##################################
    # Unit-test Classes              #
    ##################################

    # Run unit-tests and install
    print &#39;#&#39;*79
    print &#39;Running unit-tests&#39;
    testSuite.run( testResults )
    print &#39;#&#39;*79
    if testResults.errors == [] and testResults.failures == []:
        print &#39;All unit-tests passed! Ready for build/copy!&#39;
        import shutil
        sitePackagesDirs = []
        for path in sys.path:
            if path.split( os.sep )[-1].lower() == &#39;site-packages&#39;:
                sitePackagesDirs.append( path )
        for path in sitePackagesDirs:
            destinationFile = path + os.sep + __file__
            try:
                shutil.copyfile( &#39;./%s&#39; % __file__, destinationFile )
                print &#39;Copied %s to %s&#39; % ( __file__, path )
            except:
                print &#39;Could not copy file %s to location %s&#39; % ( __file__, path )
    else:
        print &#39;Unit-tests failed (see items below)&#39;
        print &#39;#&#39;*79
        if testResults.errors:
            print &#39;Errors&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for error in testResults.errors:
                print error[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
        if testResults.failures:
            if testResults.errors:
                print &#39;#&#39;*79
            print &#39;Failures&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for failure in testResults.failures:
                print failure[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
    print &#39;#&#39;*79&lt;/pre&gt;
&lt;br /&gt;
As with &lt;code&gt;UnitTestUtilities&lt;/code&gt;, I didn&#39;t want to hold up sharing this code for the sake of writing a lot of unit-tests. In this case, I&#39;m not quite sure what my testing strategy is going to be. But as I write out those tests, the updated version of the module will be shared at the location noted above, so keep watching if you have the interest...
&lt;br /&gt;
&lt;b&gt;Commentary:&lt;/b&gt;
&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;Line(s)&lt;/dt&gt;
&lt;dt&gt;20, 34, 56, etc.&lt;/dt&gt;
&lt;dd&gt;This is my usual &lt;code&gt;__all__&lt;/code&gt; set-up&lt;/dd&gt;
&lt;dt&gt;26&lt;/dt&gt;
&lt;dd&gt;The decorators use functionality from the &lt;code&gt;inspect&lt;/code&gt; module, so we import it here&lt;/dd&gt;
&lt;dt&gt;32&lt;/dt&gt;
&lt;dd&gt;Rather than have a magic string embedded in the argument-decorator, though that would probably be OK, I opted to make the &lt;code&gt;SelfDocumentationString&lt;/code&gt; constant, and use it elsewhere. The name could be changed, it&#39;s a long name to type over and over again, but since most of it&#39;s uses can be copied and pasted quickly and easily, I didn&#39;t think it was a big deal.&lt;/dd&gt;
&lt;dt&gt;40-54&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;HangWrap&lt;/code&gt; is just a string-formatting function, that provides the nice, clean dot-leader-with-hanging-indent shown in the doc-string example for &lt;code&gt;HasTestFor&lt;/code&gt;, above. It&#39;s a convenience function, nothing more, and I may not bother with writing any significant unit-tests for it later (though I should, for the sake of completion).&lt;/dd&gt;
&lt;dt&gt;58-73&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;InitializeDocMetadata&lt;/code&gt; is a critical function - it assures that the documentation-meta-data structure needed for most (all?) of the decorators here is created, but not disturbed if it already exists. I&#39;m not completely happy with the function, but I couldn&#39;t come up with a better way to handle the need, and I expect that it will perform well (though since it&#39;s not called outside of the context of a decorator that runs once at &quot;compile-time,&quot; even that&#39;s not much of a concern). The items it ensures exist are:
        &lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;_configDocumentation&lt;/dt&gt;
&lt;dd&gt;A dictionary that will eventually contain config-section keys to dictionaries of config-names/values.&lt;/dd&gt;
&lt;dt&gt;_argumentDocumentation&lt;/dt&gt;
&lt;dd&gt;A dictionary of argument-, argument-list- and keyword-dictionaries&lt;/dd&gt;
&lt;dt&gt;_baseDocumentation&lt;/dt&gt;
&lt;dd&gt;The original &lt;code&gt;__doc__&lt;/code&gt; of the decorated item.&lt;/dd&gt;
&lt;dt&gt;_exceptionDocumentation&lt;/dt&gt;
&lt;dd&gt;A dictionary of Exception keys to condition-string lists&lt;/dd&gt;
&lt;dt&gt;_returnDocumentation&lt;/dt&gt;
&lt;dd&gt;A list of types&lt;/dd&gt;
&lt;dt&gt;_todoDocumentation&lt;/dt&gt;
&lt;dd&gt;A list of to-do strings&lt;/dd&gt;
        &lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;77-172&lt;/dt&gt;
&lt;dd&gt;This is the function that generates the final, meta-data-inclusive docstring that we see when browsing decorated items with pydoc. It uses &lt;code&gt;HangWrap&lt;/code&gt; to format long lines into hanging-indent lines, but is pretty straightforward, I believe. Just long, since there&#39;s a lot of stuff to look for and format, potentially.&lt;/dd&gt;
&lt;dt&gt;174-217&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;DocumentArgument&lt;/code&gt; is (probably) the most commonly used decorator of this whole set, and likely deserves some in-depth commentary:
        &lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;Line(s)&lt;/dt&gt;
&lt;dt&gt;177&lt;/dt&gt;
&lt;dd&gt;Makes sure that the meta-data structure is available on the decorated item.&lt;/dd&gt;
&lt;dt&gt;179&lt;/dt&gt;
&lt;dd&gt;Gets ahold of the list of arguments that were defined by the code using &lt;code&gt;inspect&lt;/code&gt; functionality.&lt;/dd&gt;
&lt;dt&gt;180-186&lt;/dt&gt;
&lt;dd&gt;Checks for the existence of the argument-name, documents it with the &lt;code&gt;SelfDocumentationString&lt;/code&gt; value if it&#39;s &lt;code&gt;self&lt;/code&gt;, or with &quot;Not documented&quot; by default, after checking to see that it hasn&#39;t already &lt;i&gt;been&lt;/i&gt; documented.&lt;/dd&gt;
&lt;dt&gt;187-189&lt;/dt&gt;
&lt;dd&gt;Stuffs an inspected argument-list&#39;s name into the meta-data structure.&lt;/dd&gt;
&lt;dt&gt;190-191&lt;/dt&gt;
&lt;dd&gt;Stuffs an inspected keyword-arument-list&#39;s name into the meta-data structure.&lt;/dd&gt;
&lt;dt&gt;194-196&lt;/dt&gt;
&lt;dd&gt;Sets the documentation-string for the specified argument.&lt;/dd&gt;
&lt;dt&gt;198-199&lt;/dt&gt;
&lt;dd&gt;Skips any further processing for an argument-list, since there can be only one, and it&#39;s done by the time we reach this section of the code.&lt;/dd&gt;
&lt;dt&gt;201-205&lt;/dt&gt;
&lt;dd&gt;Adds the specified keyword-name and documentation-value to the keyword-argument&#39;s meta-data (a dictionary of known keywords, and their documentation, if you recall).&lt;/dd&gt;
&lt;dt&gt;211&lt;/dt&gt;
&lt;dd&gt;Rebuilds the decorated item&#39;s &lt;code&gt;__doc__&lt;/code&gt;.&lt;/dd&gt;
        &lt;/dl&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
The patterns of various branches of &lt;code&gt;DocumentArgument&lt;/code&gt; are echoed in &lt;code&gt;DocumentConfiguration&lt;/code&gt;, &lt;code&gt;DocumentException&lt;/code&gt;, &lt;code&gt;DocumentReturn&lt;/code&gt; and &lt;code&gt;ToDo&lt;/code&gt;, albeit with different and shallower meta-data-structure targets - hopefully, their functionality is straightforward.
&lt;br /&gt;
&lt;br /&gt;
Lines 298 through 356 explicitly decorate all of the decorator functions after they have all been defined, in order to provide the (hopefully) expected documentation for them. The structure/syntax probably looks a bit weird if you aren&#39;t used to it, but it does the same thing as the typical @DecoratorFunction(args) call right before the item being decorated...&lt;br /&gt;
&lt;br /&gt;
The final doc-strings of each of these items demonstrate that:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;DocumentArgument(argumentType, argumentName, keywordName, argumentDocumentation)
    Decorator function that documents a function- or method-argument.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + argumentType ........ (&#39;argument&#39;, &#39;arglist&#39; or &#39;keyword&#39;) The argument-type 
                             for the argument. 
     + argumentName ........ (String, required) The name of the argument of the 
                             function or method being documented. 
     + keywordName ......... (String, required for keyword arguments, ignored for 
                             other types) The name of a keyword argument that is 
                             recognized by the function or method. 
     + argumentDocumentation (String) The documentation for the argument.

DocumentConfiguration(configSection, configName, configDescription)
    Decorator function that documents the configuration-file section(s) relevant to 
    instances of the object. Intended for use with ConfigParser instances, but may 
    be valid for other configuration mechanisms.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + configSection ....... (String, required) The name of the 
                             configuration-section that the configName argument 
                             will be found in. 
     + configName .......... (String, required) The name of the 
                             configuration-value. 
     + configDescription ... (String) The documentation for the configuration 
                             name/value pair.

DocumentException(exceptionType, exceptionCase)
    Decorator function that documents an exception that can be raised by a function or method.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + exceptionType ....... (Exception, required) The exception-type expected 
                             under the case in exceptionCase. 
     + exceptionCase ....... (String) The case that will raise the specified 
                             exception. Documentation will be built as a list of 
                             cases for each exception, in a format similar to 
                             &quot;Raises [exceptionType] when:&quot; followed by all 
                             exception-cases for that type.

DocumentReturn(*returnTypes)
    Decorator function that documents the return type(s) of a function or method.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + returnTypes ......... returnTypes

HangWrap(baseString, lineLength=80, indent=30)
    Helper/formatting function that wraps a string to the specified line-length, with the specified hanging indent.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + baseString .......... (String, required) The base string to re-format. 
     + lineLength .......... (Integer, optional, defaults to 80) The maximum length 
                             of a line in the re-formatted string. 
     + indent .............. (Integer, optional, defaults to 30) The number of 
                             spaces to indent second and subsequent lines in the 
                             re-formatted string

InitializeDocMetadata(decoratedItem)
    Helper function that initializes all of the decorated item&#39;s documentation-metadata structures.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + decoratedItem ....... The item to initialize documentation-meta-data 
                             structures on.

RebuildDocumentation(decoratedItem)
    Helper function that rebuilds documentation metadata and stuffs it into the item&#39;s __doc__.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + decoratedItem ....... The item to rebuild __doc__ on, using current 
                             documentation-meta-data structure.

ToDo(todo)
    Decorator function that documents &quot;To-do&quot; items.
     
    ###################
    ## TO-DOs        ##
    ###################
     - Type-check arguments. 
     - Value-check arguments(?). 
     - Document exceptions 
     
    ###################
    ## Arguments     ##
    ###################
     + todo ................ (String, required) The &quot;To-do&quot; item to attach.&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/3665288195861401467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/docmeta-module.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3665288195861401467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/3665288195861401467'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/docmeta-module.html' title='The DocMeta Module'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-6443325159550121937</id><published>2011-11-09T05:02:00.000-08:00</published><updated>2011-11-09T05:02:45.524-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="unit-testing"/><category scheme="http://www.blogger.com/atom/ns#" term="unittest"/><category scheme="http://www.blogger.com/atom/ns#" term="UnitTestUtilities"/><title type='text'>The UnitTestUtilities Module</title><content type='html'>&lt;b&gt;Shared-code location:&lt;/b&gt; &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/UnitTestUtilities.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/UnitTestUtilities.py&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Earlier I promised to share the &lt;code&gt;UnitTestUtilities&lt;/code&gt; and &lt;code&gt;DocMeta&lt;/code&gt; modules. Today, it&#39;ll be &lt;code&gt;UnitTestUtilities&lt;/code&gt;.&lt;br /&gt;
&lt;br /&gt;
The rationale for this chunk of code was, quite simply, that I found I needed a way to make sure that all of the items that &lt;i&gt;needed&lt;/i&gt; to be tested for a given class, abstract class, or interface would be automatically identified in as simple a way as possible.&lt;br /&gt;
&lt;br /&gt;
The test-cases template in the module-template I shared yesterday provides just such hooks (for properties and methods, at least) in the &lt;code&gt;testPropertyCountAndTests&lt;/code&gt; and &lt;code&gt;testMethodCountAndTests&lt;/code&gt; test-methods, you may recall. Those, in turn depend on two functions provided by &lt;code&gt;UnitTestUtilities&lt;/code&gt;.

Here&#39;s what that code looks like at present.

&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;# UnitTestUtilities.py
&quot;&quot;&quot;Provides some utility functions to assist in assuring code-coverage for unit-tests.

These functions are predicated on a test structure that looks something like so:

########################################

    class ClassNameDerived( ClassName ):
        def __init__( self ):
            pass
    
    class testClassName( unittest.TestCase ):
        \&quot;\&quot;\&quot;Unit-tests the ClassName class.\&quot;\&quot;\&quot;
    
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
#        def testDerived( self ):
#            \&quot;\&quot;\&quot;Testing abstract nature of the ClassName class.\&quot;\&quot;\&quot;
#            pass
    
#        def testFinal( self ):
#            \&quot;\&quot;\&quot;Testing final nature of the ClassName class.\&quot;\&quot;\&quot;
#            pass
    
        def testConstruction( self ):
            \&quot;\&quot;\&quot;Testing construction of the ClassName class.\&quot;\&quot;\&quot;
            pass
    
        def testPropertyCountAndTests( self ):
            \&quot;\&quot;\&quot;Testing the properties of the ClassName class.\&quot;\&quot;\&quot;
            items = getMemberNames( ClassName )[0]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;ClassName is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            \&quot;\&quot;\&quot;Testing the methods of the ClassName class.\&quot;\&quot;\&quot;
            items = getMemberNames( ClassName )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;ClassName is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

########################################

The functions, as used in testPropertyCountAndTests and testMethodCountAndTests, 
assure that there is a test[MethodName] and test[PropertyName] for each *public* 
method and property, respectively, and raise assertion errors if they don&#39;t exist. 
It is up to the developer to generate those test-methods and to assure that they 
provide complete coverage (at least so far).

&quot;&quot;&quot;

__author__  = &#39;Brian D. Allbee&#39;
__licence__ = &#39;http://creativecommons.org/licenses/by-sa/3.0/&#39;
__version__ = &#39;0.2&#39;
__copyright__ = &quot;Copyright 2011, Brian D. Allbee&quot;
__credits__ = [&quot;Brian D. Allbee&quot;]
__maintainer__ = &quot;Brian D. Allbee&quot;
__email__ = &quot;brian.allbee@gmail.com&quot;
__status__ = &quot;Development&quot;

#####################################
# Build an &quot;__all__&quot; list to        #
# support                           #
# &quot;from Namespace.blah import *&quot;    #
# syntax                            #
#####################################
__all__ = []

#####################################
# Required imports                  #
#####################################

import inspect, types
from DocMeta import *

#####################################
# Functions defined in the module   #
#####################################

@ToDo( &#39;Unit-test the function&#39; )
@ToDo( &#39;Document exceptions raised&#39; )
@ToDo( &#39;Value-check arguments&#39; )
@ToDo( &#39;Type-check arguments&#39; )
@DocumentArgument( &#39;argument&#39;, &#39;theClass&#39;, None, &#39;(Class, required) The class to get property and method names from.&#39; )
@DocumentReturn( types.TupleType )
def getMemberNames( theClass ):
    &quot;&quot;&quot;Returns a list of property- and method-names for the specified class.&quot;&quot;&quot;
    classMembers = inspect.getmembers( theClass )
    properties = []
    methods = []
    for member in classMembers:
        if inspect.isdatadescriptor( member[1] ) and member[0][0] != &#39;_&#39;:
            properties.append( member[0] )
        if inspect.ismethod( member[1] ) and member[0][0] != &#39;_&#39;:
            methods.append( member[0] )
    return properties, methods

__all__ += [ &#39;getMemberNames&#39; ]

@ToDo( &#39;Unit-test the function&#39; )
@ToDo( &#39;Document exceptions raised&#39; )
@ToDo( &#39;Value-check arguments&#39; )
@ToDo( &#39;Type-check arguments&#39; )
@DocumentArgument( &#39;argument&#39;, &#39;testCase&#39;, None, &#39;(unittest.TestCase, required) The TestCase instance to check for the named test-method in.&#39; )
@DocumentArgument( &#39;argument&#39;, &#39;itemName&#39;, None, &#39;(Class, required) The name of the property or method to look for a test-method for (&quot;test[itemName]&quot;).&#39; )
@DocumentReturn( types.BooleanType )
def HasTestFor( testCase, itemName ):
    &quot;&quot;&quot;Returns true if the testCase provided has a test-method named &#39;test[itemName]&#39;.&quot;&quot;&quot;
    classMembers = inspect.getmembers( testCase )
    nameToFind = &#39;test%s&#39; % ( itemName )
    for member in classMembers:
        if member[0] == nameToFind:
            return True
    return False

__all__ += [ &#39;HasTestFor&#39; ]

#####################################
# Interfaces defined in the module  #
#####################################

#####################################
# Abstract Classes defined in the   #
# module                            #
#####################################

#####################################
# Classes defined in the module     #
#####################################

#####################################
# Package sub-modules and -packages #
#####################################

#####################################
# Unit-test the module on main      #
#####################################

if __name__ == &#39;__main__&#39;:
    import inspect, os, sys, unittest

    testSuite = unittest.TestSuite()
    testResults = unittest.TestResult()

    #################################
    # Unit-test Constants           #
    #################################

    class testConstants( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the constants defined in the module.&quot;&quot;&quot;
        # No constants to test
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testConstants ) )

    #################################
    # Unit-test Functions           #
    #################################

    class Parent( object ):
        ParentPublic1 = property()
        ParentPublic2 = property()
        _ParentProtected1 = property()
        _ParentProtected2 = property()
        __ParentPrivate1 = property()
        __ParentPrivate2 = property()
        ParentOverrideMe1 = property()
        def __init__( self ): pass
        def ParentMethod1( self ): pass
        def ParentMethod2( self ): pass
        def _ParentMethod1( self ): pass
        def _ParentMethod2( self ): pass
        def __ParentMethod1( self ): pass
        def __ParentMethod2( self ): pass
        def ParentOverrideMe1( self ): pass

    class Child( Parent, object ):
        ChildPublic1 = property()
        ChildPublic2 = property()
        _ChildProtected1 = property()
        _ChildProtected2 = property()
        __ChildPrivate1 = property()
        __ChildPrivate2 = property()
        ChildOverrideMe1 = property()
        def __init__( self ): pass
        def ChildMethod1( self ): pass
        def ChildMethod2( self ): pass
        def _ChildMethod1( self ): pass
        def _ChildMethod2( self ): pass
        def __ChildMethod1( self ): pass
        def __ChildMethod2( self ): pass
        def ParentOverrideMe1( self ): pass

    class BadParentTestCase( object ): pass

    class GoodParentTestCase( object ):
        def testParentPublic1( self ): pass
        def testParentPublic2( self ): pass
        def testParentMethod1( self ): pass
        def testParentMethod2( self ): pass
        def testParentOverrideMe2( self ): pass

    class BadChildTestCase( object ): pass

    class GoodChildTestCase( object ):
        def testParentPublic1( self ): pass
        def testParentPublic2( self ): pass
        def testParentMethod1( self ): pass
        def testParentMethod2( self ): pass
        def testChildPublic1( self ): pass
        def testChildPublic2( self ): pass
        def testChildMethod1( self ): pass
        def testChildMethod2( self ): pass
        def testParentOverrideMe2( self ): pass

    class testFunctions( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the module&#39;s functions.&quot;&quot;&quot;
        
        def testgetMemberNames( self ):
            &quot;&quot;&quot;Tests the getMemberNames function.&quot;&quot;&quot;
            self.assertEquals( getMemberNames( Parent ), 
                (
                    [&#39;ParentPublic1&#39;, &#39;ParentPublic2&#39;], 
                    [&#39;ParentMethod1&#39;, &#39;ParentMethod2&#39;, &#39;ParentOverrideMe1&#39;]
                )
            )
            self.assertEquals( getMemberNames( Child ), 
                (
                    [&#39;ChildOverrideMe1&#39;, &#39;ChildPublic1&#39;, &#39;ChildPublic2&#39;, &#39;ParentPublic1&#39;, &#39;ParentPublic2&#39;], 
                    [&#39;ChildMethod1&#39;, &#39;ChildMethod2&#39;, &#39;ParentMethod1&#39;, &#39;ParentMethod2&#39;, &#39;ParentOverrideMe1&#39;]
                )
            )
        
        def testHasTestFor( self ):
            &quot;&quot;&quot;Tests the HasTestFor function.&quot;&quot;&quot;
            goodCases = {
                GoodParentTestCase:[
                    &#39;ParentPublic1&#39;,
                    &#39;ParentPublic2&#39;,
                    &#39;ParentMethod1&#39;,
                    &#39;ParentMethod2&#39;,
                    &#39;ParentOverrideMe2&#39;,
                    ],
                GoodChildTestCase:[
                    &#39;ParentPublic1&#39;,
                    &#39;ParentPublic2&#39;,
                    &#39;ParentMethod1&#39;,
                    &#39;ParentMethod2&#39;,
                    &#39;ParentOverrideMe2&#39;,
                    &#39;ChildPublic1&#39;,
                    &#39;ChildPublic2&#39;,
                    &#39;ChildMethod1&#39;,
                    &#39;ChildMethod2&#39;,
                    ],
                }
            for testCase in goodCases:
                caseItems = goodCases[ testCase ]
                for item in caseItems:
                    self.assertTrue( HasTestFor( testCase, item ), &#39;There should be a test-method in %s for %s (test%s)&#39; % ( testCase.__name__, item, item ) )
            badCases = {
                BadParentTestCase:[
                    &#39;ParentPublic1&#39;,
                    &#39;ParentPublic2&#39;,
                    &#39;ParentMethod1&#39;,
                    &#39;ParentMethod2&#39;,
                    &#39;testParentOverrideMe2&#39;,
                    ],
                BadChildTestCase:[
                    &#39;ParentPublic1&#39;,
                    &#39;ParentPublic2&#39;,
                    &#39;ParentMethod1&#39;,
                    &#39;ParentMethod2&#39;,
                    &#39;ParentOverrideMe2&#39;,
                    &#39;ChildPublic1&#39;,
                    &#39;ChildPublic2&#39;,
                    &#39;ChildMethod1&#39;,
                    &#39;ChildMethod2&#39;,
                    ],
                }
            for testCase in badCases:
                caseItems = badCases[ testCase ]
                for item in caseItems:
                    self.assertFalse( HasTestFor( testCase, item ), &#39;There should not be a test-method in %s for %s (test%s)&#39; % ( testCase.__name__, item, item ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testFunctions ) )

    #################################
    # Unit-test Interfaces          #
    #################################

    #################################
    # Unit-test Abstract Classes    #
    #################################

    #################################
    # Unit-test Classes             #
    #################################

    # Run unit-tests and install
    print &#39;#&#39;*79
    print &#39;Running unit-tests&#39;
    testSuite.run( testResults )
    print &#39;#&#39;*79
    if testResults.errors == [] and testResults.failures == []:
        print &#39;All unit-tests passed! Ready for build/copy!&#39;
        import shutil
        sitePackagesDirs = []
        for path in sys.path:
            if path.split( os.sep )[-1].lower() == &#39;site-packages&#39;:
                sitePackagesDirs.append( path )
        for path in sitePackagesDirs:
            destinationFile = path + os.sep + __file__
            try:
                shutil.copyfile( &#39;./%s&#39; % __file__, destinationFile )
                print &#39;Copied %s to %s&#39; % ( __file__, path )
            except:
                print &#39;Could not copy file %s to location %s&#39; % ( __file__, path )
    else:
        print &#39;Unit-tests failed (see items below)&#39;
        print &#39;#&#39;*79
        if testResults.errors:
            print &#39;Errors&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for error in testResults.errors:
                print error[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
        if testResults.failures:
            if testResults.errors:
                print &#39;#&#39;*79
            print &#39;Failures&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for failure in testResults.failures:
                print failure[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
    print &#39;#&#39;*79&lt;/pre&gt;
&lt;br /&gt;
You might note that there&#39;s several &lt;code&gt;@ToDo&lt;/code&gt; annotations here — I&#39;m planning on getting to each of those in the near future, but didn&#39;t want to hold up sharing this code for want of those items, particularly as the &lt;i&gt;main&lt;/i&gt; unit-tests are functional and pass...</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/6443325159550121937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/unittestutilities-module.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6443325159550121937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/6443325159550121937'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/unittestutilities-module.html' title='The UnitTestUtilities Module'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1660699321100518149.post-126981753998959345</id><published>2011-11-07T06:00:00.000-08:00</published><updated>2011-11-07T16:06:24.499-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="abstract class"/><category scheme="http://www.blogger.com/atom/ns#" term="class"/><category scheme="http://www.blogger.com/atom/ns#" term="code conventions"/><category scheme="http://www.blogger.com/atom/ns#" term="code coverage"/><category scheme="http://www.blogger.com/atom/ns#" term="function"/><category scheme="http://www.blogger.com/atom/ns#" term="interface"/><category scheme="http://www.blogger.com/atom/ns#" term="template file"/><title type='text'>The importance of Code Templates</title><content type='html'>It&#39;s been a while now since I posted anything... It&#39;s been a busy couple of months, and I just couldn&#39;t spare the time, unfortunately.&lt;br /&gt;
&lt;br /&gt;
But I haven&#39;t been completely idle, either. I actually wrote some Python code that was intended for production use at work (which I&#39;m not sure I can share, at least not directly), which was cool and enlightening. I also had several insights into things that have made changes to my preferred coding style, most of which I&#39;m going to share with you here.&lt;br /&gt;
&lt;br /&gt;
First, as I was thrashing through 20+ classes, nominal interfaces and nominal abstract classes, all of which were going to need to be (potentially) maintained by others who weren&#39;t as familiar with Python as I am, I determined that I should make some attempt to make the structure as consistent as possible for everyone who might have to look at the code later on. The first step in this effort, for me, at least, was to build out a template-file for Python modules, including both main-code structure and some built-in unit-testing.&lt;br /&gt;
&lt;br /&gt;
This template-file can be downloaded from my public Dropbox folder at &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/Template.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/Template.py&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
As of today, here&#39;s what that template looks like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;python&quot; name=&quot;code&quot;&gt;# Python module template
&quot;&quot;&quot;Python module template. Provides no functionality.&quot;&quot;&quot;

__author__  = &#39;Brian D. Allbee&#39;
__licence__ = &#39;http://creativecommons.org/licenses/by-sa/3.0/&#39;
__version__ = &#39;0.2&#39;
__copyright__ = &quot;Copyright 2011, Brian D. Allbee&quot;
__credits__ = [&quot;Brian D. Allbee&quot;]
__maintainer__ = &quot;Brian D. Allbee&quot;
__email__ = &quot;brian.allbee@gmail.com&quot;
__status__ = &quot;Development&quot;

#####################################
# Build an &quot;__all__&quot; list to        #
# support                           #
# &quot;from Namespace.blah import *&quot;    #
# syntax                            #
#####################################
__all__ = []

#####################################
# Required imports                  #
#####################################

from DocMeta import *

#####################################
# Functions defined in the module   #
#####################################

#####################################
# Interfaces defined in the module  #
#####################################

#####################################
# Abstract Classes defined in the   #
# module                            #
#####################################

#####################################
# Classes defined in the module     #
#####################################

class ClassName( object ):
    &quot;&quot;&quot;Class doc-string.&quot;&quot;&quot;

    ##################################
    # Class Attributes               #
    ##################################

    ##################################
    # Class Property-Getter Methods  #
    ##################################

    ##################################
    # Class Property-Setter Methods  #
    ##################################

    ##################################
    # Class Property-Deleter Methods #
    ##################################

    ##################################
    # Class Properties               #
    ##################################

    ##################################
    # Object Constructor             #
    ##################################

    @DocumentArgument( &#39;argument&#39;, &#39;self&#39;, None, SelfDocumentationString )
    def __init__( self ):
        &quot;&quot;&quot;Object constructor.&quot;&quot;&quot;
        # Nominally final: Don&#39;t allow any class other than this one
#        if self.__class__ != ClassName:
#            raise NotImplementedError( &#39;ClassName is (nominally) a final class, and is not intended to be derived from.&#39; )
        # Nominally abstract: Don&#39;t allow instantiation of the class
#        if self.__class__ == ClassName:
#            raise NotImplementedError( &#39;ClassName is (nominally) an interface, and is not intended to be instantiated.&#39; )
#            raise NotImplementedError( &#39;ClassName is (nominally) an abstract class, and is not intended to be instantiated.&#39; )
        pass

    ##################################
    # Object Destructor              #
    ##################################

    ##################################
    # Class Methods                  #
    ##################################

__all__ += [ &#39;ClassName&#39; ]

#####################################
# Package sub-modules and -packages #
#####################################

#####################################
# Unit-test the module on main      #
#####################################

if __name__ == &#39;__main__&#39;:
    import os, sys, unittest
    from UnitTestUtilities import *

    testSuite = unittest.TestSuite()
    testResults = unittest.TestResult()

    #################################
    # Unit-test Constants           #
    #################################

    class testConstants( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the constants defined in the module.&quot;&quot;&quot;
        pass
    
    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testConstants ) )

    #################################
    # Unit-test Functions           #
    #################################

    #################################
    # Unit-test Interfaces          #
    #################################

    #################################
    # Unit-test Abstract Classes    #
    #################################

    #################################
    # Unit-test Classes             #
    #################################

    class ClassNameDerived( ClassName ):
        def __init__( self ):
            pass
    
    class testClassName( unittest.TestCase ):
        &quot;&quot;&quot;Unit-tests the ClassName class.&quot;&quot;&quot;
   
        def setUp( self ):
            pass
    
        def tearDown( self ):
            pass
        
#        def testDerived( self ):
#            &quot;&quot;&quot;Testing abstract nature of the ClassName class.&quot;&quot;&quot;
#            pass
    
#        def testFinal( self ):
#            &quot;&quot;&quot;Testing final nature of the ClassName class.&quot;&quot;&quot;
#            pass
    
        def testConstruction( self ):
            &quot;&quot;&quot;Testing construction of the ClassName class.&quot;&quot;&quot;
            pass
    
        def testPropertyCountAndTests( self ):
            &quot;&quot;&quot;Testing the properties of the ClassName class.&quot;&quot;&quot;
            items = getMemberNames( ClassName )[0]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;ClassName is expected to have %d properties to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s property (test%s), but none was identifiable.&#39; % ( item, item ) )

        def testMethodCountAndTests( self ):
            &quot;&quot;&quot;Testing the methods of the ClassName class.&quot;&quot;&quot;
            items = getMemberNames( ClassName )[1]
            actual = len( items )
            expected = 0
            self.assertEquals( expected, actual, &#39;ClassName is expected to have %d methods to test, but %d were dicovered by inspection.&#39; % ( expected, actual ) )
            for item in items:
                self.assertTrue( HasTestFor( self, item ), &#39;There should be a test for the %s method (test%s), but none was identifiable.&#39; % ( item, item ) )

    testSuite.addTests( unittest.TestLoader().loadTestsFromTestCase( testClassName ) )

    # Run unit-tests and install
    print &#39;#&#39;*79
    print &#39;Running unit-tests&#39;
    testSuite.run( testResults )
    print &#39;#&#39;*79
    if testResults.errors == [] and testResults.failures == []:
        print &#39;All unit-tests passed! Ready for build/copy!&#39;
        # Uncomment the following line to allow installation
        # import shutil
        sitePackagesDirs = []
        for path in sys.path:
            if path.split( os.sep )[-1].lower() == &#39;site-packages&#39;:
                sitePackagesDirs.append( path )
        for path in sitePackagesDirs:
            destinationFile = path + os.sep + __file__
            try:
                shutil.copyfile( &#39;./%s&#39; % __file__, destinationFile )
                print &#39;Copied %s to %s&#39; % ( __file__, path )
            except:
                print &#39;Could not copy file %s to location %s&#39; % ( __file__, path )
    else:
        print &#39;Unit-tests failed (see items below)&#39;
        print &#39;#&#39;*79
        if testResults.errors:
            print &#39;Errors&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for error in testResults.errors:
                print error[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
        if testResults.failures:
            if testResults.errors:
                print &#39;#&#39;*79
            print &#39;Failures&#39;
            print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            for failure in testResults.failures:
                print failure[1]
                print &#39;#&#39; + &#39;-&#39;*77 + &#39;#&#39;
            print
    print &#39;#&#39;*79&lt;/pre&gt;
&lt;br /&gt;
There&#39;s a lot of stuff going on here that may not be immediately apparent:&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;b&gt;Line(s)&lt;/b&gt;&lt;/dt&gt;
&lt;dt&gt;4-11&lt;/dt&gt;
&lt;dd&gt;Some meta-data about the module, which I found a fairly complete list of at &lt;a href=&quot;http://bayes.colorado.edu/PythonGuidelines.html&quot;&gt;this University of Colorado page&lt;/a&gt;. Some of these items (&lt;code&gt;__version__&lt;/code&gt; for example) will appear in standard pydoc output, some will not.&lt;/dd&gt;
&lt;dt&gt;19, 91&lt;/dt&gt;
&lt;dd&gt;Per &lt;a href=&quot;http://docs.python.org/tutorial/modules.html#importing-from-a-package&quot;&gt;official Python documentation&lt;/a&gt;, &lt;code&gt;__all__&lt;/code&gt; should be used to ensure that module- and package-items can be imported using the &lt;code&gt;from [module] import [item|*]&lt;/code&gt; syntax that is common in Python code. An example of this, as it relates to the provided template class &lt;code&gt;ClassName&lt;/code&gt; appears on line 91.&lt;/dd&gt;
&lt;dt&gt;25, 71&lt;/dt&gt;
&lt;dd&gt;The &lt;code&gt;DocMeta&lt;/code&gt; module is a documentation-meta-data module I built for my own ends, that makes documenting things like function and method arguments, configuration file needs, and so on easier, using a set of decorator functions. An example of it&#39;s output (from pydoc browsing) for the ClassName class listed in the template is:&lt;br /&gt;
&lt;pre&gt;class ClassName(__builtin__.object)
     Class doc-string.
 
   Methods defined here:

__init__(self)
    Object constructor.
     
    ###################
    ## Arguments     ##
    ###################
     + self ................ The object-instance that the method is called against.&lt;/pre&gt;
&lt;/dd&gt;
&lt;dd&gt;The DocMeta module has been shared, though I haven&#39;t yet posted about it. It can be found at &lt;a href=&quot;http://dl.dropbox.com/u/1917253/site-packages/DocMeta.py&quot;&gt;dl.dropbox.com/u/1917253/site-packages/DocMeta.py&lt;/a&gt;&lt;/dd&gt;
&lt;dt&gt;44-91&lt;/dt&gt;
&lt;dd&gt;This is a template class, &lt;code&gt;ClassName&lt;/code&gt;, including commented sections for everything I thought I might ever need:
 &lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;Line(s)&lt;/dt&gt;
&lt;dt&gt;48&lt;/dt&gt;
&lt;dd&gt;Class Attributes (class-defined attributes and their values, where applicable.&lt;/dd&gt;
&lt;dt&gt;52, 56 and 60&lt;/dt&gt;
&lt;dd&gt;Property Getter, Setter and Deleter methods - since I prefer to have the getter methods freestanding and use a &lt;code&gt;property&lt;/code&gt; call later to assign them to properties.&lt;/dd&gt;
&lt;dt&gt;64&lt;/dt&gt;
&lt;dd&gt;Property declarations&lt;/dd&gt;
&lt;dt&gt;68-81&lt;/dt&gt;
&lt;dd&gt;A stub object-constructor method, with commented-out common code for making a class nominally final (75-76), nominally abstract (78, 80), or nominally an interface (78-79).&lt;/dd&gt;
&lt;dt&gt;84&lt;/dt&gt;
&lt;dd&gt;An object destructor. I haven&#39;t actually found a &lt;i&gt;need&lt;/i&gt; for one yet in my Python travels, but that doesn&#39;t mean I &lt;i&gt;won&#39;t&lt;/i&gt;&lt;/dd&gt;
&lt;dt&gt;88&lt;/dt&gt;
&lt;dd&gt;Class methods.&lt;/dd&gt;
  &lt;dd&gt;I&#39;ve found that I prefer to group methods that are being overridden from other classes in their own sections, whenever I can, &lt;i&gt;after&lt;/i&gt; the &quot;local&quot; methods have been defined.&lt;/dd&gt;
&lt;dt&gt;91&lt;/dt&gt;
&lt;dd&gt;Inclusion of the class into the &lt;code&gt;__all__&lt;/code&gt; list&lt;/dd&gt;
 &lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;94&lt;/dt&gt;
&lt;dd&gt;Package- and module-inclusions, each of which should be added to the &lt;code&gt;__all__&lt;/code&gt; list.&lt;/dd&gt;
&lt;dt&gt;98-177&lt;/dt&gt;
&lt;dd&gt;Unit-test definitions. I have a very specific unit-test structure that I prefer (at least for now), that I&#39;ve written some utility code for in the &lt;code&gt;UnitTestUtilities&lt;/code&gt; module (again, I&#39;ll share that soon).
 &lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;Line(s)&lt;/dt&gt;
&lt;dt&gt;105, 116 and 177&lt;/dt&gt;
&lt;dd&gt;Define a TestSuite to gather up all the unit-tests in, and make sure that each &lt;code&gt;TestCase&lt;/code&gt;-derived test-class is added to that suite.&lt;/dd&gt;
&lt;dt&gt;112-116&lt;/dt&gt;
&lt;dd&gt;Unit-test the package/module constants. They should be predictable, and raise errors if breaking changes are caused by altering them.&lt;/dd&gt;
&lt;dt&gt;119&lt;/dt&gt;
&lt;dd&gt;Unit-test any functions defined in the module.&lt;/dd&gt;
&lt;dt&gt;123&lt;/dt&gt;
&lt;dd&gt;Unit-test any nominal interfaces defined in the module. They may have nominally-abstract properties (I typically just do something like &lt;code&gt;PropertyName = property()&lt;/code&gt; for abstract properties) and nominally-abstract methods (I usually just raise a &lt;code&gt;NotImplementedError&lt;/code&gt;).&lt;/dd&gt;
&lt;dt&gt;127&lt;/dt&gt;
&lt;dd&gt;Unit-test any nominal abstract classes defined in the module. Any nominally-abstract properties and methods defined in one of these follows the pattern for nominal interfaces, above, while concrete properties and methods should be tested &quot;normally.&quot;&lt;/dd&gt;
&lt;dt&gt;131-177&lt;/dt&gt;
&lt;dd&gt;Unit-test any classes defined in the module. At the very least, I like to provide template locations for:
   &lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;Line(s)&lt;/dt&gt;
&lt;dt&gt;134&lt;/dt&gt;
&lt;dd&gt;A class derived from the class being tested, for use in either testing it&#39;s final or nominally-abstract nature (147, 151)&lt;/dd&gt;
&lt;dt&gt;141, 144&lt;/dt&gt;
&lt;dd&gt;Standard &lt;code&gt;setUp&lt;/code&gt; and &lt;code&gt;tearDown&lt;/code&gt; methods&lt;/dd&gt;
&lt;dt&gt;147&lt;/dt&gt;
&lt;dd&gt;A commented-out test method for nominally-final classes (75-76).&lt;/dd&gt;
&lt;dt&gt;151&lt;/dt&gt;
&lt;dd&gt;A commented-out test method for nominally-abstract classes or nominal interfaces (78-80).&lt;/dd&gt;
&lt;dt&gt;155&lt;/dt&gt;
&lt;dd&gt;Unit-tests for object-construction, though typically I find that the abstract/interface/final tests suffice.&lt;/dd&gt;
&lt;dt&gt;159-166&lt;/dt&gt;
&lt;dd&gt;Test for the expected number of class-properties, and that those properties each have an easily-identifiable test-method.&lt;/dd&gt;
    &lt;dd&gt;This functionality is dependent on the &lt;code&gt;UnitTestUtilities&lt;/code&gt; module mentioned earlier. What it does, in a nutshell, is to use the &lt;code&gt;inspect&lt;/code&gt; module&#39;s capabilities to find all class-members that are &lt;i&gt;data descriptors&lt;/i&gt; (properties), then check each named item thus found to assure that there is a test-method defined for each (by looking for a &lt;code&gt;testPropertyName&lt;/code&gt; method in the test-case class if the property name of the class being tested is &lt;code&gt;PropertyName&lt;/code&gt;, for example). It also requires the developer to pay attention to the number and names of proerties that are being acquired from a super-class, which I&#39;ve found very helpful.&lt;/dd&gt;
&lt;dt&gt;168-173&lt;/dt&gt;
&lt;dd&gt;Test for the expected number of class-methods, and that those methods each have an easily-identifiable test-method.&lt;/dd&gt;
    &lt;dd&gt;Apart from the fact that this test-method is looking for &lt;i&gt;methods&lt;/i&gt; instead of properties, it behaves much like the property-oriented test-method above.&lt;/dd&gt;
&lt;dt&gt;&lt;br /&gt;&lt;/dt&gt;
&lt;dd&gt;&lt;br /&gt;&lt;/dd&gt;
   &lt;/dl&gt;
&lt;/dd&gt;

 &lt;/dl&gt;
&lt;/dd&gt;
&lt;dt&gt;178-218&lt;/dt&gt;
&lt;dd&gt;Unit-test runs and &lt;i&gt;module&lt;/i&gt; installation on successful test-runs.&lt;/dd&gt;
&lt;/dl&gt;
&lt;br /&gt;
The last several lines, 178-218, run the unit-tests defined, and if there are no failures, attempts to copy the file being tested to any/all of the various &lt;code&gt;site-packages&lt;/code&gt; directories that the script can discover.</content><link rel='replies' type='application/atom+xml' href='http://my-brain-on-python.blogspot.com/feeds/126981753998959345/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/importance-of-code-templates.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/126981753998959345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1660699321100518149/posts/default/126981753998959345'/><link rel='alternate' type='text/html' href='http://my-brain-on-python.blogspot.com/2011/11/importance-of-code-templates.html' title='The importance of Code Templates'/><author><name>Brian Allbee</name><uri>http://www.blogger.com/profile/03342235418826668532</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>