errors.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. # File: errors.py
  2. # Library: DOPAL - DO Python Azureus Library
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; version 2 of the License.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details ( see the COPYING file ).
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. '''
  17. Module containing all errors defined in DOPAL.
  18. '''
  19. def as_error(error, error_class, **kwargs):
  20. if not isinstance(error, error_class):
  21. error = error_class(error=error, **kwargs)
  22. return error
  23. def raise_as(error, error_class, **kwargs):
  24. import sys
  25. raise as_error(error, error_class, **kwargs), None, sys.exc_info()[2]
  26. class DopalError(Exception):
  27. "Subclass of all errors in the Dopal library"
  28. def __init__(self, *args, **kwargs):
  29. if len(args) + len(kwargs) > 3:
  30. raise ValueError, "DopalError.__init__ takes at most 3 arguments - %s positional argument(s) given, %s keyword argument(s) given" % (len(args), len(kwargs))
  31. # Filters out invalid keywords.
  32. from dopal.utils import handle_kwargs
  33. handle_kwargs(kwargs, error=None, obj=None, text=None)
  34. error = obj = text = None
  35. has_error = has_object = has_text = False
  36. import types
  37. for kwname, kwvalue in kwargs.items():
  38. if kwname == 'error':
  39. if not isinstance(kwvalue, Exception):
  40. msg = "'error' keyword argument is not Exception: %r"
  41. raise TypeError, msg % (kwvalue,)
  42. has_error = True
  43. error = kwvalue
  44. elif kwname == 'text':
  45. if not isinstance(kwvalue, types.StringTypes):
  46. msg = "'text' keyword argument is not a String type: %r"
  47. raise TypeError, msg % (kwvalue,)
  48. has_text = True
  49. text = kwvalue
  50. else: # kwname == 'obj'
  51. has_object = True
  52. obj = kwvalue
  53. import types
  54. for arg in args:
  55. if isinstance(arg, Exception) and not has_error:
  56. has_error = True
  57. error = arg
  58. elif isinstance(arg, types.StringTypes) and not has_text:
  59. has_text = True
  60. text = arg
  61. else:
  62. if has_object:
  63. msg = "could not determine Dopal argument type for %r"
  64. raise TypeError, msg % (arg,)
  65. has_object = True
  66. obj = arg
  67. dopal_arg_tuple = args
  68. if kwargs:
  69. dopal_arg_tuple += tuple(kwargs.values())
  70. dopal_arg_dict = {}
  71. if has_error:
  72. dopal_arg_dict['error'] = error
  73. if has_object:
  74. dopal_arg_dict['object'] = obj
  75. if has_text:
  76. dopal_arg_dict['text'] = text
  77. self.dopal_arg_tuple = dopal_arg_tuple
  78. self.dopal_arg_dict = dopal_arg_dict
  79. self.error = error
  80. self.obj = obj
  81. self.text = text
  82. self.has_object = has_object
  83. self.has_error = has_error
  84. self.has_text = has_text
  85. #super(DopalError, self).__init__(dopal_arg_tuple)
  86. Exception.__init__(self, *dopal_arg_tuple)
  87. def __str__(self):
  88. # Allow the subclass to render the string if:
  89. # 1) self.args is not the tuple that this class passed to the super
  90. # constructor; or
  91. # 2) We have 2 or more values given to us - the default behaviour for
  92. # rendering the string by the superclass for one or no arguments
  93. # is fine.
  94. if self.args != self.dopal_arg_tuple or \
  95. len(self.args) < 2:
  96. #return super(DopalError, self).__str__()
  97. return Exception.__str__(self)
  98. if not self.has_error:
  99. tmpl = "%(text)s (%(object)r)"
  100. elif not self.has_object:
  101. tmpl = "%(text)s - %(error)s"
  102. elif not self.has_text:
  103. tmpl = "%(error)s (%(object)r)"
  104. else:
  105. tmpl = "%(text)s - %(error)s (%(object)r)"
  106. return tmpl % self.dopal_arg_dict
  107. def __repr__(self):
  108. arg_parts = ["%s=%r" % item_tpl for item_tpl in self.dopal_arg_dict.items()]
  109. return "%s(%s)" % (self.__class__.__name__, ', '.join(arg_parts))
  110. # An alternative to str() of the object - this will make a string of the
  111. # form:
  112. # ErrorClassName: <error output>
  113. #
  114. # (This is similar to the last line you see of a traceback). The main
  115. # difference here is that we will use the class name of the internal error
  116. # if there is one, otherwise we will use the class name of the object
  117. # itself.
  118. #
  119. # The error output will be the same string as you get when you apply str()
  120. # to this object.
  121. #
  122. # Setting use_error to False will force it to always ignore the internal
  123. # error.
  124. def to_error_string(self, use_error=True):
  125. if use_error and self.has_error:
  126. error_to_use = self.error
  127. else:
  128. error_to_use = self
  129. error_output = str(self)
  130. result = error_to_use.__class__.__name__
  131. if error_output:
  132. result += ": " + error_output
  133. return result
  134. #---- core module ----#
  135. class LinkError(DopalError):
  136. "Error communicating with Azureus (low-level)"
  137. class RemoteError(DopalError): # Base class.
  138. "Error reported by Azureus"
  139. class RemoteInvocationError(RemoteError): # Base class.
  140. "Unable to invoke remote method"
  141. class NoSuchMethodError(RemoteInvocationError):
  142. """
  143. This error is thrown when Azureus reports that the requested method does
  144. not exist, or is not allowed.
  145. A NoSuchMethodError is a representation of the response Azureus returns
  146. when trying to invoke a method, but is unable to map the requested method
  147. to a method it can actually invoke.
  148. Causes
  149. ======
  150. There are various reasons why this error may occur, but here are the
  151. most likely.
  152. Wrong method requested
  153. ----------------------
  154. The wrong method signature was used - this is possible for a variety
  155. of reasons (though it isn't likely). Check that the method you want to
  156. use is the same one being reported in the NoSuchMethodError instance.
  157. Method not available in this version of Azureus
  158. -----------------------------------------------
  159. This method may not be available in the version of Azureus you are
  160. using - although DOPAL normally supports all methods made available in
  161. the latest beta release, this error will occur if the version of Azureus
  162. does not support that method.
  163. XML/HTTP request processor may not support this method
  164. ------------------------------------------------------
  165. The request processor used by Azureus may not be able to resolve that
  166. method. Versions 2.3.0.6 and older only allow a small subset of methods
  167. defined in the plugin API to be called. Version 2.4.0.0 (as well as some
  168. later beta versions of 2.3.0.7) have been changed to allow any method to
  169. be called. To enable this, go to the XML/HTTP plugin configuration page,
  170. and tick the I{"Advanced Settings -> Use generic classes"} setting.
  171. Non read-only method requested, but XML/HTTP in view mode
  172. ---------------------------------------------------------
  173. The XML/HTTP plugin in Azureus is set up to be in "view" mode, so only
  174. certain methods are allowed. Note - if you are unable to call a method
  175. which you think should be allowed in read only mode, contact the
  176. developers of the XML/HTTP plugin.
  177. @ivar obj: This will be a string which describes the method signature
  178. which was requested - for example::
  179. getDownloads
  180. setPosition[int]
  181. setTorrentAttribute[TorrentAttribute,String]
  182. """
  183. def __init__(self, method_sig):
  184. """
  185. Creates a new NoSuchMethodError instance.
  186. """
  187. RemoteInvocationError.__init__(self, obj=method_sig)
  188. class NoObjectIDGivenError(DopalError, ValueError):
  189. "No object ID given when needed"
  190. class NoEstablishedConnectionError(DopalError, TypeError):
  191. "Connection object has no valid connection established"
  192. # Raised by generate_remote_error (which means it is indirectly raised in
  193. # AzureusConnection.invoke_remote_method).
  194. #
  195. # These errors are masked by ExtendedAzureusConnection.invoke_remote_method
  196. # who throw one of the subclass errors (InvalidRemoteObjectError and
  197. # InvalidConnectionIDError).
  198. #
  199. # This error shouldn't arise if you are using a ExtendedAzureusConnection or
  200. # higher-level connection object.
  201. class InvalidObjectIDError(RemoteInvocationError):
  202. "Invalid remote object ID given (bad object or bad connection)"
  203. class InvalidRemoteObjectError(InvalidObjectIDError):
  204. "Invalid remote object ID used"
  205. class InvalidConnectionIDError(InvalidObjectIDError):
  206. "Invalid connection ID used"
  207. class MissingObjectIDError(RemoteInvocationError):
  208. "Missing object ID"
  209. # Raised by generate_remote_error (which means it is indirectly raised in
  210. # AzureusConnection.invoke_remote_method).
  211. #
  212. # Higher-level connections (like AzureusObjectConnection) may raise subclasses
  213. # of this error, if they are able to give a more precise error can be
  214. # determined.
  215. class RemoteMethodError(RemoteError):
  216. "Error thrown by remote method"
  217. class RemoteInternalError(RemoteError):
  218. "Internal error occurred during remote method invocation"
  219. class AzureusResponseXMLError(DopalError):
  220. "Error while parsing XML returned by Azureus"
  221. #---- core module ----#
  222. #---- types module ----#
  223. class ConversionError(DopalError): # Base class.
  224. "Error converting value (Azureus <--> Python)"
  225. class WrapError(ConversionError):
  226. "Error converting value to remote method argument"
  227. class UnwrapError(ConversionError):
  228. "Error converting remote method result to Python value"
  229. class InvalidWrapTypeError(WrapError, TypeError):
  230. '''
  231. Invalid wrap type given.
  232. This error is raised when a value is passed which cannot be converted into
  233. something that can be represented in Azureus. This either means that the
  234. value doesn't meet the criteria as something which can be represented, or
  235. the value doesn't fit the type that it is being wrapped as (e.g. a
  236. non-integer string as a integer).
  237. @see: L{wrap_value<dopal.aztypes.wrap_value>}
  238. @see: L{remote_method_call_to_xml<dopal.core.remote_method_call_to_xml>}
  239. '''
  240. class InvalidUnwrapTypeError(UnwrapError, TypeError):
  241. "Invalid unwrap type given."
  242. class InconsistentWrapTypeError(WrapError, TypeError):
  243. "Object has wrap type different to requested type"
  244. #---- types module ----#
  245. #---- types (AzMethod) module ----#
  246. class AzMethodError(DopalError): # Base class.
  247. "Error selecting matching AzMethod"
  248. def __init__(self, obj, *args, **kwargs):
  249. kwargs['obj'] = obj
  250. self.method_name = obj
  251. DopalError.__init__(self, *args, **kwargs)
  252. class IncorrectArgumentCountError(AzMethodError, TypeError):
  253. "Wrong number of arguments given for AzMethod"
  254. def __init__(self, obj, given_arg_count, required_arg_count):
  255. TypeError.__init__(self)
  256. # self.required_arg_count is a list
  257. # required_count is used for the 'text' variable
  258. self.given_arg_count = given_arg_count
  259. if isinstance(required_arg_count, (int, long)):
  260. self.required_arg_count = [required_arg_count]
  261. required_count = required_arg_count
  262. elif len(required_arg_count) == 1:
  263. self.required_arg_count = required_arg_count
  264. required_count = required_arg_count[0]
  265. else:
  266. self.required_arg_count = required_arg_count
  267. required_count = required_arg_count
  268. required_count = list(required_count)
  269. required_count.sort()
  270. text = "wrong number of arguments given (wanted %(required_count)s, given %(given_arg_count)s)" % locals()
  271. AzMethodError.__init__(self, obj, text=text)
  272. class ArgumentWrapError(AzMethodError):
  273. "Error wrapping argument for AzMethod"
  274. def __init__(self, arg_index, value, arg_type, error):
  275. text = "error converting arg %(arg_index)s to %(arg_type)s" % locals()
  276. AzMethodError.__init__(self, obj=value, error=error, text=text)
  277. self.arg_index = arg_index
  278. self.arg_type = arg_type
  279. class NoSuchAzMethodError(AzMethodError, AttributeError):
  280. "No method of that name available"
  281. def __init__(self, *args, **kwargs):
  282. AttributeError.__init__(self)
  283. AzMethodError.__init__(self, *args, **kwargs)
  284. class MethodArgumentWrapError(AzMethodError):
  285. "Error wrapping argument for multiple AzMethods"
  286. def __init__(self, name, invocation_errors):
  287. AzMethodError.__init__(self, name)
  288. self.invocation_errors = invocation_errors
  289. def __str__(self):
  290. text = "Error wrapping arguments:"
  291. error_data = [(str(method_data), str(error.__class__.__name__), str(error)) for (method_data, error) in self.invocation_errors]
  292. error_data.sort()
  293. for method_data, err_class, error in error_data:
  294. text += "\n %(method_data)s - %(err_class)s: %(error)s" % locals()
  295. return text
  296. #---- types (AzMethod) module ----#
  297. #---- objects module ----#
  298. class ConnectionlessObjectError(DopalError):
  299. "Object has no remote connection"
  300. class NonRefreshableObjectError(DopalError): # Base class
  301. "Object cannot be refreshed - refresh not implemented"
  302. class NonRefreshableConnectionlessObjectError(NonRefreshableObjectError, ConnectionlessObjectError):
  303. "Object cannot be refreshed - no connection attached"
  304. def __init__(self, *args, **kwargs):
  305. NonRefreshableObjectError.__init__(self)
  306. ConnectionlessObjectError.__init__(self, *args, **kwargs)
  307. #_superclass = super(NonRefreshableConnectionlessObjectError, self)
  308. #_superclass.__init__(*args, **kwargs)
  309. class NonRefreshableObjectTypeError(NonRefreshableObjectError):
  310. "Object cannot be refreshed - not implemented for this type"
  311. class NonRefreshableIncompleteObjectError(NonRefreshableObjectError):
  312. "Object cannot be refreshed - insufficient information on object"
  313. class StaleObjectReferenceError(NonRefreshableObjectError):
  314. "Object used belongs to old connection, which doesn't have persistency enabled"
  315. class MissingRemoteAttributeError(DopalError, AttributeError):
  316. "Object does not have remote attribute available"
  317. #---- objects module ----#
  318. #---- convert module ----#
  319. class InvalidRemoteClassTypeError(DopalError, TypeError):
  320. "Invalid remote class type given"
  321. # Base exception class - used when something cannot be converted.
  322. class StructureConversionError(ConversionError): # Base class.
  323. "Error converting response structure"
  324. # Base class for flow control exceptions.
  325. class ConversionControl(StructureConversionError): # Base class.
  326. "Base class for structured conversion control"
  327. # Use this class if you want to skip converting the object which
  328. # is being handled.
  329. class SkipConversion(ConversionControl):
  330. "Structured conversion of object skipped"
  331. # Use this class if you want to stop converting the object which
  332. # is being handled (essentially signalling a somewhat "fatal" error).
  333. class AbortConversion(ConversionControl):
  334. "Structured conversion of object aborted"
  335. # Use this class if you want to signal that you need more information
  336. # before you can proceed with the conversion - either that you need
  337. # the items lower down to be converted first, or you need the items
  338. # higher up converted first.
  339. class DelayConversion(ConversionControl):
  340. "Structured conversion of object delayed"
  341. # Use this class if you want to halt conversion completely - this is a more
  342. # severe form of AbortConversion, where it won't be passed to
  343. # Converter.handle_errors.
  344. class HaltConversion(ConversionControl):
  345. "Structured conversion of object halted"
  346. class DopalDeprecationWarning(DeprecationWarning, DopalError):
  347. pass
  348. # PendingDeprecationWarning class doesn't exist in Python 2.2.
  349. try:
  350. class DopalPendingDeprecationWarning(DopalDeprecationWarning, PendingDeprecationWarning):
  351. pass
  352. except NameError:
  353. class DopalPendingDeprecationWarning(DopalDeprecationWarning):
  354. pass
  355. class NoDefaultScriptConnectionError(DopalError):
  356. pass
  357. class ScriptFunctionError(DopalError):
  358. "Error occurred inside script function."