You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

793 rivejä
26 KiB

  1. @cython.no_gc_clear
  2. cdef class UVProcess(UVHandle):
  3. """Abstract class; wrapper over uv_process_t handle."""
  4. def __cinit__(self):
  5. self.uv_opt_env = NULL
  6. self.uv_opt_args = NULL
  7. self._returncode = None
  8. self._pid = None
  9. self._fds_to_close = list()
  10. self._preexec_fn = None
  11. self._restore_signals = True
  12. self.context = Context_CopyCurrent()
  13. cdef _close_process_handle(self):
  14. # XXX: This is a workaround for a libuv bug:
  15. # - https://github.com/libuv/libuv/issues/1933
  16. # - https://github.com/libuv/libuv/pull/551
  17. if self._handle is NULL:
  18. return
  19. self._handle.data = NULL
  20. uv.uv_close(self._handle, __uv_close_process_handle_cb)
  21. self._handle = NULL # close callback will free() the memory
  22. cdef _init(self, Loop loop, list args, dict env,
  23. cwd, start_new_session,
  24. _stdin, _stdout, _stderr, # std* can be defined as macros in C
  25. pass_fds, debug_flags, preexec_fn, restore_signals):
  26. global __forking
  27. global __forking_loop
  28. global __forkHandler
  29. cdef int err
  30. self._start_init(loop)
  31. self._handle = <uv.uv_handle_t*>PyMem_RawMalloc(
  32. sizeof(uv.uv_process_t))
  33. if self._handle is NULL:
  34. self._abort_init()
  35. raise MemoryError()
  36. # Too early to call _finish_init, but still a lot of work to do.
  37. # Let's set handle.data to NULL, so in case something goes wrong,
  38. # callbacks have a chance to avoid casting *something* into UVHandle.
  39. self._handle.data = NULL
  40. force_fork = False
  41. if system.PLATFORM_IS_APPLE and not (
  42. preexec_fn is None
  43. and not pass_fds
  44. ):
  45. # see _execute_child() in CPython/subprocess.py
  46. force_fork = True
  47. try:
  48. self._init_options(args, env, cwd, start_new_session,
  49. _stdin, _stdout, _stderr, force_fork)
  50. restore_inheritable = set()
  51. if pass_fds:
  52. for fd in pass_fds:
  53. if not os_get_inheritable(fd):
  54. restore_inheritable.add(fd)
  55. os_set_inheritable(fd, True)
  56. except Exception:
  57. self._abort_init()
  58. raise
  59. if __forking or loop.active_process_handler is not None:
  60. # Our pthread_atfork handlers won't work correctly when
  61. # another loop is forking in another thread (even though
  62. # GIL should help us to avoid that.)
  63. self._abort_init()
  64. raise RuntimeError(
  65. 'Racing with another loop to spawn a process.')
  66. self._errpipe_read, self._errpipe_write = os_pipe()
  67. fds_to_close = self._fds_to_close
  68. self._fds_to_close = None
  69. fds_to_close.append(self._errpipe_read)
  70. # add the write pipe last so we can close it early
  71. fds_to_close.append(self._errpipe_write)
  72. try:
  73. os_set_inheritable(self._errpipe_write, True)
  74. self._preexec_fn = preexec_fn
  75. self._restore_signals = restore_signals
  76. loop.active_process_handler = self
  77. __forking = 1
  78. __forking_loop = loop
  79. system.setForkHandler(<system.OnForkHandler>&__get_fork_handler)
  80. PyOS_BeforeFork()
  81. err = uv.uv_spawn(loop.uvloop,
  82. <uv.uv_process_t*>self._handle,
  83. &self.options)
  84. __forking = 0
  85. __forking_loop = None
  86. system.resetForkHandler()
  87. loop.active_process_handler = None
  88. PyOS_AfterFork_Parent()
  89. if err < 0:
  90. self._close_process_handle()
  91. self._abort_init()
  92. raise convert_error(err)
  93. self._finish_init()
  94. # close the write pipe early
  95. os_close(fds_to_close.pop())
  96. if preexec_fn is not None:
  97. errpipe_data = bytearray()
  98. while True:
  99. # XXX: This is a blocking code that has to be
  100. # rewritten (using loop.connect_read_pipe() or
  101. # otherwise.)
  102. part = os_read(self._errpipe_read, 50000)
  103. errpipe_data += part
  104. if not part or len(errpipe_data) > 50000:
  105. break
  106. finally:
  107. while fds_to_close:
  108. os_close(fds_to_close.pop())
  109. for fd in restore_inheritable:
  110. os_set_inheritable(fd, False)
  111. # asyncio caches the PID in BaseSubprocessTransport,
  112. # so that the transport knows what the PID was even
  113. # after the process is finished.
  114. self._pid = (<uv.uv_process_t*>self._handle).pid
  115. # Track the process handle (create a strong ref to it)
  116. # to guarantee that __dealloc__ doesn't happen in an
  117. # uncontrolled fashion. We want to wait until the process
  118. # exits and libuv calls __uvprocess_on_exit_callback,
  119. # which will call `UVProcess._close()`, which will, in turn,
  120. # untrack this handle.
  121. self._loop._track_process(self)
  122. if debug_flags & __PROCESS_DEBUG_SLEEP_AFTER_FORK:
  123. time_sleep(1)
  124. if preexec_fn is not None and errpipe_data:
  125. # preexec_fn has raised an exception. The child
  126. # process must be dead now.
  127. try:
  128. exc_name, exc_msg = errpipe_data.split(b':', 1)
  129. exc_name = exc_name.decode()
  130. exc_msg = exc_msg.decode()
  131. except Exception:
  132. self._close()
  133. raise subprocess_SubprocessError(
  134. 'Bad exception data from child: {!r}'.format(
  135. errpipe_data))
  136. exc_cls = getattr(__builtins__, exc_name,
  137. subprocess_SubprocessError)
  138. exc = subprocess_SubprocessError(
  139. 'Exception occurred in preexec_fn.')
  140. exc.__cause__ = exc_cls(exc_msg)
  141. self._close()
  142. raise exc
  143. cdef _after_fork(self):
  144. # See CPython/_posixsubprocess.c for details
  145. cdef int err
  146. if self._restore_signals:
  147. _Py_RestoreSignals()
  148. PyOS_AfterFork_Child()
  149. err = uv.uv_loop_fork(self._loop.uvloop)
  150. if err < 0:
  151. raise convert_error(err)
  152. if self._preexec_fn is not None:
  153. try:
  154. gc_disable()
  155. self._preexec_fn()
  156. except BaseException as ex:
  157. try:
  158. with open(self._errpipe_write, 'wb') as f:
  159. f.write(str(ex.__class__.__name__).encode())
  160. f.write(b':')
  161. f.write(str(ex.args[0]).encode())
  162. finally:
  163. system._exit(255)
  164. return
  165. else:
  166. os_close(self._errpipe_write)
  167. else:
  168. os_close(self._errpipe_write)
  169. cdef _close_after_spawn(self, int fd):
  170. if self._fds_to_close is None:
  171. raise RuntimeError(
  172. 'UVProcess._close_after_spawn called after uv_spawn')
  173. self._fds_to_close.append(fd)
  174. def __dealloc__(self):
  175. if self.uv_opt_env is not NULL:
  176. PyMem_RawFree(self.uv_opt_env)
  177. self.uv_opt_env = NULL
  178. if self.uv_opt_args is not NULL:
  179. PyMem_RawFree(self.uv_opt_args)
  180. self.uv_opt_args = NULL
  181. cdef char** __to_cstring_array(self, list arr):
  182. cdef:
  183. int i
  184. ssize_t arr_len = len(arr)
  185. bytes el
  186. char **ret
  187. ret = <char **>PyMem_RawMalloc((arr_len + 1) * sizeof(char *))
  188. if ret is NULL:
  189. raise MemoryError()
  190. for i in range(arr_len):
  191. el = arr[i]
  192. # NB: PyBytes_AsString doesn't copy the data;
  193. # we have to be careful when the "arr" is GCed,
  194. # and it shouldn't be ever mutated.
  195. ret[i] = PyBytes_AsString(el)
  196. ret[arr_len] = NULL
  197. return ret
  198. cdef _init_options(self, list args, dict env, cwd, start_new_session,
  199. _stdin, _stdout, _stderr, bint force_fork):
  200. memset(&self.options, 0, sizeof(uv.uv_process_options_t))
  201. self._init_env(env)
  202. self.options.env = self.uv_opt_env
  203. self._init_args(args)
  204. self.options.file = self.uv_opt_file
  205. self.options.args = self.uv_opt_args
  206. if start_new_session:
  207. self.options.flags |= uv.UV_PROCESS_DETACHED
  208. if force_fork:
  209. # This is a hack to work around the change in libuv 1.44:
  210. # > macos: use posix_spawn instead of fork
  211. # where Python subprocess options like preexec_fn are
  212. # crippled. CPython only uses posix_spawn under a pretty
  213. # strict list of conditions (see subprocess.py), and falls
  214. # back to using fork() otherwise. We'd like to simulate such
  215. # behavior with libuv, but unfortunately libuv doesn't
  216. # provide explicit API to choose such implementation detail.
  217. # Based on current (libuv 1.46) behavior, setting
  218. # UV_PROCESS_SETUID or UV_PROCESS_SETGID would reliably make
  219. # libuv fallback to use fork, so let's just use it for now.
  220. self.options.flags |= uv.UV_PROCESS_SETUID
  221. self.options.uid = uv.getuid()
  222. if cwd is not None:
  223. cwd = os_fspath(cwd)
  224. if isinstance(cwd, str):
  225. cwd = PyUnicode_EncodeFSDefault(cwd)
  226. if not isinstance(cwd, bytes):
  227. raise ValueError('cwd must be a str or bytes object')
  228. self.__cwd = cwd
  229. self.options.cwd = PyBytes_AsString(self.__cwd)
  230. self.options.exit_cb = &__uvprocess_on_exit_callback
  231. self._init_files(_stdin, _stdout, _stderr)
  232. cdef _init_args(self, list args):
  233. cdef:
  234. bytes path
  235. int an = len(args)
  236. if an < 1:
  237. raise ValueError('cannot spawn a process: args are empty')
  238. self.__args = args.copy()
  239. for i in range(an):
  240. arg = os_fspath(args[i])
  241. if isinstance(arg, str):
  242. self.__args[i] = PyUnicode_EncodeFSDefault(arg)
  243. elif not isinstance(arg, bytes):
  244. raise TypeError('all args must be str or bytes')
  245. path = self.__args[0]
  246. self.uv_opt_file = PyBytes_AsString(path)
  247. self.uv_opt_args = self.__to_cstring_array(self.__args)
  248. cdef _init_env(self, dict env):
  249. if env is not None:
  250. self.__env = list()
  251. for key in env:
  252. val = env[key]
  253. if isinstance(key, str):
  254. key = PyUnicode_EncodeFSDefault(key)
  255. elif not isinstance(key, bytes):
  256. raise TypeError(
  257. 'all environment vars must be bytes or str')
  258. if isinstance(val, str):
  259. val = PyUnicode_EncodeFSDefault(val)
  260. elif not isinstance(val, bytes):
  261. raise TypeError(
  262. 'all environment values must be bytes or str')
  263. self.__env.append(key + b'=' + val)
  264. self.uv_opt_env = self.__to_cstring_array(self.__env)
  265. else:
  266. self.__env = None
  267. cdef _init_files(self, _stdin, _stdout, _stderr):
  268. self.options.stdio_count = 0
  269. cdef _kill(self, int signum):
  270. cdef int err
  271. self._ensure_alive()
  272. err = uv.uv_process_kill(<uv.uv_process_t*>self._handle, signum)
  273. if err < 0:
  274. raise convert_error(err)
  275. cdef _on_exit(self, int64_t exit_status, int term_signal):
  276. if term_signal:
  277. # From Python docs:
  278. # A negative value -N indicates that the child was
  279. # terminated by signal N (POSIX only).
  280. self._returncode = -term_signal
  281. else:
  282. self._returncode = exit_status
  283. self._close()
  284. cdef _close(self):
  285. try:
  286. if self._loop is not None:
  287. self._loop._untrack_process(self)
  288. finally:
  289. UVHandle._close(self)
  290. DEF _CALL_PIPE_DATA_RECEIVED = 0
  291. DEF _CALL_PIPE_CONNECTION_LOST = 1
  292. DEF _CALL_PROCESS_EXITED = 2
  293. DEF _CALL_CONNECTION_LOST = 3
  294. @cython.no_gc_clear
  295. cdef class UVProcessTransport(UVProcess):
  296. def __cinit__(self):
  297. self._exit_waiters = []
  298. self._protocol = None
  299. self._init_futs = []
  300. self._pending_calls = []
  301. self._stdio_ready = 0
  302. self._stdin = self._stdout = self._stderr = None
  303. self.stdin_proto = self.stdout_proto = self.stderr_proto = None
  304. self._finished = 0
  305. cdef _on_exit(self, int64_t exit_status, int term_signal):
  306. UVProcess._on_exit(self, exit_status, term_signal)
  307. if self._stdio_ready:
  308. self._loop.call_soon(self._protocol.process_exited,
  309. context=self.context)
  310. else:
  311. self._pending_calls.append((_CALL_PROCESS_EXITED, None, None))
  312. self._try_finish()
  313. for waiter in self._exit_waiters:
  314. if not waiter.cancelled():
  315. waiter.set_result(self._returncode)
  316. self._exit_waiters.clear()
  317. self._close()
  318. cdef _check_proc(self):
  319. if not self._is_alive() or self._returncode is not None:
  320. raise ProcessLookupError()
  321. cdef _pipe_connection_lost(self, int fd, exc):
  322. if self._stdio_ready:
  323. self._loop.call_soon(self._protocol.pipe_connection_lost, fd, exc,
  324. context=self.context)
  325. self._try_finish()
  326. else:
  327. self._pending_calls.append((_CALL_PIPE_CONNECTION_LOST, fd, exc))
  328. cdef _pipe_data_received(self, int fd, data):
  329. if self._stdio_ready:
  330. self._loop.call_soon(self._protocol.pipe_data_received, fd, data,
  331. context=self.context)
  332. else:
  333. self._pending_calls.append((_CALL_PIPE_DATA_RECEIVED, fd, data))
  334. cdef _file_redirect_stdio(self, int fd):
  335. fd = os_dup(fd)
  336. os_set_inheritable(fd, True)
  337. self._close_after_spawn(fd)
  338. return fd
  339. cdef _file_devnull(self):
  340. dn = os_open(os_devnull, os_O_RDWR)
  341. os_set_inheritable(dn, True)
  342. self._close_after_spawn(dn)
  343. return dn
  344. cdef _file_outpipe(self):
  345. r, w = __socketpair()
  346. os_set_inheritable(w, True)
  347. self._close_after_spawn(w)
  348. return r, w
  349. cdef _file_inpipe(self):
  350. r, w = __socketpair()
  351. os_set_inheritable(r, True)
  352. self._close_after_spawn(r)
  353. return r, w
  354. cdef _init_files(self, _stdin, _stdout, _stderr):
  355. cdef uv.uv_stdio_container_t *iocnt
  356. UVProcess._init_files(self, _stdin, _stdout, _stderr)
  357. io = [None, None, None]
  358. self.options.stdio_count = 3
  359. self.options.stdio = self.iocnt
  360. if _stdin is not None:
  361. if _stdin == subprocess_PIPE:
  362. r, w = self._file_inpipe()
  363. io[0] = r
  364. self.stdin_proto = WriteSubprocessPipeProto(self, 0)
  365. waiter = self._loop._new_future()
  366. self._stdin = WriteUnixTransport.new(
  367. self._loop, self.stdin_proto, None, waiter)
  368. self._init_futs.append(waiter)
  369. self._stdin._open(w)
  370. self._stdin._init_protocol()
  371. elif _stdin == subprocess_DEVNULL:
  372. io[0] = self._file_devnull()
  373. elif _stdout == subprocess_STDOUT:
  374. raise ValueError(
  375. 'subprocess.STDOUT is supported only by stderr parameter')
  376. else:
  377. io[0] = self._file_redirect_stdio(_stdin)
  378. else:
  379. io[0] = self._file_redirect_stdio(0)
  380. if _stdout is not None:
  381. if _stdout == subprocess_PIPE:
  382. # We can't use UV_CREATE_PIPE here, since 'stderr' might be
  383. # set to 'subprocess.STDOUT', and there is no way to
  384. # emulate that functionality with libuv high-level
  385. # streams API. Therefore, we create pipes for stdout and
  386. # stderr manually.
  387. r, w = self._file_outpipe()
  388. io[1] = w
  389. self.stdout_proto = ReadSubprocessPipeProto(self, 1)
  390. waiter = self._loop._new_future()
  391. self._stdout = ReadUnixTransport.new(
  392. self._loop, self.stdout_proto, None, waiter)
  393. self._init_futs.append(waiter)
  394. self._stdout._open(r)
  395. self._stdout._init_protocol()
  396. elif _stdout == subprocess_DEVNULL:
  397. io[1] = self._file_devnull()
  398. elif _stdout == subprocess_STDOUT:
  399. raise ValueError(
  400. 'subprocess.STDOUT is supported only by stderr parameter')
  401. else:
  402. io[1] = self._file_redirect_stdio(_stdout)
  403. else:
  404. io[1] = self._file_redirect_stdio(1)
  405. if _stderr is not None:
  406. if _stderr == subprocess_PIPE:
  407. r, w = self._file_outpipe()
  408. io[2] = w
  409. self.stderr_proto = ReadSubprocessPipeProto(self, 2)
  410. waiter = self._loop._new_future()
  411. self._stderr = ReadUnixTransport.new(
  412. self._loop, self.stderr_proto, None, waiter)
  413. self._init_futs.append(waiter)
  414. self._stderr._open(r)
  415. self._stderr._init_protocol()
  416. elif _stderr == subprocess_STDOUT:
  417. if io[1] is None:
  418. # shouldn't ever happen
  419. raise RuntimeError('cannot apply subprocess.STDOUT')
  420. io[2] = self._file_redirect_stdio(io[1])
  421. elif _stderr == subprocess_DEVNULL:
  422. io[2] = self._file_devnull()
  423. else:
  424. io[2] = self._file_redirect_stdio(_stderr)
  425. else:
  426. io[2] = self._file_redirect_stdio(2)
  427. assert len(io) == 3
  428. for idx in range(3):
  429. iocnt = &self.iocnt[idx]
  430. if io[idx] is not None:
  431. iocnt.flags = uv.UV_INHERIT_FD
  432. iocnt.data.fd = io[idx]
  433. else:
  434. iocnt.flags = uv.UV_IGNORE
  435. cdef _call_connection_made(self, waiter):
  436. try:
  437. # we're always called in the right context, so just call the user's
  438. self._protocol.connection_made(self)
  439. except (KeyboardInterrupt, SystemExit):
  440. raise
  441. except BaseException as ex:
  442. if waiter is not None and not waiter.cancelled():
  443. waiter.set_exception(ex)
  444. else:
  445. raise
  446. else:
  447. if waiter is not None and not waiter.cancelled():
  448. waiter.set_result(True)
  449. self._stdio_ready = 1
  450. if self._pending_calls:
  451. pending_calls = self._pending_calls.copy()
  452. self._pending_calls.clear()
  453. for (type, fd, arg) in pending_calls:
  454. if type == _CALL_PIPE_CONNECTION_LOST:
  455. self._pipe_connection_lost(fd, arg)
  456. elif type == _CALL_PIPE_DATA_RECEIVED:
  457. self._pipe_data_received(fd, arg)
  458. elif type == _CALL_PROCESS_EXITED:
  459. self._loop.call_soon(self._protocol.process_exited)
  460. elif type == _CALL_CONNECTION_LOST:
  461. self._loop.call_soon(self._protocol.connection_lost, None)
  462. cdef _try_finish(self):
  463. if self._returncode is None or self._finished:
  464. return
  465. if ((self.stdin_proto is None or self.stdin_proto.disconnected) and
  466. (self.stdout_proto is None or
  467. self.stdout_proto.disconnected) and
  468. (self.stderr_proto is None or
  469. self.stderr_proto.disconnected)):
  470. self._finished = 1
  471. if self._stdio_ready:
  472. # copy self.context for simplicity
  473. self._loop.call_soon(self._protocol.connection_lost, None,
  474. context=self.context)
  475. else:
  476. self._pending_calls.append((_CALL_CONNECTION_LOST, None, None))
  477. def __stdio_inited(self, waiter, stdio_fut):
  478. exc = stdio_fut.exception()
  479. if exc is not None:
  480. if waiter is None:
  481. raise exc
  482. else:
  483. waiter.set_exception(exc)
  484. else:
  485. self._loop._call_soon_handle(
  486. new_MethodHandle1(self._loop,
  487. "UVProcessTransport._call_connection_made",
  488. <method1_t>self._call_connection_made,
  489. None, # means to copy the current context
  490. self, waiter))
  491. @staticmethod
  492. cdef UVProcessTransport new(Loop loop, protocol, args, env,
  493. cwd, start_new_session,
  494. _stdin, _stdout, _stderr, pass_fds,
  495. waiter,
  496. debug_flags,
  497. preexec_fn,
  498. restore_signals):
  499. cdef UVProcessTransport handle
  500. handle = UVProcessTransport.__new__(UVProcessTransport)
  501. handle._protocol = protocol
  502. handle._init(loop, args, env, cwd, start_new_session,
  503. __process_convert_fileno(_stdin),
  504. __process_convert_fileno(_stdout),
  505. __process_convert_fileno(_stderr),
  506. pass_fds,
  507. debug_flags,
  508. preexec_fn,
  509. restore_signals)
  510. if handle._init_futs:
  511. handle._stdio_ready = 0
  512. init_fut = aio_gather(*handle._init_futs)
  513. # add_done_callback will copy the current context and run the
  514. # callback within the context
  515. init_fut.add_done_callback(
  516. ft_partial(handle.__stdio_inited, waiter))
  517. else:
  518. handle._stdio_ready = 1
  519. loop._call_soon_handle(
  520. new_MethodHandle1(loop,
  521. "UVProcessTransport._call_connection_made",
  522. <method1_t>handle._call_connection_made,
  523. None, # means to copy the current context
  524. handle, waiter))
  525. return handle
  526. def get_protocol(self):
  527. return self._protocol
  528. def set_protocol(self, protocol):
  529. self._protocol = protocol
  530. def get_pid(self):
  531. return self._pid
  532. def get_returncode(self):
  533. return self._returncode
  534. def get_pipe_transport(self, fd):
  535. if fd == 0:
  536. return self._stdin
  537. elif fd == 1:
  538. return self._stdout
  539. elif fd == 2:
  540. return self._stderr
  541. def terminate(self):
  542. self._check_proc()
  543. self._kill(uv.SIGTERM)
  544. def kill(self):
  545. self._check_proc()
  546. self._kill(uv.SIGKILL)
  547. def send_signal(self, int signal):
  548. self._check_proc()
  549. self._kill(signal)
  550. def is_closing(self):
  551. return self._closed
  552. def close(self):
  553. if self._returncode is None:
  554. self._kill(uv.SIGKILL)
  555. if self._stdin is not None:
  556. self._stdin.close()
  557. if self._stdout is not None:
  558. self._stdout.close()
  559. if self._stderr is not None:
  560. self._stderr.close()
  561. if self._returncode is not None:
  562. # The process is dead, just close the UV handle.
  563. #
  564. # (If "self._returncode is None", the process should have been
  565. # killed already and we're just waiting for a SIGCHLD; after
  566. # which the transport will be GC'ed and the uvhandle will be
  567. # closed in UVHandle.__dealloc__.)
  568. self._close()
  569. def get_extra_info(self, name, default=None):
  570. return default
  571. def _wait(self):
  572. fut = self._loop._new_future()
  573. if self._returncode is not None:
  574. fut.set_result(self._returncode)
  575. return fut
  576. self._exit_waiters.append(fut)
  577. return fut
  578. class WriteSubprocessPipeProto(aio_BaseProtocol):
  579. def __init__(self, proc, fd):
  580. if UVLOOP_DEBUG:
  581. if type(proc) is not UVProcessTransport:
  582. raise TypeError
  583. if not isinstance(fd, int):
  584. raise TypeError
  585. self.proc = proc
  586. self.fd = fd
  587. self.pipe = None
  588. self.disconnected = False
  589. def connection_made(self, transport):
  590. self.pipe = transport
  591. def __repr__(self):
  592. return ('<%s fd=%s pipe=%r>'
  593. % (self.__class__.__name__, self.fd, self.pipe))
  594. def connection_lost(self, exc):
  595. self.disconnected = True
  596. (<UVProcessTransport>self.proc)._pipe_connection_lost(self.fd, exc)
  597. self.proc = None
  598. def pause_writing(self):
  599. (<UVProcessTransport>self.proc)._protocol.pause_writing()
  600. def resume_writing(self):
  601. (<UVProcessTransport>self.proc)._protocol.resume_writing()
  602. class ReadSubprocessPipeProto(WriteSubprocessPipeProto,
  603. aio_Protocol):
  604. def data_received(self, data):
  605. (<UVProcessTransport>self.proc)._pipe_data_received(self.fd, data)
  606. cdef __process_convert_fileno(object obj):
  607. if obj is None or isinstance(obj, int):
  608. return obj
  609. fileno = obj.fileno()
  610. if not isinstance(fileno, int):
  611. raise TypeError(
  612. '{!r}.fileno() returned non-integer'.format(obj))
  613. return fileno
  614. cdef void __uvprocess_on_exit_callback(
  615. uv.uv_process_t *handle,
  616. int64_t exit_status,
  617. int term_signal,
  618. ) noexcept with gil:
  619. if __ensure_handle_data(<uv.uv_handle_t*>handle,
  620. "UVProcess exit callback") == 0:
  621. return
  622. cdef UVProcess proc = <UVProcess> handle.data
  623. try:
  624. proc._on_exit(exit_status, term_signal)
  625. except BaseException as ex:
  626. proc._error(ex, False)
  627. cdef __socketpair():
  628. cdef:
  629. int fds[2]
  630. int err
  631. err = system.socketpair(uv.AF_UNIX, uv.SOCK_STREAM, 0, fds)
  632. if err:
  633. exc = convert_error(-err)
  634. raise exc
  635. os_set_inheritable(fds[0], False)
  636. os_set_inheritable(fds[1], False)
  637. return fds[0], fds[1]
  638. cdef void __uv_close_process_handle_cb(
  639. uv.uv_handle_t* handle
  640. ) noexcept with gil:
  641. PyMem_RawFree(handle)