Changeset 1003
- Timestamp:
- 02/11/08 21:42:26 (11 months ago)
- Location:
- pyamf/trunk
- Files:
-
- 7 modified
-
CHANGES.txt (modified) (1 diff)
-
pyamf/__init__.py (modified) (3 diffs)
-
pyamf/remoting/__init__.py (modified) (6 diffs)
-
pyamf/remoting/client/__init__.py (modified) (1 diff)
-
pyamf/tests/test_basic.py (modified) (2 diffs)
-
pyamf/tests/test_remoting.py (modified) (2 diffs)
-
pyamf/tests/test_sol.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
pyamf/trunk/CHANGES.txt
r961 r1003 6 6 of PyAMF. 7 7 8 0.1 rc1(unreleased)8 0.1 (unreleased) 9 9 ------------------- 10 10 11 - a new error handling api useful for registering custom exception classes 12 (Ticket:185) 13 - when a client receives a remoting error, an exception is generated (Ticket:167) 11 14 - expose_request per service control vastly improved (Ticket:169) 12 15 - Authentication per service control vastly improved (Ticket:166) -
pyamf/trunk/pyamf/__init__.py
r975 r1003 44 44 TYPE_MAP = {} 45 45 46 #: Maps error classes to string codes 47 ERROR_CLASS_MAP = {} 48 46 49 #: Specifies that objects are serialized using AMF for ActionScript 1.0 and 2.0. 47 50 AMF0 = 0 … … 89 92 """ 90 93 91 class EOStream( DecodeError):94 class EOStream(BaseError): 92 95 """ 93 96 Raised if the data stream has come to a natural end. … … 992 995 return declaration 993 996 997 def add_error_class(klass, code): 998 """ 999 Maps an exception class to a string code. Used to map remoting onStatus 1000 objects to an exception class so that an exception can be built to 1001 represent that error. 1002 1003 class AuthenticationError(Exception): 1004 pass 1005 1006 An example: add_error_class(AuthenticationError, 'Auth.Failed') 1007 """ 1008 if not isinstance(code, basestring): 1009 code = str(code) 1010 1011 if not isinstance(klass, (type, types.ClassType)): 1012 raise TypeError, "klass must be a class type" 1013 1014 mro = util.get_mro(klass) 1015 1016 if not Exception in util.get_mro(klass): 1017 raise TypeError, 'error classes must subclass the __builtin__.Exception class' 1018 1019 if code in ERROR_CLASS_MAP.keys(): 1020 raise ValueError, 'Code %s is already registered' % code 1021 1022 ERROR_CLASS_MAP[code] = klass 1023 1024 def remove_error_class(klass): 1025 """ 1026 Removes a class from ERROR_CLASS_MAP 1027 """ 1028 if isinstance(klass, basestring): 1029 if not klass in ERROR_CLASS_MAP.keys(): 1030 raise ValueError, 'Code %s is not registered' % klass 1031 elif isinstance(klass, (type, types.ClassType)): 1032 classes = ERROR_CLASS_MAP.values() 1033 if not klass in classes: 1034 raise ValueError, 'Class %s is not registered' % klass 1035 1036 klass = ERROR_CLASS_MAP.keys()[classes.index(klass)] 1037 else: 1038 raise TypeError, "Invalid type, expected class or string" 1039 1040 del ERROR_CLASS_MAP[klass] 1041 994 1042 register_adapters() -
pyamf/trunk/pyamf/remoting/__init__.py
r946 r1003 51 51 CONTENT_TYPE = 'application/x-amf' 52 52 53 ERROR_CALL_FAILED, = range(1) 54 ERROR_CODES = { 55 ERROR_CALL_FAILED: 'Server.Call.Failed' 56 } 57 53 58 class RemotingError(pyamf.BaseError): 54 59 """ 55 60 Generic remoting error class. 56 61 """ 62 63 class RemotingCallFailed(RemotingError): 64 """ 65 Raised if Server.Call.Failed received 66 """ 67 68 pyamf.add_error_class(RemotingCallFailed, ERROR_CODES[ERROR_CALL_FAILED]) 57 69 58 70 class HeaderCollection(dict): … … 233 245 return x + '>' 234 246 247 def raiseException(self): 248 """ 249 Raises an exception based on the fault object. There is no traceback 250 available. 251 """ 252 raise get_exception_from_fault(self), self.description, None 253 235 254 pyamf.register_class(BaseFault, 236 255 attrs=['level', 'code', 'type', 'details', 'description']) … … 243 262 level = 'error' 244 263 245 pyamf.register_class(ErrorFault, 246 attrs=['level', 'code', 'type', 'details', 'description']) 264 pyamf.register_class(ErrorFault) 247 265 248 266 def _read_header(stream, decoder, strict=False): … … 443 461 return STATUS_CODES[status] 444 462 445 def get_fault_class(level): 463 def get_fault_class(level, **kwargs): 464 code = kwargs.get('code', ) 446 465 if level == 'error': 447 466 return ErrorFault … … 464 483 e[x] = y 465 484 466 return get_fault_class(level )(**e)485 return get_fault_class(level, **e)(**e) 467 486 468 487 def decode(stream, context=None, strict=False): … … 569 588 570 589 return stream 590 591 def get_exception_from_fault(fault): 592 # XXX nick: threading problems here? 593 try: 594 return pyamf.ERROR_CLASS_MAP[fault.code] 595 except KeyError: 596 # default to RemotingError 597 return RemotingError -
pyamf/trunk/pyamf/remoting/client/__init__.py
r960 r1003 144 144 self.response = response 145 145 self.result = self.response.body 146 147 if isinstance(self.result, remoting.ErrorFault): 148 self.result.raiseException() 146 149 147 150 def _get_result(self): -
pyamf/trunk/pyamf/tests/test_basic.py
r853 r1003 612 612 self.assertEquals(td, td2) 613 613 614 class ErrorClassMapTestCase(unittest.TestCase): 615 """ 616 I test all functionality related to manipulating L{pyamf.ERROR_CLASS_MAP} 617 """ 618 619 def setUp(self): 620 self.map_copy = pyamf.ERROR_CLASS_MAP.copy() 621 622 def tearDown(self): 623 pyamf.ERROR_CLASS_MAP = self.map_copy 624 625 def test_add(self): 626 class A: 627 pass 628 629 class B(Exception): 630 pass 631 632 self.assertRaises(TypeError, pyamf.add_error_class, None, 'a') 633 634 # class A does not sub-class Exception 635 self.assertRaises(TypeError, pyamf.add_error_class, A, 'a') 636 637 pyamf.add_error_class(B, 'b') 638 self.assertEquals(pyamf.ERROR_CLASS_MAP['b'], B) 639 640 pyamf.add_error_class(B, 'a') 641 self.assertEquals(pyamf.ERROR_CLASS_MAP['a'], B) 642 643 class C(Exception): 644 pass 645 646 self.assertRaises(ValueError, pyamf.add_error_class, C, 'b') 647 648 def test_remove(self): 649 class B(Exception): 650 pass 651 652 pyamf.ERROR_CLASS_MAP['abc'] = B 653 654 self.assertRaises(TypeError, pyamf.remove_error_class, None) 655 656 pyamf.remove_error_class('abc') 657 self.assertFalse('abc' in pyamf.ERROR_CLASS_MAP.keys()) 658 self.assertRaises(KeyError, pyamf.ERROR_CLASS_MAP.__getitem__, 'abc') 659 660 pyamf.ERROR_CLASS_MAP['abc'] = B 661 662 pyamf.remove_error_class(B) 663 664 self.assertRaises(KeyError, pyamf.ERROR_CLASS_MAP.__getitem__, 'abc') 665 self.assertRaises(ValueError, pyamf.remove_error_class, B) 666 self.assertRaises(ValueError, pyamf.remove_error_class, 'abc') 667 614 668 def suite(): 615 669 suite = unittest.TestSuite() … … 623 677 suite.addTest(unittest.makeSuite(ClassLoaderTestCase)) 624 678 suite.addTest(unittest.makeSuite(TypeMapTestCase)) 679 suite.addTest(unittest.makeSuite(ErrorClassMapTestCase)) 625 680 626 681 return suite -
pyamf/trunk/pyamf/tests/test_remoting.py
r847 r1003 270 270 '\x00\x0c\n\x00\x00\x00\x01\x02\x00\x04spam') 271 271 272 class FaultTestCase(unittest.TestCase): 273 def test_exception(self): 274 x = remoting.get_fault({'level': 'error', 'code': 'Server.Call.Failed'}) 275 276 self.assertRaises(remoting.RemotingCallFailed, x.raiseException) 277 272 278 def suite(): 273 279 """ … … 279 285 suite.addTest(unittest.makeSuite(EncoderTestCase)) 280 286 suite.addTest(unittest.makeSuite(StrictEncodingTestCase)) 287 suite.addTest(unittest.makeSuite(FaultTestCase)) 281 288 282 289 from pyamf.tests.remoting import test_client, test_remoteobject -
pyamf/trunk/pyamf/tests/test_sol.py
r973 r1003 12 12 """ 13 13 14 import unittest, os, os.path 14 import unittest, os, os.path, warnings 15 15 16 16 import pyamf 17 17 from pyamf import amf0, util, sol 18 19 warnings.simplefilter('ignore', RuntimeWarning) 18 20 19 21 class DecoderTestCase(unittest.TestCase):
