[Checkins] SVN: keas.pbstate/trunk/s Added tests (2 of which are failing at the moment)
Shane Hathaway
shane at hathawaymix.org
Mon Jan 12 22:01:42 EST 2009
Log message for revision 94715:
Added tests (2 of which are failing at the moment)
Changed:
U keas.pbstate/trunk/setup.py
A keas.pbstate/trunk/src/keas/pbstate/README.txt
U keas.pbstate/trunk/src/keas/pbstate/meta.py
A keas.pbstate/trunk/src/keas/pbstate/testclasses.proto
A keas.pbstate/trunk/src/keas/pbstate/testclasses_pb2.py
A keas.pbstate/trunk/src/keas/pbstate/tests.py
-=-
Modified: keas.pbstate/trunk/setup.py
===================================================================
--- keas.pbstate/trunk/setup.py 2009-01-13 02:37:21 UTC (rev 94714)
+++ keas.pbstate/trunk/setup.py 2009-01-13 03:01:41 UTC (rev 94715)
@@ -19,7 +19,7 @@
version='0.1dev',
author='Shane Hathaway and the Zope Community',
author_email='zope-dev at zope.org',
- description='Method of storing object state in a Google Protocol Buffer',
+ description='Object state storage in a Google Protocol Buffer',
license='ZPL 2.1',
package_dir={'': 'src'},
Added: keas.pbstate/trunk/src/keas/pbstate/README.txt
===================================================================
--- keas.pbstate/trunk/src/keas/pbstate/README.txt (rev 0)
+++ keas.pbstate/trunk/src/keas/pbstate/README.txt 2009-01-13 03:01:41 UTC (rev 94715)
@@ -0,0 +1,178 @@
+
+=====
+Tests
+=====
+
+These are tests of keas.pbstate, a Python package that provides
+a method of storing object state in a Google Protocol Buffer.
+These tests also serve as basic documentation of this package.
+This package is designed to be compatible with ZODB, but ZODB is
+not required.
+
+These tests depend on the module named testclasses_pb2.py, which
+is generated from testclasses.proto using the following command:
+
+ protoc --python_out . *.proto
+
+Create a Contact class.
+
+ >>> import time
+ >>> from keas.pbstate.meta import ProtobufState
+ >>> from keas.pbstate.testclasses_pb2 import ContactPB
+ >>> class Contact(object):
+ ... __metaclass__ = ProtobufState
+ ... protobuf_type = ContactPB
+ ... def __init__(self):
+ ... self.create_time = int(time.time())
+ ...
+
+Create an instance of this class and verify the instance has the expected
+attributes.
+
+ >>> c = Contact()
+ >>> c.create_time > 0
+ True
+ >>> c.name
+ u''
+ >>> c.address.line1
+ u''
+ >>> c.address.country
+ u'United States'
+
+The instance also provides access to the protobuf message, its type (inherited
+from the class), and the references from the message.
+
+ >>> c.protobuf
+ <keas.pbstate.testclasses_pb2.ContactPB object at ...>
+ >>> c.protobuf_type
+ <class 'keas.pbstate.testclasses_pb2.ContactPB'>
+ >>> c.protobuf_refs
+ <keas.pbstate.meta.ProtobufReferences object at ...>
+
+Set and retrieve some of the attributes.
+
+ >>> c.name = u'John Doe'
+ >>> c.address.line1 = u'100 First Avenue'
+ >>> c.address.country = u'Canada'
+ >>> c.name
+ u'John Doe'
+ >>> c.address.country
+ u'Canada'
+
+Try to set one of the attributes to a value the protobuf message can't
+serialize.
+
+ >>> c.name = 100
+ Traceback (most recent call last):
+ ...
+ TypeError: 100 has type <type 'int'>, but expected one of: (<type 'str'>, <type 'unicode'>)
+ >>> c.name
+ u'John Doe'
+
+Try to set an attribute not declared in the .proto file.
+
+ >>> c.phone = u'555-1234'
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'Contact' object has no attribute 'phone'
+
+Try to serialize the object without providing all of the required fields.
+
+ >>> c.__getstate__()
+ Traceback (most recent call last):
+ ...
+ EncodeError: Required field AddressPB.city is not set.
+
+Finish filling out the required fields, then serialize.
+
+ >>> c.address.city = u'Toronto'
+ >>> c.create_time = 1001
+ >>> c.__getstate__()
+ ('\x08\xe9\x07\x12\x08John Doe\x1a#\n\x10100 First Avenue\x1a\x07Toronto2\x06Canada', {})
+
+Create a contact and fill in its state from c.
+
+ >>> c_dup = Contact.__new__(Contact)
+ >>> c_dup.__setstate__(c.__getstate__())
+ >>> c_dup.name
+ u'John Doe'
+ >>> c_dup.address.country
+ u'Canada'
+
+Create another contact, but this time provide no address information.
+
+ >>> c2 = Contact()
+ >>> c2.create_time = 1002
+ >>> c2.name = u'Mary Anne'
+ >>> c2.__getstate__()
+ ('\x08\xea\x07\x12\tMary Anne', {})
+
+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.
+
+ >>> c2.protobuf_refs[guardian_ref] = c
+
+Verify the reference is serialized correctly.
+
+ >>> data, targets = c2.__getstate__()
+ >>> targets[c2.guardians[0]._p_refid] is c
+ True
+
+
+Features Designed for ZODB
+--------------------------
+
+The _p_changed attribute, if it exists, is set to True whenever the protobuf
+changes. Here is the PersistentContact class, which has a _p_changed
+attribute.
+
+ >>> class FakePersistent(object):
+ ... __slots__ = ('_changed',)
+ ... def _get_changed(self):
+ ... return getattr(self, '_changed', False)
+ ... def _set_changed(self, value):
+ ... self._changed = value
+ ... _p_changed = property(_get_changed, _set_changed)
+ ...
+ >>> class PersistentContact(FakePersistent):
+ ... __metaclass__ = ProtobufState
+ ... protobuf_type = ContactPB
+ ...
+
+ >>> c3 = PersistentContact()
+ >>> c3.create_time = 1003
+ >>> c3.name = u'Snoopy'
+ >>> c3._p_changed = False
+
+Reading an attribute does not set _p_changed.
+
+ >>> c3.name
+ u'Snoopy'
+ >>> c3._p_changed
+ False
+
+Writing an attribute sets _p_changed.
+
+ >>> c3.name = u'Woodstock'
+ >>> c3._p_changed
+ True
+
+Adding to a repeated element sets _p_changed.
+
+ >>> c3._p_changed = False
+ >>> c3._p_changed
+ False
+ >>> c3.guardians.add()
+ <keas.pbstate.testclasses_pb2.Ref object at ...>
+ >>> c3._p_changed
+ True
Modified: keas.pbstate/trunk/src/keas/pbstate/meta.py
===================================================================
--- keas.pbstate/trunk/src/keas/pbstate/meta.py 2009-01-13 02:37:21 UTC (rev 94714)
+++ keas.pbstate/trunk/src/keas/pbstate/meta.py 2009-01-13 03:01:41 UTC (rev 94715)
@@ -213,13 +213,10 @@
# Arrange for class instances to always have the 'protobuf' and
# 'protobuf_refs' instance attributes. This is done by creating
# or overriding the __new__() method of the new class.
- parent__new__ = dct.get('__new__')
def __new__(subclass, *args, **kw):
# subclass is either created_class or a subclass of created_class.
- create = parent__new__
- if create is None:
- create = super(created_class, subclass).__new__
- instance = create(subclass, *args, **kw)
+ super_new = super(created_class, subclass).__new__
+ instance = super_new(subclass, *args, **kw)
instance.protobuf_refs = ProtobufReferences({})
instance.protobuf = subclass.protobuf_type()
if hasattr(instance, '_p_changed'):
Added: keas.pbstate/trunk/src/keas/pbstate/testclasses.proto
===================================================================
--- keas.pbstate/trunk/src/keas/pbstate/testclasses.proto (rev 0)
+++ keas.pbstate/trunk/src/keas/pbstate/testclasses.proto 2009-01-13 03:01:41 UTC (rev 94715)
@@ -0,0 +1,20 @@
+
+message Ref {
+ required uint32 _p_refid = 1;
+}
+
+message AddressPB {
+ required string line1 = 1;
+ optional string line2 = 2;
+ required string city = 3;
+ optional string state = 4;
+ optional string postal_code = 5;
+ required string country = 6 [default = 'United States'];
+}
+
+message ContactPB {
+ required uint64 create_time = 1;
+ required string name = 2;
+ optional AddressPB address = 3;
+ repeated Ref guardians = 4;
+}
Added: keas.pbstate/trunk/src/keas/pbstate/testclasses_pb2.py
===================================================================
--- keas.pbstate/trunk/src/keas/pbstate/testclasses_pb2.py (rev 0)
+++ keas.pbstate/trunk/src/keas/pbstate/testclasses_pb2.py 2009-01-13 03:01:41 UTC (rev 94715)
@@ -0,0 +1,149 @@
+#!/usr/bin/python2.4
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+
+from google.protobuf import descriptor
+from google.protobuf import message
+from google.protobuf import reflection
+from google.protobuf import service
+from google.protobuf import service_reflection
+from google.protobuf import descriptor_pb2
+
+
+
+_REF = descriptor.Descriptor(
+ name='Ref',
+ full_name='Ref',
+ filename='src/keas/pbstate/testclasses.proto',
+ containing_type=None,
+ fields=[
+ descriptor.FieldDescriptor(
+ name='_p_refid', full_name='Ref._p_refid', index=0,
+ number=1, type=13, cpp_type=3, label=2,
+ default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[], # TODO(robinson): Implement.
+ enum_types=[
+ ],
+ options=None)
+
+
+_ADDRESSPB = descriptor.Descriptor(
+ name='AddressPB',
+ full_name='AddressPB',
+ filename='src/keas/pbstate/testclasses.proto',
+ containing_type=None,
+ fields=[
+ descriptor.FieldDescriptor(
+ name='line1', full_name='AddressPB.line1', index=0,
+ number=1, type=9, cpp_type=9, label=2,
+ default_value=unicode("", "utf-8"),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='line2', full_name='AddressPB.line2', index=1,
+ number=2, type=9, cpp_type=9, label=1,
+ default_value=unicode("", "utf-8"),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='city', full_name='AddressPB.city', index=2,
+ number=3, type=9, cpp_type=9, label=2,
+ default_value=unicode("", "utf-8"),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='state', full_name='AddressPB.state', index=3,
+ number=4, type=9, cpp_type=9, label=1,
+ default_value=unicode("", "utf-8"),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='postal_code', full_name='AddressPB.postal_code', index=4,
+ number=5, type=9, cpp_type=9, label=1,
+ default_value=unicode("", "utf-8"),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='country', full_name='AddressPB.country', index=5,
+ number=6, type=9, cpp_type=9, label=2,
+ default_value=unicode("United States", "utf-8"),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[], # TODO(robinson): Implement.
+ enum_types=[
+ ],
+ options=None)
+
+
+_CONTACTPB = descriptor.Descriptor(
+ name='ContactPB',
+ full_name='ContactPB',
+ filename='src/keas/pbstate/testclasses.proto',
+ containing_type=None,
+ fields=[
+ descriptor.FieldDescriptor(
+ name='create_time', full_name='ContactPB.create_time', index=0,
+ number=1, type=4, cpp_type=4, label=2,
+ default_value=0,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='name', full_name='ContactPB.name', index=1,
+ number=2, type=9, cpp_type=9, label=2,
+ default_value=unicode("", "utf-8"),
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='address', full_name='ContactPB.address', index=2,
+ number=3, type=11, cpp_type=10, label=1,
+ default_value=None,
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ descriptor.FieldDescriptor(
+ name='guardians', full_name='ContactPB.guardians', index=3,
+ number=4, type=11, cpp_type=10, label=3,
+ default_value=[],
+ message_type=None, enum_type=None, containing_type=None,
+ is_extension=False, extension_scope=None,
+ options=None),
+ ],
+ extensions=[
+ ],
+ nested_types=[], # TODO(robinson): Implement.
+ enum_types=[
+ ],
+ options=None)
+
+
+_CONTACTPB.fields_by_name['address'].message_type = _ADDRESSPB
+_CONTACTPB.fields_by_name['guardians'].message_type = _REF
+
+class Ref(message.Message):
+ __metaclass__ = reflection.GeneratedProtocolMessageType
+ DESCRIPTOR = _REF
+
+class AddressPB(message.Message):
+ __metaclass__ = reflection.GeneratedProtocolMessageType
+ DESCRIPTOR = _ADDRESSPB
+
+class ContactPB(message.Message):
+ __metaclass__ = reflection.GeneratedProtocolMessageType
+ DESCRIPTOR = _CONTACTPB
+
Added: keas.pbstate/trunk/src/keas/pbstate/tests.py
===================================================================
--- keas.pbstate/trunk/src/keas/pbstate/tests.py (rev 0)
+++ keas.pbstate/trunk/src/keas/pbstate/tests.py 2009-01-13 03:01:41 UTC (rev 94715)
@@ -0,0 +1,27 @@
+##############################################################################
+#
+# Copyright (c) 2008 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+import unittest
+
+from zope.testing import doctest
+
+def test_suite():
+ return unittest.TestSuite([
+ doctest.DocFileSuite(
+ 'README.txt',
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+ ])
+
+if __name__ == '__main__':
+ unittest.main()
More information about the Checkins
mailing list