Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gevent
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
gevent
Commits
0fdf78c9
Commit
0fdf78c9
authored
Sep 16, 2015
by
Jason Madden
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Wrap os.forkpty for waitpid compatibility in the same way that os.fork is. Fixes #650.
parent
ddc826ac
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
137 additions
and
31 deletions
+137
-31
changelog.rst
changelog.rst
+4
-0
gevent/os.py
gevent/os.py
+89
-12
greentest/test__monkey.py
greentest/test__monkey.py
+7
-2
greentest/test__monkey_sigchld.py
greentest/test__monkey_sigchld.py
+37
-17
No files found.
changelog.rst
View file @
0fdf78c9
...
@@ -43,6 +43,10 @@
...
@@ -43,6 +43,10 @@
top-level of a module is typically not recommended, but this
top-level of a module is typically not recommended, but this
situation can arise when monkey-patching existing scripts. Reported
situation can arise when monkey-patching existing scripts. Reported
in :issue:`651` and :issue:`652` by Mike Kaplinskiy.
in :issue:`651` and :issue:`652` by Mike Kaplinskiy.
- ``SIGCHLD`` and ``waitpid`` now work for the pids returned by the
(monkey-patched) ``os.forkpty`` and ``pty.fork`` functions in the
same way they do for the ``os.fork`` function. Reported in
:issue:`650` by Erich Heine.
.. _WSGI specification: https://www.python.org/dev/peps/pep-3333/#the-start-response-callable
.. _WSGI specification: https://www.python.org/dev/peps/pep-3333/#the-start-response-callable
...
...
gevent/os.py
View file @
0fdf78c9
...
@@ -30,7 +30,7 @@ be done with the threadpool.
...
@@ -30,7 +30,7 @@ be done with the threadpool.
Child Processes
Child Processes
===============
===============
The functions :func:`fork` and (on POSIX) :func:`waitpid` can be used
The functions :func:`fork` and (on POSIX) :func:`
forkpty` and :func:`
waitpid` can be used
to manage child processes.
to manage child processes.
.. warning::
.. warning::
...
@@ -150,17 +150,18 @@ if hasattr(os, 'fork'):
...
@@ -150,17 +150,18 @@ if hasattr(os, 'fork'):
.. note::
.. note::
The PID returned by this function may not be waitable with
The PID returned by this function may not be waitable with
either :func:`os.waitpid` or :func:`waitpid` and it may
either the original :func:`os.waitpid` or this module's
not generate SIGCHLD signals if libev child watchers are
:func:`waitpid` and it may not generate SIGCHLD signals if
or ever have been in use. For example, the
libev child watchers are or ever have been in use. For
:mod:`gevent.subprocess` module uses libev child watchers
example, the :mod:`gevent.subprocess` module uses libev
(which parts of gevent use libev child watchers is subject
child watchers (which parts of gevent use libev child
to change at any time). Most applications should use
watchers is subject to change at any time). Most
:func:`fork_and_watch`, which is monkey-patched as the
applications should use :func:`fork_and_watch`, which is
default replacement for :func:`os.fork` and implements the
monkey-patched as the default replacement for
``fork`` function of this module by default, unless the
:func:`os.fork` and implements the ``fork`` function of
environment variable ``GEVENT_NOWAITPID`` is defined
this module by default, unless the environment variable
before this module is imported.
``GEVENT_NOWAITPID`` is defined before this module is
imported.
.. versionadded:: 1.1b2
.. versionadded:: 1.1b2
"""
"""
...
@@ -175,6 +176,33 @@ if hasattr(os, 'fork'):
...
@@ -175,6 +176,33 @@ if hasattr(os, 'fork'):
"""
"""
return
fork_gevent
()
return
fork_gevent
()
if
hasattr
(
os
,
'forkpty'
):
_raw_forkpty
=
os
.
forkpty
def
forkpty_gevent
():
"""
Forks the process using :func:`os.forkpty` and prepares the
child process to continue using gevent before returning.
Returns a tuple (pid, master_fd). The `master_fd` is *not* put into
non-blocking mode.
Availability: Some Unix systems.
.. seealso:: This function has the same limitations as :func:`fork_gevent`.
.. versionadded:: 1.1b5
"""
pid
,
master_fd
=
_raw_forkpty
()
if
not
pid
:
reinit
()
return
pid
,
master_fd
forkpty
=
forkpty_gevent
__implements__
.
append
(
'forkpty'
)
__extensions__
.
append
(
"forkpty_gevent"
)
if
hasattr
(
os
,
'WNOWAIT'
)
or
hasattr
(
os
,
'WNOHANG'
):
if
hasattr
(
os
,
'WNOWAIT'
)
or
hasattr
(
os
,
'WNOHANG'
):
# We can only do this on POSIX
# We can only do this on POSIX
import
time
import
time
...
@@ -317,8 +345,30 @@ if hasattr(os, 'fork'):
...
@@ -317,8 +345,30 @@ if hasattr(os, 'fork'):
__extensions__
.
append
(
'fork_and_watch'
)
__extensions__
.
append
(
'fork_and_watch'
)
__extensions__
.
append
(
'fork_gevent'
)
__extensions__
.
append
(
'fork_gevent'
)
if
'forkpty'
in
__implements__
:
def
forkpty_and_watch
(
callback
=
None
,
loop
=
None
,
ref
=
False
,
forkpty
=
forkpty_gevent
):
"""
Like :func:`fork_and_watch`, except using :func:`forkpty_gevent`.
Availability: Some Unix systems.
.. versionadded:: 1.1b5
"""
result
=
[]
def
_fork
():
pid_and_fd
=
forkpty
()
result
.
append
(
pid_and_fd
)
return
pid_and_fd
[
0
]
fork_and_watch
(
callback
,
loop
,
ref
,
_fork
)
return
result
[
0
]
__extensions__
.
append
(
'forkpty_and_watch'
)
# Watch children by default
# Watch children by default
if
not
os
.
getenv
(
'GEVENT_NOWAITPID'
):
if
not
os
.
getenv
(
'GEVENT_NOWAITPID'
):
# Broken out into separate functions instead of simple name aliases
# for documentation purposes.
def
fork
(
*
args
,
**
kwargs
):
def
fork
(
*
args
,
**
kwargs
):
"""
"""
Forks a child process and starts a child watcher for it in the
Forks a child process and starts a child watcher for it in the
...
@@ -332,6 +382,20 @@ if hasattr(os, 'fork'):
...
@@ -332,6 +382,20 @@ if hasattr(os, 'fork'):
"""
"""
# take any args to match fork_and_watch
# take any args to match fork_and_watch
return
fork_and_watch
(
*
args
,
**
kwargs
)
return
fork_and_watch
(
*
args
,
**
kwargs
)
if
'forkpty'
in
__implements__
:
def
forkpty
(
*
args
,
**
kwargs
):
"""
Like :func:`fork`, but using :func:`forkpty_gevent`.
This implementation of ``forkpty`` is a wrapper for :func:`forkpty_and_watch`
when the environment variable ``GEVENT_NOWAITPID`` is *not* defined.
This is the default and should be used by most applications.
.. versionadded:: 1.1b5
"""
# take any args to match fork_and_watch
return
forkpty_and_watch
(
*
args
,
**
kwargs
)
__implements__
.
append
(
"waitpid"
)
__implements__
.
append
(
"waitpid"
)
else
:
else
:
def
fork
():
def
fork
():
...
@@ -344,6 +408,19 @@ if hasattr(os, 'fork'):
...
@@ -344,6 +408,19 @@ if hasattr(os, 'fork'):
This is not recommended for most applications.
This is not recommended for most applications.
"""
"""
return
fork_gevent
()
return
fork_gevent
()
if
'forkpty'
in
__implements__
:
def
forkpty
():
"""
Like :func:`fork`, but using :func:`os.forkpty`
This implementation of ``forkptf`` is a wrapper for :func:`forkpty_gevent`
when the environment variable ``GEVENT_NOWAITPID`` *is* defined.
This is not recommended for most applications.
.. versionadded:: 1.1b5
"""
return
forkpty_gevent
()
__extensions__
.
append
(
"waitpid"
)
__extensions__
.
append
(
"waitpid"
)
else
:
else
:
...
...
greentest/test__monkey.py
View file @
0fdf78c9
...
@@ -20,8 +20,13 @@ from gevent import socket as gevent_socket
...
@@ -20,8 +20,13 @@ from gevent import socket as gevent_socket
assert
socket
.
create_connection
is
gevent_socket
.
create_connection
assert
socket
.
create_connection
is
gevent_socket
.
create_connection
import
os
import
os
if
hasattr
(
os
,
'fork'
):
import
types
assert
'built-in'
not
in
repr
(
os
.
fork
),
repr
(
os
.
fork
)
for
name
in
(
'fork'
,
'forkpty'
):
if
hasattr
(
os
,
name
):
attr
=
getattr
(
os
,
name
)
assert
'built-in'
not
in
repr
(
attr
),
repr
(
attr
)
assert
not
isinstance
(
attr
,
types
.
BuiltinFunctionType
),
repr
(
attr
)
assert
isinstance
(
attr
,
types
.
FunctionType
),
repr
(
attr
)
assert
monkey
.
saved
assert
monkey
.
saved
...
...
greentest/test__monkey_sigchld.py
View file @
0fdf78c9
...
@@ -8,7 +8,7 @@ import gevent.monkey
...
@@ -8,7 +8,7 @@ import gevent.monkey
gevent
.
monkey
.
patch_all
()
gevent
.
monkey
.
patch_all
()
pid
=
None
pid
=
None
awaiting_child
=
[
True
]
awaiting_child
=
[]
def
handle_sigchld
(
*
args
):
def
handle_sigchld
(
*
args
):
...
@@ -26,24 +26,44 @@ if hasattr(signal, 'SIGCHLD'):
...
@@ -26,24 +26,44 @@ if hasattr(signal, 'SIGCHLD'):
handler
=
signal
.
getsignal
(
signal
.
SIGCHLD
)
handler
=
signal
.
getsignal
(
signal
.
SIGCHLD
)
assert
signal
.
getsignal
(
signal
.
SIGCHLD
)
is
handle_sigchld
,
handler
assert
signal
.
getsignal
(
signal
.
SIGCHLD
)
is
handle_sigchld
,
handler
pid
=
os
.
fork
()
if
hasattr
(
os
,
'forkpty'
):
def
forkpty
():
# For printing in errors
return
os
.
forkpty
()[
0
]
funcs
=
(
os
.
fork
,
forkpty
)
else
:
funcs
=
(
os
.
fork
,)
for
func
in
funcs
:
awaiting_child
=
[
True
]
pid
=
func
()
if
not
pid
:
if
not
pid
:
# child
# child
gevent
.
sleep
(
0.2
)
gevent
.
sleep
(
0.3
)
sys
.
exit
(
0
)
sys
.
exit
(
0
)
else
:
else
:
with
gevent
.
Timeout
(
1
):
timeout
=
gevent
.
Timeout
(
1
)
try
:
while
awaiting_child
:
while
awaiting_child
:
gevent
.
sleep
(
0.01
)
gevent
.
sleep
(
0.01
)
# We should now be able to waitpid() for an arbitrary child
# We should now be able to waitpid() for an arbitrary child
wpid
,
status
=
os
.
waitpid
(
-
1
,
os
.
WNOHANG
)
wpid
,
status
=
os
.
waitpid
(
-
1
,
os
.
WNOHANG
)
assert
wpid
==
pid
if
wpid
!=
pid
:
raise
AssertionError
(
"Failed to wait on a child pid forked with a function"
,
wpid
,
pid
,
func
)
# And a second call should raise ECHILD
# And a second call should raise ECHILD
try
:
try
:
wpid
,
status
=
os
.
waitpid
(
-
1
,
os
.
WNOHANG
)
wpid
,
status
=
os
.
waitpid
(
-
1
,
os
.
WNOHANG
)
raise
AssertionError
(
"Should not be able to wait again"
)
raise
AssertionError
(
"Should not be able to wait again"
)
except
OSError
as
e
:
except
OSError
as
e
:
assert
e
.
errno
==
errno
.
ECHILD
assert
e
.
errno
==
errno
.
ECHILD
except
gevent
.
Timeout
as
t
:
if
timeout
is
not
t
:
raise
raise
AssertionError
(
"Failed to wait using"
,
func
)
finally
:
timeout
.
cancel
()
sys
.
exit
(
0
)
sys
.
exit
(
0
)
else
:
else
:
print
(
"No SIGCHLD, not testing"
)
print
(
"No SIGCHLD, not testing"
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment