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
4562b741
Commit
4562b741
authored
Mar 04, 2016
by
Jason Madden
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #758 from gevent/monkey-signal+os-warnings
Issue some warnings for patch_all with invalid parameters
parents
cb98a0c4
62fa048a
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
146 additions
and
11 deletions
+146
-11
changelog.rst
changelog.rst
+10
-0
doc/whatsnew_1_1.rst
doc/whatsnew_1_1.rst
+11
-0
gevent/monkey.py
gevent/monkey.py
+93
-10
gevent/os.py
gevent/os.py
+1
-0
gevent/subprocess.py
gevent/subprocess.py
+1
-1
greentest/test__monkey.py
greentest/test__monkey.py
+30
-0
No files found.
changelog.rst
View file @
4562b741
...
@@ -30,6 +30,16 @@
...
@@ -30,6 +30,16 @@
requires having Cython installed first. (Note that the binary installation
requires having Cython installed first. (Note that the binary installation
formats (wheels, exes, msis) are preferred on Windows.) Reported in
formats (wheels, exes, msis) are preferred on Windows.) Reported in
:issue:`757` by Ned Batchelder.
:issue:`757` by Ned Batchelder.
- Issue a warning when :func:`~gevent.monkey.patch_all` is called with
``os`` set to False (*not* the default) but ``signal`` is still True
(the default). This combination of parameters will cause signal
handlers for ``SIGCHLD`` to not get called. In the future this might
raise an error. Reported by Josh Zuech.
- Issue a warning when :func:`~gevent.monkey.patch_all` is called more
than once with different arguments. That causes the cumulative set of all True
arguments to be patched, which may cause unexpected results.
- Fix returning the original values of certain ``threading``
attributes from :func:`gevent.monkey.get_original`.
.. _bug 13502: http://bugs.python.org/issue13502
.. _bug 13502: http://bugs.python.org/issue13502
...
...
doc/whatsnew_1_1.rst
View file @
4562b741
...
@@ -119,6 +119,11 @@ downstream libraries, notably `gunicorn`_.
...
@@ -119,6 +119,11 @@ downstream libraries, notably `gunicorn`_.
:func:`gevent.os.waitpid` (again monkey patched by default) and
:func:`gevent.os.waitpid` (again monkey patched by default) and
:func:`gevent.signal.signal` (which is monkey patched only for the
:func:`gevent.signal.signal` (which is monkey patched only for the
:data:`signal.SIGCHLD` case). The latter two patches are new in 1.1.
:data:`signal.SIGCHLD` case). The latter two patches are new in 1.1.
- In gevent 1.0, use of libev child watchers (which are used
internally by ``gevent.subprocess``) had race conditions with
user-provided ``SIGCHLD`` handlers, causing many types of
unpredictable breakage. The two new APIs described above are
intended to rectify this.
- Fork-watchers will be called, even in multi-threaded programs
- Fork-watchers will be called, even in multi-threaded programs
(except on Windows).
(except on Windows).
- The default threadpool and threaded resolver work in child
- The default threadpool and threaded resolver work in child
...
@@ -138,6 +143,12 @@ possible in a monkey patched system, at least on POSIX platforms.
...
@@ -138,6 +143,12 @@ possible in a monkey patched system, at least on POSIX platforms.
..
caution
::
It
is
not
possible
to
use
:
mod
:`
gevent
.
subprocess
`
from
..
caution
::
It
is
not
possible
to
use
:
mod
:`
gevent
.
subprocess
`
from
native
threads
.
See
:
mod
:`
gevent
.
subprocess
`
for
details
.
native
threads
.
See
:
mod
:`
gevent
.
subprocess
`
for
details
.
..
note
::
If
the
``
SIGCHLD
``
signal
is
to
be
handled
,
it
is
important
to
monkey
patch
(
or
directly
use
)
both
:
mod
:`
os
`
and
:
mod
:`
signal
`;
this
is
the
default
for
:
func
:`~
gevent
.
monkey
.
patch_all
`.
Failure
to
do
so
can
result
in
the
``
SIGCHLD
``
signal
being
lost
.
..
tip
::
All
of
the
above
entail
forking
a
child
process
.
Forking
..
tip
::
All
of
the
above
entail
forking
a
child
process
.
Forking
a
child
process
that
uses
gevent
,
greenlets
,
and
libev
a
child
process
that
uses
gevent
,
greenlets
,
and
libev
can
have
some
unexpected
consequences
if
the
child
can
have
some
unexpected
consequences
if
the
child
...
...
gevent/monkey.py
View file @
4562b741
...
@@ -89,8 +89,8 @@ if sys.platform.startswith("win"):
...
@@ -89,8 +89,8 @@ if sys.platform.startswith("win"):
else
:
else
:
WIN
=
False
WIN
=
False
# maps module name ->
attribute name -> original item
# maps module name ->
{attribute name: original item}
# e.g. "time" ->
"sleep" -> built-in function sleep
# e.g. "time" ->
{"sleep": built-in function sleep}
saved
=
{}
saved
=
{}
...
@@ -161,6 +161,23 @@ def patch_module(name, items=None):
...
@@ -161,6 +161,23 @@ def patch_module(name, items=None):
raise
AttributeError
(
'%r does not have __implements__'
%
gevent_module
)
raise
AttributeError
(
'%r does not have __implements__'
%
gevent_module
)
for
attr
in
items
:
for
attr
in
items
:
patch_item
(
module
,
attr
,
getattr
(
gevent_module
,
attr
))
patch_item
(
module
,
attr
,
getattr
(
gevent_module
,
attr
))
return
module
def
_queue_warning
(
message
,
_warnings
):
# Queues a warning to show after the monkey-patching process is all done.
# Done this way to avoid extra imports during the process itself, just
# in case. If we're calling a function one-off (unusual) go ahead and do it
if
_warnings
is
None
:
_process_warnings
([
message
])
else
:
_warnings
.
append
(
message
)
def
_process_warnings
(
_warnings
):
import
warnings
for
warning
in
_warnings
:
warnings
.
warn
(
warning
,
RuntimeWarning
,
stacklevel
=
3
)
def
_patch_sys_std
(
name
):
def
_patch_sys_std
(
name
):
...
@@ -202,6 +219,9 @@ def patch_os():
...
@@ -202,6 +219,9 @@ def patch_os():
:func:`os.waitpid` with :func:`gevent.os.waitpid` (if the
:func:`os.waitpid` with :func:`gevent.os.waitpid` (if the
environment variable ``GEVENT_NOWAITPID`` is not defined). Does
environment variable ``GEVENT_NOWAITPID`` is not defined). Does
nothing if fork is not available.
nothing if fork is not available.
This method must be used with :func:`patch_signal` to have proper SIGCHLD
handling. :func:`patch_all` calls both by default.
"""
"""
patch_module
(
'os'
)
patch_module
(
'os'
)
...
@@ -252,7 +272,8 @@ def _patch_existing_locks(threading):
...
@@ -252,7 +272,8 @@ def _patch_existing_locks(threading):
def
patch_thread
(
threading
=
True
,
_threading_local
=
True
,
Event
=
False
,
logging
=
True
,
def
patch_thread
(
threading
=
True
,
_threading_local
=
True
,
Event
=
False
,
logging
=
True
,
existing_locks
=
True
):
existing_locks
=
True
,
_warnings
=
None
):
"""
"""
Replace the standard :mod:`thread` module to make it greenlet-based.
Replace the standard :mod:`thread` module to make it greenlet-based.
...
@@ -293,10 +314,20 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
...
@@ -293,10 +314,20 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
# return r, w
# return r, w
# os.pipe = _pipe
# os.pipe = _pipe
# The 'threading' module copies some attributes from the
# thread module the first time it is imported. If we patch 'thread'
# before that happens, then we store the wrong values in 'saved',
# So if we're going to patch threading, we either need to import it
# before we patch thread, or manually clean up the attributes that
# are in trouble. The latter is tricky because of the different names
# on different versions.
if
threading
:
__import__
(
'threading'
)
patch_module
(
'thread'
)
patch_module
(
'thread'
)
if
threading
:
if
threading
:
patch_module
(
'threading'
)
threading
=
patch_module
(
'threading'
)
threading
=
__import__
(
'threading'
)
if
Event
:
if
Event
:
from
gevent.event
import
Event
from
gevent.event
import
Event
patch_item
(
threading
,
'Event'
,
Event
)
patch_item
(
threading
,
'Event'
,
Event
)
...
@@ -348,11 +379,20 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
...
@@ -348,11 +379,20 @@ def patch_thread(threading=True, _threading_local=True, Event=False, logging=Tru
sleep
(
0.01
)
sleep
(
0.01
)
main_thread
.
join
=
join
main_thread
.
join
=
join
# Patch up the ident of the main thread to match. This
# matters if threading was imported before monkey-patching
# thread
oldid
=
main_thread
.
ident
main_thread
.
_ident
=
threading
.
get_ident
()
if
oldid
in
threading
.
_active
:
threading
.
_active
[
main_thread
.
ident
]
=
threading
.
_active
[
oldid
]
if
oldid
!=
main_thread
.
ident
:
del
threading
.
_active
[
oldid
]
else
:
else
:
# TODO: Can we use warnings here or does that mess up monkey patching?
_queue_warning
(
"Monkey-patching not on the main thread; "
print
(
"Monkey-patching not on the main thread; "
"threading.main_thread().join() will hang from a greenlet"
,
"threading.main_thread().join() will hang from a greenlet"
,
_warnings
)
file
=
sys
.
stderr
)
def
patch_socket
(
dns
=
True
,
aggressive
=
True
):
def
patch_socket
(
dns
=
True
,
aggressive
=
True
):
...
@@ -478,15 +518,50 @@ def patch_signal():
...
@@ -478,15 +518,50 @@ def patch_signal():
"""
"""
Make the signal.signal function work with a monkey-patched os.
Make the signal.signal function work with a monkey-patched os.
This method must be used with :func:`patch_os` to have proper SIGCHLD
handling. :func:`patch_all` calls both by default.
.. seealso:: :mod:`gevent.signal`
.. seealso:: :mod:`gevent.signal`
"""
"""
patch_module
(
"signal"
)
patch_module
(
"signal"
)
def
_check_repatching
(
**
module_settings
):
_warnings
=
[]
key
=
'_gevent_saved_patch_all'
if
saved
.
get
(
key
,
module_settings
)
!=
module_settings
:
_queue_warning
(
"Patching more than once will result in the union of all True"
" parameters being patched"
,
_warnings
)
first_time
=
key
not
in
saved
saved
[
key
]
=
module_settings
return
_warnings
,
first_time
def
patch_all
(
socket
=
True
,
dns
=
True
,
time
=
True
,
select
=
True
,
thread
=
True
,
os
=
True
,
ssl
=
True
,
httplib
=
False
,
def
patch_all
(
socket
=
True
,
dns
=
True
,
time
=
True
,
select
=
True
,
thread
=
True
,
os
=
True
,
ssl
=
True
,
httplib
=
False
,
subprocess
=
True
,
sys
=
False
,
aggressive
=
True
,
Event
=
False
,
subprocess
=
True
,
sys
=
False
,
aggressive
=
True
,
Event
=
False
,
builtins
=
True
,
signal
=
True
):
builtins
=
True
,
signal
=
True
):
"""Do all of the default monkey patching (calls every other applicable function in this module)."""
"""
Do all of the default monkey patching (calls every other applicable
function in this module).
.. versionchanged:: 1.1
Issue a :mod:`warning <warnings>` if this function is called multiple times
with different arguments. The second and subsequent calls will only add more
patches, they can never remove existing patches by setting an argument to ``False``.
.. versionchanged:: 1.1
Issue a :mod:`warning <warnings>` if this function is called with ``os=False``
and ``signal=True``. This will cause SIGCHLD handlers to not be called. This may
be an error in the future.
"""
# Check to see if they're changing the patched list
_warnings
,
first_time
=
_check_repatching
(
**
locals
())
if
not
_warnings
and
not
first_time
:
# Nothing to do, identical args to what we just
# did
return
# order is important
# order is important
if
os
:
if
os
:
patch_os
()
patch_os
()
...
@@ -511,8 +586,16 @@ def patch_all(socket=True, dns=True, time=True, select=True, thread=True, os=Tru
...
@@ -511,8 +586,16 @@ def patch_all(socket=True, dns=True, time=True, select=True, thread=True, os=Tru
if
builtins
:
if
builtins
:
patch_builtins
()
patch_builtins
()
if
signal
:
if
signal
:
if
not
os
:
_queue_warning
(
'Patching signal but not os will result in SIGCHLD handlers'
' installed after this not being called and os.waitpid may not'
' function correctly if gevent.subprocess is used. This may raise an'
' error in the future.'
,
_warnings
)
patch_signal
()
patch_signal
()
_process_warnings
(
_warnings
)
def
main
():
def
main
():
args
=
{}
args
=
{}
...
...
gevent/os.py
View file @
4562b741
...
@@ -210,6 +210,7 @@ if hasattr(os, 'fork'):
...
@@ -210,6 +210,7 @@ if hasattr(os, 'fork'):
_waitpid
=
os
.
waitpid
_waitpid
=
os
.
waitpid
_WNOHANG
=
os
.
WNOHANG
_WNOHANG
=
os
.
WNOHANG
# replaced by the signal module.
_on_child_hook
=
lambda
:
None
_on_child_hook
=
lambda
:
None
# {pid -> watcher or tuple(pid, rstatus, timestamp)}
# {pid -> watcher or tuple(pid, rstatus, timestamp)}
...
...
gevent/subprocess.py
View file @
4562b741
...
@@ -45,7 +45,7 @@ __implements__ = [
...
@@ -45,7 +45,7 @@ __implements__ = [
'check_call'
,
'check_call'
,
'check_output'
,
'check_output'
,
]
]
if
PY3
:
if
PY3
and
not
sys
.
platform
.
startswith
(
'win32'
)
:
__implements__
.
append
(
"_posixsubprocess"
)
__implements__
.
append
(
"_posixsubprocess"
)
_posixsubprocess
=
None
_posixsubprocess
=
None
...
...
greentest/test__monkey.py
View file @
4562b741
...
@@ -39,3 +39,33 @@ for modname in monkey.saved:
...
@@ -39,3 +39,33 @@ for modname in monkey.saved:
for
objname
in
monkey
.
saved
[
modname
]:
for
objname
in
monkey
.
saved
[
modname
]:
assert
monkey
.
is_object_patched
(
modname
,
objname
)
assert
monkey
.
is_object_patched
(
modname
,
objname
)
orig_saved
=
{}
for
k
,
v
in
monkey
.
saved
.
items
():
orig_saved
[
k
]
=
v
.
copy
()
import
warnings
with
warnings
.
catch_warnings
(
record
=
True
)
as
issued_warnings
:
# Patch again, triggering two warnings, on for os=False/signal=True,
# one for repeated monkey-patching.
monkey
.
patch_all
(
os
=
False
)
assert
len
(
issued_warnings
)
==
2
,
len
(
issued_warnings
)
assert
'SIGCHLD'
in
str
(
issued_warnings
[
-
1
].
message
),
issued_warnings
[
-
1
]
assert
'more than once'
in
str
(
issued_warnings
[
0
].
message
),
issued_warnings
[
0
]
# Patching with the exact same argument doesn't issue a second warning.
# in fact, it doesn't do anything
del
issued_warnings
[:]
monkey
.
patch_all
(
os
=
False
)
orig_saved
[
'_gevent_saved_patch_all'
]
=
monkey
.
saved
[
'_gevent_saved_patch_all'
]
assert
len
(
issued_warnings
)
==
0
,
len
(
issued_warnings
)
# Make sure that re-patching did not change the monkey.saved
# attribute, overwriting the original functions.
assert
orig_saved
==
monkey
.
saved
,
(
orig_saved
,
monkey
.
saved
)
# Make sure some problematic attributes stayed correct.
# NOTE: This was only a problem if threading was not previously imported.
for
k
,
v
in
monkey
.
saved
[
'threading'
].
items
():
assert
'gevent'
not
in
str
(
v
),
(
k
,
v
)
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