[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pyamf-commits
Subject:    [pyamf-commits] r1789 - in pyamf/trunk: . cpyamf pyamf
From:       commits () pyamf ! org (commits () pyamf ! org)
Date:       2008-11-23 20:44:01
Message-ID: 20081123194345.194A87BC026 () mail ! collab ! com
[Download RAW message or body]

Author: nick
Date: 2008-11-23 20:43:44 +0100 (Sun, 23 Nov 2008)
New Revision: 1789

Modified:
   pyamf/trunk/
   pyamf/trunk/cpyamf/
   pyamf/trunk/pyamf/__init__.py
   pyamf/trunk/pyamf/amf0.py
   pyamf/trunk/pyamf/amf3.py
   pyamf/trunk/pyamf/remoting/__init__.py
   pyamf/trunk/pyamf/tests/test_amf0.py
   pyamf/trunk/pyamf/tests/test_amf3.py
   pyamf/trunk/pyamf/tests/test_basic.py
Log:
Added a strict mode to the decoders to attempt to decode objects that have no \
registered class. Defaults to False. Switch to True for the old behaviour.

Fixes: #378
Author: nick
Reviewer: thijs



Property changes on: pyamf/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /pyamf/branches/adapter-helper-350:1638-1668
/pyamf/branches/bytearray-amf0-379:1752-1765
/pyamf/branches/class-def-ref-341:1594-1597
/pyamf/branches/class-init-args-322:1527-1528,1530-1532
/pyamf/branches/client-http-headers-api-337:1604-1609
/pyamf/branches/error-stacktrace-331:1624-1644
/pyamf/branches/gae-key-307:1603-1760
/pyamf/branches/gerard-celementtree-364:1687-1697
/pyamf/branches/gerard-cpyamf-225:1611-1661
/pyamf/branches/immutable-set-280:1746-1764
/pyamf/branches/nan-failures-345:1624-1640
/pyamf/branches/raise-python3.0-351:1642-1647
/pyamf/branches/register_class-args-check-334:1542-1592
/pyamf/branches/remoting-request-context-309-2:1595-1607
/pyamf/branches/slots-347:1675-1781
/pyamf/branches/tempfile-338:1588-1596
/pyamf/branches/win-sol-test-failures-344:1624-1645
/pyamf/trunk:361-1524,1527-1540,1542-1582,1588-1593,1595-1602,1673-1686,1699-1751
/trunk:2-360
   + /pyamf/branches/adapter-helper-350:1638-1668
/pyamf/branches/bytearray-amf0-379:1752-1765
/pyamf/branches/class-def-ref-341:1594-1597
/pyamf/branches/class-init-args-322:1527-1528,1530-1532
/pyamf/branches/client-http-headers-api-337:1604-1609
/pyamf/branches/error-stacktrace-331:1624-1644
/pyamf/branches/gae-key-307:1603-1760
/pyamf/branches/gerard-celementtree-364:1687-1697
/pyamf/branches/gerard-cpyamf-225:1611-1661
/pyamf/branches/immutable-set-280:1746-1764
/pyamf/branches/nan-failures-345:1624-1640
/pyamf/branches/raise-python3.0-351:1642-1647
/pyamf/branches/register_class-args-check-334:1542-1592
/pyamf/branches/remoting-request-context-309-2:1595-1607
/pyamf/branches/slots-347:1675-1781
/pyamf/branches/strict-decoding-378:1743-1788
/pyamf/branches/tempfile-338:1588-1596
/pyamf/branches/win-sol-test-failures-344:1624-1645
/pyamf/trunk:361-1524,1527-1540,1542-1582,1588-1593,1595-1602,1699-1751
/trunk:2-360


Property changes on: pyamf/trunk/cpyamf
___________________________________________________________________
Modified: svn:mergeinfo
   - /pyamf/branches/adapter-helper-350/cpyamf:1638-1668
/pyamf/branches/bytearray-amf0-379/cpyamf:1752-1765
/pyamf/branches/class-def-ref-341/cpyamf:1594-1597
/pyamf/branches/class-init-args-322/cpyamf:1527-1528,1530-1532
/pyamf/branches/client-http-headers-api-337/cpyamf:1604-1609
/pyamf/branches/error-stacktrace-331/cpyamf:1624-1644
/pyamf/branches/gerard-celementtree-364/cpyamf:1687-1697
/pyamf/branches/gerard-cpyamf-225/cpyamf:1611-1661
/pyamf/branches/immutable-set-280/cpyamf:1746-1764
/pyamf/branches/nan-failures-345/cpyamf:1624-1640
/pyamf/branches/raise-python3.0-351/cpyamf:1642-1647
/pyamf/branches/register_class-args-check-334/cpyamf:1542-1592
/pyamf/branches/remoting-request-context-309-2/cpyamf:1595-1607
/pyamf/branches/slots-347/cpyamf:1675-1781
/pyamf/branches/tempfile-338/cpyamf:1588-1596
/pyamf/branches/win-sol-test-failures-344/cpyamf:1624-1645
/pyamf/trunk/cpyamf:1525-1526,1541,1583-1587,1594,1624-1641,1673-1686,1699-1751
   + /pyamf/branches/adapter-helper-350/cpyamf:1638-1668
/pyamf/branches/bytearray-amf0-379/cpyamf:1752-1765
/pyamf/branches/class-def-ref-341/cpyamf:1594-1597
/pyamf/branches/class-init-args-322/cpyamf:1527-1528,1530-1532
/pyamf/branches/client-http-headers-api-337/cpyamf:1604-1609
/pyamf/branches/error-stacktrace-331/cpyamf:1624-1644
/pyamf/branches/gerard-celementtree-364/cpyamf:1687-1697
/pyamf/branches/gerard-cpyamf-225/cpyamf:1611-1661
/pyamf/branches/immutable-set-280/cpyamf:1746-1764
/pyamf/branches/nan-failures-345/cpyamf:1624-1640
/pyamf/branches/raise-python3.0-351/cpyamf:1642-1647
/pyamf/branches/register_class-args-check-334/cpyamf:1542-1592
/pyamf/branches/remoting-request-context-309-2/cpyamf:1595-1607
/pyamf/branches/slots-347/cpyamf:1675-1781
/pyamf/branches/strict-decoding-378/cpyamf:1743-1788
/pyamf/branches/tempfile-338/cpyamf:1588-1596
/pyamf/branches/win-sol-test-failures-344/cpyamf:1624-1645
/pyamf/trunk/cpyamf:1525-1526,1541,1583-1587,1594,1624-1641,1673-1686,1699-1751

Modified: pyamf/trunk/pyamf/__init__.py
===================================================================
--- pyamf/trunk/pyamf/__init__.py	2008-11-23 19:30:50 UTC (rev 1788)
+++ pyamf/trunk/pyamf/__init__.py	2008-11-23 19:43:44 UTC (rev 1789)
@@ -467,6 +467,43 @@
         """
         return self.klass(*args, **kwargs)
 
+class TypedObject(dict):
+    """
+    This class is used when a strongly typed object is decoded but there is no
+    registered class to apply it to.
+
+    This object can only be used for 'simple' streams - i.e. not externalized
+    data. If encountered, a L{DecodeError} will be thrown.
+
+    @ivar alias: The alias of the typed object.
+    @ivar alias: C{unicode}
+    """
+
+    def __init__(self, alias):
+        dict.__init__(self)
+
+        self.alias = alias
+
+    def __readamf__(self, o):
+        raise DecodeError('Unable to decode an externalised stream.\n\nThe '
+            'class alias \'%s\' was found and because strict mode is False an'
+            ' attempt was made to decode the object automatically. To decode '
+            'this stream, a registered class with the alias and a '
+            'corresponding __readamf__ method will be required.' % (
+                self.alias,))
+
+    def __writeamf__(self, o):
+        raise EncodeError('Unable to encode an externalised stream.\n\nThe '
+            'class alias \'%s\' was found and because strict mode is False an'
+            'attempt was made to encode the object automatically. To encode '
+            'this stream, a registered class with the alias and a '
+            'corresponding __readamf__ method will be required.' % (
+                self.alias,))
+
+class TypedObjectClassAlias(ClassAlias):
+    def createInstance(self):
+        return TypedObject(self.alias)
+
 class BaseDecoder(object):
     """
     Base AMF decoder.
@@ -477,11 +514,15 @@
     @type type_map: C{list}
     @ivar stream: The underlying data stream.
     @type stream: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
+    @ivar strict: Defines how strict the decoding should be. For the time
+        being this relates to typed objects in the stream that do not have a
+        registered alias. Introduced in 0.4.
+    @type strict: C{bool}
     """
     context_class = BaseContext
     type_map = {}
 
-    def __init__(self, data=None, context=None):
+    def __init__(self, data=None, context=None, strict=False):
         """
         @type   data: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
         @param  data: Data stream.
@@ -504,6 +545,8 @@
             raise TypeError("context must be of type %s.%s" % (
                 self.context_class.__module__, self.context_class.__name__))
 
+        self.strict = strict
+
     def readType(self):
         """
         Override in a subclass.
@@ -901,7 +944,7 @@
     except UnknownClassAlias:
         return False
 
-def decode(stream, encoding=AMF0, context=None):
+def decode(stream, encoding=AMF0, context=None, strict=False):
     """
     A generator function to decode a datastream.
 
@@ -914,7 +957,7 @@
     @param  context: Context.
     @return: Each element in the stream.
     """
-    decoder = _get_decoder_class(encoding)(stream, context)
+    decoder = _get_decoder_class(encoding)(stream, context, strict)
 
     while 1:
         try:
@@ -950,8 +993,8 @@
 
     return stream
 
-def get_decoder(encoding, data=None, context=None):
-    return _get_decoder_class(encoding)(data=data, context=context)
+def get_decoder(encoding, data=None, context=None, strict=False):
+    return _get_decoder_class(encoding)(data=data, context=context, strict=strict)
 
 def _get_decoder_class(encoding):
     """
@@ -1197,3 +1240,5 @@
     ALIAS_TYPES[klass] = args
 
 register_adapters()
+
+register_alias_type(TypedObjectClassAlias, TypedObject)
\ No newline at end of file

Modified: pyamf/trunk/pyamf/amf0.py
===================================================================
--- pyamf/trunk/pyamf/amf0.py	2008-11-23 19:30:50 UTC (rev 1788)
+++ pyamf/trunk/pyamf/amf0.py	2008-11-23 19:43:44 UTC (rev 1789)
@@ -301,9 +301,18 @@
         @see: L{load_class<pyamf.load_class>}
         """
         classname = self.readString()
-        alias = pyamf.load_class(classname)
+        alias = None
 
-        ret = alias.createInstance()
+        try:
+            alias = pyamf.load_class(classname)
+
+            ret = alias.createInstance()
+        except pyamf.UnknownClassAlias:
+            if self.strict:
+                raise
+
+            ret = pyamf.TypedObject(classname)
+
         self.context.addObject(ret)
         self._readObject(ret, alias)
 
@@ -321,7 +330,7 @@
 
             self.context.amf3_context = amf3.Context()
 
-        decoder = pyamf._get_decoder_class(pyamf.AMF3)(self.stream, \
self.context.amf3_context) +        decoder = \
pyamf._get_decoder_class(pyamf.AMF3)(self.stream, self.context.amf3_context, \
strict=self.strict)  
         element = decoder.readElement()
         self.context.addAMF3Object(element)
@@ -774,7 +783,7 @@
         self.writeType(ASTypes.AMF3)
         encoder.writeElement(data)
 
-def decode(stream, context=None):
+def decode(stream, context=None, strict=False):
     """
     A helper function to decode an AMF0 datastream.
 
@@ -783,7 +792,7 @@
     @type   context: L{Context<pyamf.amf0.Context>}
     @param  context: AMF0 Context.
     """
-    decoder = Decoder(stream, context)
+    decoder = Decoder(stream, context, strict=strict)
 
     while 1:
         try:

Modified: pyamf/trunk/pyamf/amf3.py
===================================================================
--- pyamf/trunk/pyamf/amf3.py	2008-11-23 19:30:50 UTC (rev 1788)
+++ pyamf/trunk/pyamf/amf3.py	2008-11-23 19:43:44 UTC (rev 1789)
@@ -1097,7 +1097,20 @@
         if class_ref:
             class_def = self.context.getClassDefinition(ref)
         else:
-            class_def = ClassDefinition(self.readString(), encoding=ref & 0x03)
+            class_name = self.readString()
+
+            if class_name == '':
+                class_name = None
+            else:
+                try:
+                    class_name = pyamf.get_class_alias(class_name)
+                except pyamf.UnknownClassAlias:
+                    if self.strict:
+                        raise
+
+                    class_name = pyamf.TypedObjectClassAlias(pyamf.TypedObject, \
class_name) +
+            class_def = ClassDefinition(class_name, encoding=ref & 0x03)
             self.context.addClassDefinition(class_def)
 
         return class_ref, class_def, ref >> 2
@@ -1717,7 +1730,7 @@
 
         self._writeString(util.ET.tostring(n, 'utf-8'), False)
 
-def decode(stream, context=None):
+def decode(stream, context=None, strict=False):
     """
     A helper function to decode an AMF3 datastream.
 
@@ -1726,7 +1739,7 @@
     @type   context: L{Context}
     @param  context: Context.
     """
-    decoder = Decoder(stream, context)
+    decoder = Decoder(stream, context, strict)
 
     while 1:
         try:

Modified: pyamf/trunk/pyamf/remoting/__init__.py
===================================================================
--- pyamf/trunk/pyamf/remoting/__init__.py	2008-11-23 19:30:50 UTC (rev 1788)
+++ pyamf/trunk/pyamf/remoting/__init__.py	2008-11-23 19:43:44 UTC (rev 1789)
@@ -595,7 +595,7 @@
     else:
         context = copy.copy(context)
 
-    decoder = pyamf._get_decoder_class(pyamf.AMF0)(stream, context=context)
+    decoder = pyamf._get_decoder_class(pyamf.AMF0)(stream, context=context, \
strict=strict)  msg.clientType = stream.read_uchar()
 
     header_count = stream.read_ushort()
@@ -632,7 +632,7 @@
     @type strict: C{bool}
     @param strict: Determines whether encoding should be strict. Specifically
         header/body lengths will be written correctly, instead of the default 0.
-        Default is C{False}.
+        Default is C{False}. Introduced in 0.4.
     @rtype: C{StringIO}
     @return: File object.
     """

Modified: pyamf/trunk/pyamf/tests/test_amf0.py
===================================================================
--- pyamf/trunk/pyamf/tests/test_amf0.py	2008-11-23 19:30:50 UTC (rev 1788)
+++ pyamf/trunk/pyamf/tests/test_amf0.py	2008-11-23 19:43:44 UTC (rev 1789)
@@ -690,6 +690,34 @@
 
         self.assertEquals(foo.foo, 'bar')
 
+    def test_not_strict(self):
+        self.assertFalse(self.decoder.strict)
+
+        # write a typed object to the stream
+        self.buf.write('\x10\x00\tspam.eggs\x00\x03foo\x02\x00\x03bar\x00\x00\t')
+        self.buf.seek(0)
+
+        self.assertFalse('spam.eggs' in pyamf.CLASS_CACHE)
+
+        obj = self.decoder.readElement()
+
+        self.assertTrue(isinstance(obj, pyamf.TypedObject))
+        self.assertEquals(obj.alias, 'spam.eggs')
+        self.assertEquals(obj, {'foo': 'bar'})
+
+    def test_strict(self):
+        self.decoder.strict = True
+
+        self.assertTrue(self.decoder.strict)
+
+        # write a typed object to the stream
+        self.buf.write('\x10\x00\tspam.eggs\x00\x03foo\x02\x00\x03bar\x00\x00\t')
+        self.buf.seek(0)
+
+        self.assertFalse('spam.eggs' in pyamf.CLASS_CACHE)
+
+        self.assertRaises(pyamf.UnknownClassAlias, self.decoder.readElement)
+
     def test_slots(self):
         class Person(object):
             __slots__ = ('family_name', 'given_name')

Modified: pyamf/trunk/pyamf/tests/test_amf3.py
===================================================================
--- pyamf/trunk/pyamf/tests/test_amf3.py	2008-11-23 19:30:50 UTC (rev 1788)
+++ pyamf/trunk/pyamf/tests/test_amf3.py	2008-11-23 19:43:44 UTC (rev 1789)
@@ -913,6 +913,34 @@
         self.assertTrue(class_def in self.context.class_defs)
         self.context.class_defs.remove(class_def)
 
+    def test_not_strict(self):
+        self.assertFalse(self.decoder.strict)
+
+        # write a typed object to the stream
+        self.buf.write('\n\x0b\x13spam.eggs\x07foo\x06\x07bar\x01')
+        self.buf.seek(0)
+
+        self.assertFalse('spam.eggs' in pyamf.CLASS_CACHE)
+
+        obj = self.decoder.readElement()
+
+        self.assertTrue(isinstance(obj, pyamf.TypedObject))
+        self.assertEquals(obj.alias, 'spam.eggs')
+        self.assertEquals(obj, {'foo': 'bar'})
+
+    def test_strict(self):
+        self.decoder.strict = True
+
+        self.assertTrue(self.decoder.strict)
+
+        # write a typed object to the stream
+        self.buf.write('\n\x0b\x13spam.eggs\x07foo\x06\x07bar\x01')
+        self.buf.seek(0)
+
+        self.assertFalse('spam.eggs' in pyamf.CLASS_CACHE)
+
+        self.assertRaises(pyamf.UnknownClassAlias, self.decoder.readElement)
+
     def test_slots(self):
         class Person(object):
             __slots__ = ('family_name', 'given_name')

Modified: pyamf/trunk/pyamf/tests/test_basic.py
===================================================================
--- pyamf/trunk/pyamf/tests/test_basic.py	2008-11-23 19:30:50 UTC (rev 1788)
+++ pyamf/trunk/pyamf/tests/test_basic.py	2008-11-23 19:43:44 UTC (rev 1789)
@@ -328,14 +328,16 @@
         self.assertRaises(ValueError, pyamf.get_decoder, 'spam')
 
         context = amf0.Context()
-        decoder = pyamf.get_decoder(pyamf.AMF0, data='123', context=context)
+        decoder = pyamf.get_decoder(pyamf.AMF0, data='123', context=context, \
strict=True)  self.assertEquals(decoder.stream.getvalue(), '123')
         self.assertEquals(decoder.context, context)
+        self.assertTrue(decoder.strict)
 
         context = amf3.Context()
-        decoder = pyamf.get_decoder(pyamf.AMF3, data='456', context=context)
+        decoder = pyamf.get_decoder(pyamf.AMF3, data='456', context=context, \
strict=True)  self.assertEquals(decoder.stream.getvalue(), '456')
         self.assertEquals(decoder.context, context)
+        self.assertTrue(decoder.strict)
 
     def test_get_encoder(self):
         from pyamf import amf0, amf3
@@ -776,6 +778,13 @@
         self.assertEquals(alias.__class__, DummyAlias)
         self.assertEquals(alias.klass, B)
 
+class TypedObjectTestCase(unittest.TestCase):
+    def test_externalised(self):
+        o = pyamf.TypedObject(None)
+
+        self.assertRaises(pyamf.DecodeError, o.__readamf__, None)
+        self.assertRaises(pyamf.EncodeError, o.__writeamf__, None)
+
 def suite():
     suite = unittest.TestSuite()
 
@@ -790,6 +799,7 @@
     suite.addTest(unittest.makeSuite(ErrorClassMapTestCase))
     suite.addTest(unittest.makeSuite(RegisterAliasTypeTestCase))
     suite.addTest(unittest.makeSuite(BaseContextTestCase))
+    suite.addTest(unittest.makeSuite(TypedObjectTestCase))
 
     return suite
 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic