[Checkins] SVN: keas.pbstate/trunk/ 100% coverage. :-)

Shane Hathaway shane at hathawaymix.org
Tue Jan 13 01:35:10 EST 2009


Log message for revision 94717:
  100% coverage. :-)
  

Changed:
  U   keas.pbstate/trunk/buildout.cfg
  U   keas.pbstate/trunk/setup.py
  U   keas.pbstate/trunk/src/keas/pbstate/README.txt

-=-
Modified: keas.pbstate/trunk/buildout.cfg
===================================================================
--- keas.pbstate/trunk/buildout.cfg	2009-01-13 04:25:23 UTC (rev 94716)
+++ keas.pbstate/trunk/buildout.cfg	2009-01-13 06:35:09 UTC (rev 94717)
@@ -1,6 +1,6 @@
 [buildout]
 develop = .
-parts = test python
+parts = test python coverage-test coverage-report
 
 [test]
 recipe = zc.recipe.testrunner
@@ -10,3 +10,14 @@
 recipe = zc.recipe.egg
 eggs = keas.pbstate
 interpreter = python
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = keas.pbstate
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')

Modified: keas.pbstate/trunk/setup.py
===================================================================
--- keas.pbstate/trunk/setup.py	2009-01-13 04:25:23 UTC (rev 94716)
+++ keas.pbstate/trunk/setup.py	2009-01-13 06:35:09 UTC (rev 94717)
@@ -26,6 +26,7 @@
     packages=['keas.pbstate'],
     namespace_packages=['keas'],
     install_requires=[
+        'setuptools',
         'protobuf',
         ],
 )

Modified: keas.pbstate/trunk/src/keas/pbstate/README.txt
===================================================================
--- keas.pbstate/trunk/src/keas/pbstate/README.txt	2009-01-13 04:25:23 UTC (rev 94716)
+++ keas.pbstate/trunk/src/keas/pbstate/README.txt	2009-01-13 06:35:09 UTC (rev 94717)
@@ -76,6 +76,32 @@
     ...
     AttributeError: 'Contact' object has no attribute 'phone'
 
+
+Mixins
+------
+
+A class can mix in properties that access sub-messages.  This is
+useful when subclassing (although subclassing should be avoided).
+
+Here is a class that mixes the ContactPB properties and the AddressPB
+properties in a single class.
+
+    >>> class MixedContact(object):
+    ...     __metaclass__ = ProtobufState
+    ...     protobuf_type = ContactPB
+    ...     protobuf_mixins = ('address',)
+
+    >>> mc = MixedContact()
+    >>> mc.line1 = u'180 Market St.'
+    >>> mc.line1
+    u'180 Market St.'
+    >>> mc.address.line1
+    u'180 Market St.'
+
+
+Serialization
+-------------
+
 Try to serialize the object without providing all of the required fields.
 
     >>> c.__getstate__()
@@ -107,28 +133,59 @@
     >>> c2.__getstate__()
     ('\x08\xea\x07\x12\tMary Anne', {})
 
+
+Object References
+-----------------
+
+This library supports references to arbitrary objects through the use
+of an automatically generated reference identifier or "refid".
+
 Add a guardian to c2, but don't say who the guardian is yet.
 
     >>> guardian_ref = c2.guardians.add()
 
-Using the protobuf_refs attribute, assign c to be a guardian of c2.
-Note that the item interface of the protobuf_refs attribute is
-unusual.  Don't think of it like a mapping; just think of it as a way
-to refer to any object.  Under the covers, a reference
-ID will be generated, that ID will be assigned to guardian_ref._p_refid,
-and the refid and target object will be added to the internal
-state of the protobuf_refs object.  Any message with a _p_refid field
-is a reference.  Every _p_refid field should be of type uint32.
+Using subscript notation on the protobuf_refs attribute, assign c to be
+a guardian of c2.
 
     >>> c2.protobuf_refs[guardian_ref] = c
 
-Verify the reference is serialized correctly.
+Although this makes protobuf_refs look like a mapping, it does not behave
+like a mapping.  Under the covers, a reference ID was generated, then that
+ID was assigned to guardian_ref._p_refid, and the refid and target object
+were added to the internal state of the protobuf_refs attribute.  Any message
+with a _p_refid field is a reference.  Every _p_refid field should be
+of type uint32.
 
+(Editor's note: I suspect this may be a significant abuse of subscript
+notation, so in the future this may be replaced with a less surprising
+interface.)
+
+Read the reference.
+
+    >>> c2.protobuf_refs[guardian_ref] is c
+    True
+
+Verify the reference gets serialized correctly.
+
     >>> data, targets = c2.__getstate__()
     >>> targets[c2.guardians[0]._p_refid] is c
     True
 
+Delete the reference.
 
+    >>> del c2.protobuf_refs[guardian_ref]
+    >>> c2.protobuf_refs[guardian_ref]
+    Traceback (most recent call last):
+    ...
+    KeyError: 'No reference set'
+
+Verify the reference is no longer contained in the serialized state.
+
+    >>> data, targets = c2.__getstate__()
+    >>> len(targets)
+    0
+
+
 Features Designed for ZODB
 --------------------------
 
@@ -142,6 +199,9 @@
     ...         return getattr(self, '_changed', False)
     ...     def _set_changed(self, value):
     ...         self._changed = value
+    ...         if not value:
+    ...             # reset the _cache_byte_size_dirty flags
+    ...             self.protobuf.ByteSize()
     ...     _p_changed = property(_get_changed, _set_changed)
     ...
     >>> class PersistentContact(FakePersistent):
@@ -150,9 +210,11 @@
     ...
 
     >>> c3 = PersistentContact()
+    >>> c3._p_changed
+    False
     >>> c3.create_time = 1003
     >>> c3.name = u'Snoopy'
-    >>> c3._p_changed = False; c3.__getstate__() and None
+    >>> c3._p_changed = False
 
 Reading an attribute does not set _p_changed.
 
@@ -169,7 +231,7 @@
 
 Adding to a repeated element sets _p_changed.
 
-    >>> c3._p_changed = False; c3.__getstate__() and None
+    >>> c3._p_changed = False
     >>> c3._p_changed
     False
     >>> c3.guardians.add()
@@ -189,10 +251,78 @@
     >>> c4._p_changed
     True
 
-The tuple returned by __getstate__ is actually a subclass of tuple.  This
-might tell the serializer in ZODB to save the state without pickling.
+The tuple returned by __getstate__ is actually a subclass of tuple.  The
+StateTuple suggests to the ZODB serializer that it can save the state
+without pickling.
 
-TODO: __getstate__ returns StateTuple
+    >>> type(c.__getstate__())
+    <class 'keas.pbstate.state.StateTuple'>
 
-TODO: mixins
 
+Edge Cases
+----------
+
+Synthesize a refid hash collision.  First make a reference:
+
+    >>> guardian_ref = c2.guardians.add()
+    >>> c2.protobuf_refs[guardian_ref] = c
+
+Covertly change the target of that reference:
+
+    >>> c2.protobuf_refs._targets[guardian_ref._p_refid] = mc
+
+Add a new reference to the original target.  The first generated refid
+will collide, but he protobuf_refs should should choose a different
+refid automatically.
+
+    >>> guardian2_ref = c2.guardians.add()
+    >>> c2.protobuf_refs[guardian2_ref] = c
+    >>> guardian_ref._p_refid == guardian2_ref._p_refid
+    False
+
+
+Exception Conditions
+--------------------
+
+Deleting message attributes is not allowed.
+
+    >>> del c.name
+    Traceback (most recent call last):
+    ...
+    AttributeError: can't delete attribute
+    >>> del mc.line1
+    Traceback (most recent call last):
+    ...
+    AttributeError: can't delete attribute
+
+Mixin names are checked.
+
+    >>> class MixedUpContact(object):
+    ...     __metaclass__ = ProtobufState
+    ...     protobuf_type = ContactPB
+    ...     protobuf_mixins = ('bogus',)
+    Traceback (most recent call last):
+    ...
+    AttributeError: Field 'bogus' not defined for protobuf type <...>
+
+Create a broken reference by setting a reference using the wrong
+protobuf_refs.
+
+    >>> c.guardians.add()
+    <keas.pbstate.testclasses_pb2.Ref object at ...>
+    >>> c2.protobuf_refs[c.guardians[0]] = c
+    >>> c.__getstate__()
+    Traceback (most recent call last):
+    ...
+    KeyError: 'Object contains broken references: <Contact object at ...>'
+    >>> del c2.protobuf_refs[c.guardians[0]]
+    >>> del c.guardians[0]
+
+Don't omit the protobuf_type attribute.
+
+    >>> class FailedContact(object):
+    ...     __metaclass__ = ProtobufState
+    Traceback (most recent call last):
+    ...
+    TypeError: Class ...FailedContact needs a protobuf_type attribute
+    
\ No newline at end of file



More information about the Checkins mailing list