signal.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. """Helper functions for working with signals"""
  2. import logging
  3. from twisted.internet.defer import maybeDeferred, DeferredList, Deferred
  4. from twisted.python.failure import Failure
  5. from pydispatch.dispatcher import Any, Anonymous, liveReceivers, \
  6. getAllReceivers, disconnect
  7. from pydispatch.robustapply import robustApply
  8. from scrapy.utils.log import failure_to_exc_info
  9. logger = logging.getLogger(__name__)
  10. class _IgnoredException(Exception):
  11. pass
  12. def send_catch_log(signal=Any, sender=Anonymous, *arguments, **named):
  13. """Like pydispatcher.robust.sendRobust but it also logs errors and returns
  14. Failures instead of exceptions.
  15. """
  16. dont_log = named.pop('dont_log', _IgnoredException)
  17. spider = named.get('spider', None)
  18. responses = []
  19. for receiver in liveReceivers(getAllReceivers(sender, signal)):
  20. try:
  21. response = robustApply(receiver, signal=signal, sender=sender,
  22. *arguments, **named)
  23. if isinstance(response, Deferred):
  24. logger.error("Cannot return deferreds from signal handler: %(receiver)s",
  25. {'receiver': receiver}, extra={'spider': spider})
  26. except dont_log:
  27. result = Failure()
  28. except Exception:
  29. result = Failure()
  30. logger.error("Error caught on signal handler: %(receiver)s",
  31. {'receiver': receiver},
  32. exc_info=True, extra={'spider': spider})
  33. else:
  34. result = response
  35. responses.append((receiver, result))
  36. return responses
  37. def send_catch_log_deferred(signal=Any, sender=Anonymous, *arguments, **named):
  38. """Like send_catch_log but supports returning deferreds on signal handlers.
  39. Returns a deferred that gets fired once all signal handlers deferreds were
  40. fired.
  41. """
  42. def logerror(failure, recv):
  43. if dont_log is None or not isinstance(failure.value, dont_log):
  44. logger.error("Error caught on signal handler: %(receiver)s",
  45. {'receiver': recv},
  46. exc_info=failure_to_exc_info(failure),
  47. extra={'spider': spider})
  48. return failure
  49. dont_log = named.pop('dont_log', None)
  50. spider = named.get('spider', None)
  51. dfds = []
  52. for receiver in liveReceivers(getAllReceivers(sender, signal)):
  53. d = maybeDeferred(robustApply, receiver, signal=signal, sender=sender,
  54. *arguments, **named)
  55. d.addErrback(logerror, receiver)
  56. d.addBoth(lambda result: (receiver, result))
  57. dfds.append(d)
  58. d = DeferredList(dfds)
  59. d.addCallback(lambda out: [x[1] for x in out])
  60. return d
  61. def disconnect_all(signal=Any, sender=Any):
  62. """Disconnect all signal handlers. Useful for cleaning up after running
  63. tests
  64. """
  65. for receiver in liveReceivers(getAllReceivers(sender, signal)):
  66. disconnect(receiver, signal=signal, sender=sender)