PageRenderTime 41ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/eventlet/green/subprocess.py

https://bitbucket.org/eventlet/eventlet
Python | 105 lines | 69 code | 10 blank | 26 comment | 21 complexity | 685a5d192d4bdef81ea04c52d013eeda MD5 | raw file
Possible License(s): MIT
  1. import errno
  2. import new
  3. import time
  4. import eventlet
  5. from eventlet import greenio
  6. from eventlet import patcher
  7. from eventlet.green import os
  8. from eventlet.green import select
  9. patcher.inject('subprocess', globals(), ('select', select))
  10. subprocess_orig = __import__("subprocess")
  11. if getattr(subprocess_orig, 'TimeoutExpired', None) is None:
  12. # Backported from Python 3.3.
  13. # https://bitbucket.org/eventlet/eventlet/issue/89
  14. class TimeoutExpired(Exception):
  15. """This exception is raised when the timeout expires while waiting for
  16. a child process.
  17. """
  18. def __init__(self, cmd, output=None):
  19. self.cmd = cmd
  20. self.output = output
  21. def __str__(self):
  22. return ("Command '%s' timed out after %s seconds" %
  23. (self.cmd, self.timeout))
  24. # This is the meat of this module, the green version of Popen.
  25. class Popen(subprocess_orig.Popen):
  26. """eventlet-friendly version of subprocess.Popen"""
  27. # We do not believe that Windows pipes support non-blocking I/O. At least,
  28. # the Python file objects stored on our base-class object have no
  29. # setblocking() method, and the Python fcntl module doesn't exist on
  30. # Windows. (see eventlet.greenio.set_nonblocking()) As the sole purpose of
  31. # this __init__() override is to wrap the pipes for eventlet-friendly
  32. # non-blocking I/O, don't even bother overriding it on Windows.
  33. if not subprocess_orig.mswindows:
  34. def __init__(self, args, bufsize=0, *argss, **kwds):
  35. self.args = args
  36. # Forward the call to base-class constructor
  37. subprocess_orig.Popen.__init__(self, args, 0, *argss, **kwds)
  38. # Now wrap the pipes, if any. This logic is loosely borrowed from
  39. # eventlet.processes.Process.run() method.
  40. for attr in "stdin", "stdout", "stderr":
  41. pipe = getattr(self, attr)
  42. if pipe is not None and not type(pipe) == greenio.GreenPipe:
  43. wrapped_pipe = greenio.GreenPipe(pipe, pipe.mode, bufsize)
  44. setattr(self, attr, wrapped_pipe)
  45. __init__.__doc__ = subprocess_orig.Popen.__init__.__doc__
  46. def wait(self, timeout=None, check_interval=0.01):
  47. # Instead of a blocking OS call, this version of wait() uses logic
  48. # borrowed from the eventlet 0.2 processes.Process.wait() method.
  49. if timeout is not None:
  50. endtime = time.time() + timeout
  51. try:
  52. while True:
  53. status = self.poll()
  54. if status is not None:
  55. return status
  56. if timeout is not None and time.time() > endtime:
  57. raise TimeoutExpired(self.args)
  58. eventlet.sleep(check_interval)
  59. except OSError, e:
  60. if e.errno == errno.ECHILD:
  61. # no child process, this happens if the child process
  62. # already died and has been cleaned up
  63. return -1
  64. else:
  65. raise
  66. wait.__doc__ = subprocess_orig.Popen.wait.__doc__
  67. if not subprocess_orig.mswindows:
  68. # don't want to rewrite the original _communicate() method, we
  69. # just want a version that uses eventlet.green.select.select()
  70. # instead of select.select().
  71. try:
  72. _communicate = new.function(subprocess_orig.Popen._communicate.im_func.func_code,
  73. globals())
  74. try:
  75. _communicate_with_select = new.function(
  76. subprocess_orig.Popen._communicate_with_select.im_func.func_code,
  77. globals())
  78. _communicate_with_poll = new.function(
  79. subprocess_orig.Popen._communicate_with_poll.im_func.func_code,
  80. globals())
  81. except AttributeError:
  82. pass
  83. except AttributeError:
  84. # 2.4 only has communicate
  85. _communicate = new.function(subprocess_orig.Popen.communicate.im_func.func_code,
  86. globals())
  87. def communicate(self, input=None):
  88. return self._communicate(input)
  89. # Borrow subprocess.call() and check_call(), but patch them so they reference
  90. # OUR Popen class rather than subprocess.Popen.
  91. call = new.function(subprocess_orig.call.func_code, globals())
  92. try:
  93. check_call = new.function(subprocess_orig.check_call.func_code, globals())
  94. except AttributeError:
  95. pass # check_call added in 2.5