[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