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
57b465b3
Commit
57b465b3
authored
Apr 03, 2018
by
Jason Madden
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add extension points to gevent.monkey using events and setuptools entry points.
Fixes #1162. Refs #1158.
parent
5f881cae
Changes
18
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
647 additions
and
104 deletions
+647
-104
.pylintrc
.pylintrc
+1
-0
CHANGES.rst
CHANGES.rst
+10
-4
doc/gevent.rst
doc/gevent.rst
+3
-3
setup.py
setup.py
+5
-0
src/gevent/_config.py
src/gevent/_config.py
+4
-6
src/gevent/_monitor.py
src/gevent/_monitor.py
+3
-14
src/gevent/events.py
src/gevent/events.py
+309
-0
src/gevent/hub.py
src/gevent/hub.py
+5
-0
src/gevent/monkey.py
src/gevent/monkey.py
+242
-59
src/greentest/greentest/testcase.py
src/greentest/greentest/testcase.py
+16
-5
src/greentest/test__core_timer.py
src/greentest/test__core_timer.py
+1
-0
src/greentest/test__issue330.py
src/greentest/test__issue330.py
+1
-0
src/greentest/test__issue6.py
src/greentest/test__issue6.py
+1
-1
src/greentest/test__monkey.py
src/greentest/test__monkey.py
+26
-5
src/greentest/test__monkey_scope.py
src/greentest/test__monkey_scope.py
+6
-3
src/greentest/test__monkey_sigchld_2.py
src/greentest/test__monkey_sigchld_2.py
+9
-3
src/greentest/test__subprocess.py
src/greentest/test__subprocess.py
+1
-0
src/greentest/test_threading_2.py
src/greentest/test_threading_2.py
+4
-1
No files found.
.pylintrc
View file @
57b465b3
...
...
@@ -96,6 +96,7 @@ ignored-modules=gevent._corecffi,gevent.os,os,greenlet,threading,gevent.libev.co
[DESIGN]
max-attributes=12
max-parents=10
[BASIC]
bad-functions=input
...
...
CHANGES.rst
View file @
57b465b3
...
...
@@ -68,6 +68,14 @@ Enhancements
``<script>``, including paths to packages or compiled bytecode.
Reported in :issue:`1157` by Eddie Linder.
- Add a simple event framework for decoupled communication. It uses
:mod:`zope.event` if that is installed.
- :mod:`gevent.monkey` has support for plugins in the form of event
subscribers and setuptools entry points. See :pr:`1158` and
:issue:`1162`. setuptools must be installed at runtime for its entry
points to function.
Monitoring and Debugging
------------------------
...
...
@@ -84,10 +92,8 @@ Monitoring and Debugging
use it, and ``GEVENT_MAX_BLOCKING_TIME`` to configure the blocking
interval.
- Add a simple event framework for decoupled communication. It uses
:mod:`zope.event` if that is installed. The monitoring thread emits
events when it detects certain conditions, like loop blocked or
memory limits exceeded.
- The monitoring thread emits events when it detects certain
conditions, like loop blocked or memory limits exceeded.
- Add settings for monitoring memory usage and emitting events when a
threshold is exceeded and then corrected. gevent currently supplies
...
...
doc/gevent.rst
View file @
57b465b3
==================================
:mod:`gevent` -- basic utilities
==================================
==================================
==================
:mod:`gevent` -- basic utilities
and configuration
==================================
==================
.. module:: gevent
...
...
setup.py
View file @
57b465b3
...
...
@@ -344,6 +344,11 @@ def run_setup(ext_modules, run_make):
"Development Status :: 4 - Beta"
],
python_requires
=
">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
,
entry_points
=
{
'gevent.plugins.monkey.will_patch_all'
:
[
"signal_os_incompat = gevent.monkey:_subscribe_signal_os"
,
],
},
)
# Tools like pyroma expect the actual call to `setup` to be performed
...
...
src/gevent/_config.py
View file @
57b465b3
...
...
@@ -487,13 +487,11 @@ class MonitorThread(BoolSettingMixin, Setting):
If this setting is true, then this thread will be created
the first time the hub is switched to,
or you can call `gevent.hub.Hub.start_periodic_monitoring_thread` at any
or you can call
:meth:
`gevent.hub.Hub.start_periodic_monitoring_thread` at any
time to create it (from the same thread that will run the hub). That function
will return an object with a method ``add_monitoring_function(function, period)``
that you can call to add your own periodic monitoring function. ``function``
will be called with one argument, the hub it is monitoring. It will be called
in a separate native thread than the one running the hub and **must not**
attempt to use the gevent asynchronous API.
will return an instance of :class:`gevent.events.IPeriodicMonitorThread`
to which you can add your own monitoring functions. That function
also emits an event of :class:`gevent.events.PeriodicMonitorThreadStartedEvent`.
.. seealso:: `max_blocking_time`
...
...
src/gevent/_monitor.py
View file @
57b465b3
...
...
@@ -16,6 +16,8 @@ from gevent.events import notify
from
gevent.events
import
EventLoopBlocked
from
gevent.events
import
MemoryUsageThresholdExceeded
from
gevent.events
import
MemoryUsageUnderThreshold
from
gevent.events
import
IPeriodicMonitorThread
from
gevent.events
import
implementer
from
gevent._compat
import
thread_mod_name
from
gevent._compat
import
perf_counter
...
...
@@ -63,6 +65,7 @@ class _MonitorEntry(object):
def
__repr__
(
self
):
return
repr
((
self
.
function
,
self
.
period
,
self
.
last_run_time
))
@
implementer
(
IPeriodicMonitorThread
)
class
PeriodicMonitoringThread
(
object
):
# The amount of seconds we will sleep when we think we have nothing
...
...
@@ -145,20 +148,6 @@ class PeriodicMonitoringThread(object):
return
self
.
_monitoring_functions
def
add_monitoring_function
(
self
,
function
,
period
):
"""
Schedule the *function* to be called approximately every *period* fractional seconds.
The *function* receives one argument, the hub being monitored. It is called
in the monitoring thread, *not* the hub thread.
If the *function* is already a monitoring function, then its *period*
will be updated for future runs.
If the *period* is ``None``, then the function will be removed.
A *period* less than or equal to zero is not allowed.
"""
if
not
callable
(
function
):
raise
ValueError
(
"function must be callable"
)
...
...
src/gevent/events.py
View file @
57b465b3
...
...
@@ -28,14 +28,41 @@ from __future__ import print_function
__all__
=
[
'subscribers'
,
# monitor thread
'IEventLoopBlocked'
,
'EventLoopBlocked'
,
'IMemoryUsageThresholdExceeded'
,
'MemoryUsageThresholdExceeded'
,
'IMemoryUsageUnderThreshold'
,
'MemoryUsageUnderThreshold'
,
# Hub
'IPeriodicMonitorThread'
,
'IPeriodicMonitorThreadStartedEvent'
,
'PeriodicMonitorThreadStartedEvent'
,
# monkey
'IGeventPatchEvent'
,
'GeventPatchEvent'
,
'IGeventWillPatchEvent'
,
'DoNotPatch'
,
'GeventWillPatchEvent'
,
'IGeventDidPatchEvent'
,
'IGeventWillPatchModuleEvent'
,
'GeventWillPatchModuleEvent'
,
'IGeventDidPatchModuleEvent'
,
'GeventDidPatchModuleEvent'
,
'IGeventWillPatchAllEvent'
,
'GeventWillPatchAllEvent'
,
'IGeventDidPatchBuiltinModulesEvent'
,
'GeventDidPatchBuiltinModulesEvent'
,
'IGeventDidPatchAllEvent'
,
'GeventDidPatchAllEvent'
,
]
# pylint:disable=no-self-argument
try
:
from
zope.event
import
subscribers
from
zope.event
import
notify
...
...
@@ -58,6 +85,32 @@ except ImportError:
subscriber
(
event
)
notify
=
notify
# export
try
:
# pkg_resources is technically optional, we don't
# list a hard dependency on it.
__import__
(
'pkg_resources'
)
except
ImportError
:
notify_and_call_entry_points
=
notify
else
:
from
pkg_resources
import
iter_entry_points
import
platform
try
:
# Cache the platform info. pkg_resources uses
# platform.machine() for environment markers, and
# platform.machine() wants to call os.popen('uname'), which is
# broken on Py2 when the gevent child signal handler is
# installed. (see test__monkey_sigchild_2.py)
platform
.
uname
()
except
:
# pylint:disable=bare-except
pass
finally
:
del
platform
def
notify_and_call_entry_points
(
event
):
notify
(
event
)
for
plugin
in
iter_entry_points
(
event
.
ENTRY_POINT_NAME
):
subscriber
=
plugin
.
load
()
subscriber
(
event
)
try
:
from
zope.interface
import
Interface
...
...
@@ -74,6 +127,48 @@ except ImportError:
def
Attribute
(
s
):
return
s
class
IPeriodicMonitorThread
(
Interface
):
"""
The contract for the periodic monitoring thread that is started
by the hub.
"""
def
add_monitoring_function
(
function
,
period
):
"""
Schedule the *function* to be called approximately every *period* fractional seconds.
The *function* receives one argument, the hub being monitored. It is called
in the monitoring thread, *not* the hub thread. It **must not** attempt to
use the gevent asynchronous API.
If the *function* is already a monitoring function, then its *period*
will be updated for future runs.
If the *period* is ``None``, then the function will be removed.
A *period* less than or equal to zero is not allowed.
"""
class
IPeriodicMonitorThreadStartedEvent
(
Interface
):
"""
The event emitted when a hub starts a periodic monitoring thread.
You can use this event to add additional monitoring functions.
"""
monitor
=
Attribute
(
"The instance of `IPeriodicMonitorThread` that was started."
)
class
PeriodicMonitorThreadStartedEvent
(
object
):
"""
The implementation of :class:`IPeriodicMonitorThreadStartedEvent`.
"""
#: The name of the setuptools entry point that is called when this
#: event is emitted.
ENTRY_POINT_NAME
=
'gevent.plugins.hub.periodic_monitor_thread_started'
def
__init__
(
self
,
monitor
):
self
.
monitor
=
monitor
class
IEventLoopBlocked
(
Interface
):
"""
...
...
@@ -165,3 +260,217 @@ class MemoryUsageUnderThreshold(_AbstractMemoryEvent):
def
__init__
(
self
,
mem_usage
,
max_allowed
,
memory_info
,
max_usage
):
super
(
MemoryUsageUnderThreshold
,
self
).
__init__
(
mem_usage
,
max_allowed
,
memory_info
)
self
.
max_memory_usage
=
max_usage
class
IGeventPatchEvent
(
Interface
):
"""
The root for all monkey-patch events gevent emits.
"""
source
=
Attribute
(
"The source object containing the patches."
)
target
=
Attribute
(
"The destination object to be patched."
)
@
implementer
(
IGeventPatchEvent
)
class
GeventPatchEvent
(
object
):
"""
Implementation of `IGeventPatchEvent`.
"""
def
__init__
(
self
,
source
,
target
):
self
.
source
=
source
self
.
target
=
target
def
__repr__
(
self
):
return
'<%s source=%r target=%r at %x>'
%
(
self
.
__class__
.
__name__
,
self
.
source
,
self
.
target
,
id
(
self
))
class
IGeventWillPatchEvent
(
IGeventPatchEvent
):
"""
An event emitted *before* gevent monkey-patches something.
If a subscriber raises `DoNotPatch`, then patching this particular
item will not take place.
"""
class
DoNotPatch
(
BaseException
):
"""
Subscribers to will-patch events can raise instances
of this class to tell gevent not to patch that particular item.
"""
@
implementer
(
IGeventWillPatchEvent
)
class
GeventWillPatchEvent
(
GeventPatchEvent
):
"""
Implementation of `IGeventWillPatchEvent`.
"""
class
IGeventDidPatchEvent
(
IGeventPatchEvent
):
"""
An event emitted *after* gevent has patched something.
"""
@
implementer
(
IGeventDidPatchEvent
)
class
GeventDidPatchEvent
(
GeventPatchEvent
):
"""
Implementation of `IGeventDidPatchEvent`.
"""
class
IGeventWillPatchModuleEvent
(
IGeventWillPatchEvent
):
"""
An event emitted *before* gevent begins patching a specific module.
Both *source* and *target* attributes are module objects.
"""
module_name
=
Attribute
(
"The name of the module being patched. "
"This is the same as ``target.__name__``."
)
target_item_names
=
Attribute
(
"The list of item names to patch. "
"This can be modified in place with caution."
)
@
implementer
(
IGeventWillPatchModuleEvent
)
class
GeventWillPatchModuleEvent
(
GeventWillPatchEvent
):
"""
Implementation of `IGeventWillPatchModuleEvent`.
"""
#: The name of the setuptools entry point that is called when this
#: event is emitted.
ENTRY_POINT_NAME
=
'gevent.plugins.monkey.will_patch_module'
def
__init__
(
self
,
module_name
,
source
,
target
,
items
):
super
(
GeventWillPatchModuleEvent
,
self
).
__init__
(
source
,
target
)
self
.
module_name
=
module_name
self
.
target_item_names
=
items
class
IGeventDidPatchModuleEvent
(
IGeventDidPatchEvent
):
"""
An event emitted *after* gevent has completed patching a specific
module.
"""
module_name
=
Attribute
(
"The name of the module being patched. "
"This is the same as ``target.__name__``."
)
@
implementer
(
IGeventDidPatchModuleEvent
)
class
GeventDidPatchModuleEvent
(
GeventDidPatchEvent
):
"""
Implementation of `IGeventDidPatchModuleEvent`.
"""
#: The name of the setuptools entry point that is called when this
#: event is emitted.
ENTRY_POINT_NAME
=
'gevent.plugins.monkey.did_patch_module'
def
__init__
(
self
,
module_name
,
source
,
target
):
super
(
GeventDidPatchModuleEvent
,
self
).
__init__
(
source
,
target
)
self
.
module_name
=
module_name
# TODO: Maybe it would be useful for the the module patch events
# to have an attribute telling if they're being done during patch_all?
class
IGeventWillPatchAllEvent
(
IGeventWillPatchEvent
):
"""
An event emitted *before* gevent begins patching the system.
Following this event will be a series of
`IGeventWillPatchModuleEvent` and `IGeventDidPatchModuleEvent` for
each patched module.
Once the gevent builtin modules have been processed,
`IGeventDidPatchBuiltinModulesEvent` will be emitted. Processing
this event is an ideal time for third-party modules to be imported
and patched (which may trigger its own will/did patch module
events).
Finally, a `IGeventDidPatchAllEvent` will be sent.
If a subscriber to this event raises `DoNotPatch`, no patching
will be done.
The *source* and *target* attributes have undefined values.
"""
patch_all_arguments
=
Attribute
(
"A dictionary all the arguments to `gevent.monkey.patch_all`. "
"This dictionary should not be modified. "
)
def
will_patch_module
(
module_name
):
"""
Return whether the module named *module_name* will be patched.
"""
class
_PatchAllMixin
(
object
):
def
__init__
(
self
,
patch_all_arguments
):
super
(
_PatchAllMixin
,
self
).
__init__
(
None
,
None
)
self
.
_patch_all_arguments
=
patch_all_arguments
@
property
def
patch_all_arguments
(
self
):
return
self
.
_patch_all_arguments
.
copy
()
def
__repr__
(
self
):
return
'<%s %r at %x>'
%
(
self
.
__class__
.
__name__
,
self
.
_patch_all_arguments
,
id
(
self
))
@
implementer
(
IGeventWillPatchAllEvent
)
class
GeventWillPatchAllEvent
(
_PatchAllMixin
,
GeventWillPatchEvent
):
"""
Implementation of `IGeventWillPatchAllEvent`.
"""
#: The name of the setuptools entry point that is called when this
#: event is emitted.
ENTRY_POINT_NAME
=
'gevent.plugins.monkey.will_patch_all'
def
will_patch_module
(
self
,
module_name
):
return
self
.
patch_all_arguments
.
get
(
module_name
)
class
IGeventDidPatchBuiltinModulesEvent
(
IGeventDidPatchEvent
):
"""
Event emitted *after* the builtin modules have been patched.
The values of the *source* and *target* attributes are undefined.
"""
patch_all_arguments
=
Attribute
(
"A dictionary of all the arguments to `gevent.monkey.patch_all`. "
)
class
GeventDidPatchBuiltinModulesEvent
(
_PatchAllMixin
,
GeventDidPatchEvent
):
"""
Implementation of `IGeventDidPatchBuiltinModulesEvent`.
"""
#: The name of the setuptools entry point that is called when this
#: event is emitted.
ENTRY_POINT_NAME
=
'gevent.plugins.monkey.did_patch_builtins'
class
IGeventDidPatchAllEvent
(
IGeventDidPatchEvent
):
"""
Event emitted after gevent has patched all modules, both builtin
and those provided by plugins/subscribers.
The values of the *source* and *target* attributes are undefined.
"""
@
implementer
(
IGeventDidPatchAllEvent
)
class
GeventDidPatchAllEvent
(
GeventDidPatchEvent
):
"""
Implementation of `IGeventDidPatchAllEvent`.
"""
#: The name of the setuptools entry point that is called when this
#: event is emitted.
ENTRY_POINT_NAME
=
'gevent.plugins.monkey.did_patch_all'
def
__init__
(
self
):
super
(
GeventDidPatchAllEvent
,
self
).
__init__
(
None
,
None
)
src/gevent/hub.py
View file @
57b465b3
...
...
@@ -564,11 +564,16 @@ class Hub(WaitOperationsGreenlet):
# in the threadpool tests. The monitoring threads will eventually notice their
# hub object is gone.
from
gevent._monitor
import
PeriodicMonitoringThread
from
gevent.events
import
PeriodicMonitorThreadStartedEvent
from
gevent.events
import
notify_and_call_entry_points
self
.
periodic_monitoring_thread
=
PeriodicMonitoringThread
(
self
)
if
self
.
main_hub
:
self
.
periodic_monitoring_thread
.
install_monitor_memory_usage
()
notify_and_call_entry_points
(
PeriodicMonitorThreadStartedEvent
(
self
.
periodic_monitoring_thread
))
return
self
.
periodic_monitoring_thread
def
join
(
self
,
timeout
=
None
):
...
...
src/gevent/monkey.py
View file @
57b465b3
...
...
@@ -3,9 +3,6 @@
"""
Make the standard library cooperative.
Patching
========
The primary purpose of this module is to carefully patch, in place,
portions of the standard library with gevent-friendly functions that
behave in the same way as the original (at least as closely as possible).
...
...
@@ -32,8 +29,13 @@ thread** and **should be done while the program is single-threaded**.
Some frameworks, such as gunicorn, handle monkey-patching for you.
Check their documentation to be sure.
.. warning::
Patching too late can lead to unreliable behaviour (for example, some
modules may still use blocking sockets) or even errors.
Querying
--------
========
Sometimes it is helpful to know if objects have been monkey-patched, and in
advanced cases even to have access to the original standard library functions. This
...
...
@@ -43,6 +45,64 @@ module provides functions for that purpose.
- :func:`is_object_patched`
- :func:`get_original`
Plugins
=======
Beginning in gevent 1.3, events are emitted during the monkey patching process.
These events are delivered first to :mod:`gevent.events` subscribers, and then
to `setuptools entry points`_.
The following events are defined. They are listed in (roughly) the order
that a call to :func:`patch_all` will emit them.
- :class:`gevent.events.GeventWillPatchAllEvent`
- :class:`gevent.events.GeventWillPatchModuleEvent`
- :class:`gevent.events.GeventDidPatchModuleEvent`
- :class:`gevent.events.GeventDidPatchBuiltinModulesEvent`
- :class:`gevent.events.GeventDidPatchAllEvent`
Each event class documents the corresponding setuptools entry point name. The
entry points will be called with a single argument, the same instance of
the class that was sent to the subscribers.
You can subscribe to the events to monitor the monkey-patching process and
to manipulate it, for example by raising :exc:`gevent.events.DoNotPatch`.
You can also subscribe to the events to provide additional patching beyond what
gevent distributes, either for additional standard library modules, or
for third-party packages. The suggested time to do this patching is in
the subscriber for :class:`gevent.events.GeventDidPatchBuiltinModulesEvent`.
For example, to automatically patch `psycopg2`_ using `psycogreen`_
when the call to :func:`patch_all` is made, you could write code like this::
# mypackage.py
def patch_psycopg(event):
from psycogreen.gevent import patch_psycopg
patch_psycopg()
In your ``setup.py`` you would register it like this::
from setuptools import setup
setup(
...
entry_points={
'gevent.plugins.monkey.did_patch_builtins': [
'psycopg2 = mypackage:patch_psycopg',
],
},
...
)
For more complex patching, gevent provides a helper method
that you can call to replace attributes of modules with attributes of your
own modules. This function also takes care of emitting the appropriate events.
- :func:`patch_module`
.. _setuptools entry points: http://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
.. _psycopg2: https://pypi.python.org/pypi/psycopg2
.. _psycogreen: https://pypi.python.org/pypi/psycogreen
Use as a module
===============
...
...
@@ -51,8 +111,8 @@ were not built to be gevent aware under gevent. To do so, this module
can be run as the main module, passing the script and its arguments.
For details, see the :func:`main` function.
Functions
=========
.. versionchanged:: 1.3b1
Added support for plugins and began emitting will/did patch events.
"""
from
__future__
import
absolute_import
from
__future__
import
print_function
...
...
@@ -75,6 +135,8 @@ __all__ = [
'get_original'
,
'is_module_patched'
,
'is_object_patched'
,
# plugin API
'patch_module'
,
# module functions
'main'
,
]
...
...
@@ -97,6 +159,27 @@ class MonkeyPatchWarning(RuntimeWarning):
.. versionadded:: 1.3a2
"""
def
_notify_patch
(
event
,
_warnings
=
None
):
# Raises DoNotPatch if we're not supposed to patch
from
gevent.events
import
notify_and_call_entry_points
event
.
_warnings
=
_warnings
notify_and_call_entry_points
(
event
)
def
_ignores_DoNotPatch
(
func
):
from
functools
import
wraps
@
wraps
(
func
)
def
ignores
(
*
args
,
**
kwargs
):
from
gevent.events
import
DoNotPatch
try
:
return
func
(
*
args
,
**
kwargs
)
except
DoNotPatch
:
return
False
return
ignores
# maps module name -> {attribute name: original item}
# e.g. "time" -> {"sleep": built-in function sleep}
...
...
@@ -180,35 +263,94 @@ def remove_item(module, attr):
delattr
(
module
,
attr
)
def
__call_module_hook
(
gevent_module
,
name
,
module
,
items
,
warn
):
def
__call_module_hook
(
gevent_module
,
name
,
module
,
items
,
_warnings
):
# This function can raise DoNotPatch on 'will'
def
warn
(
message
):
_queue_warning
(
message
,
_warnings
)
func_name
=
'_gevent_'
+
name
+
'_monkey_patch'
try
:
func
=
getattr
(
gevent_module
,
func_name
)
except
AttributeError
:
pass
else
:
func
=
lambda
*
args
:
None
func
(
module
,
items
,
warn
)
def
patch_module
(
name
,
items
=
None
,
_warnings
=
None
):
def
warn
(
message
):
_queue_warning
(
message
,
_warnings
)
gevent_module
=
getattr
(
__import__
(
'gevent.'
+
name
),
name
)
module_name
=
getattr
(
gevent_module
,
'__target__'
,
name
)
module
=
__import__
(
module_name
)
def
patch_module
(
target_module
,
source_module
,
items
=
None
,
_warnings
=
None
,
_notify_did_subscribers
=
True
):
"""
patch_module(target_module, source_module, items=None)
Replace attributes in *target_module* with the attributes of the
same name in *source_module*.
The *source_module* can provide some attributes to customize the process:
* ``__implements__`` is a list of attribute names to copy; if not present,
the *items* keyword argument is mandatory.
* ``_gevent_will_monkey_patch(target_module, items, warn, **kwargs)``
* ``_gevent_did_monkey_patch(target_module, items, warn, **kwargs)``
These two functions in the *source_module* are called *if* they exist,
before and after copying attributes, respectively. The "will" function
may modify *items*. The value of *warn* is a function that should be called
with a single string argument to issue a warning to the user. If the "will"
function raises :exc:`gevent.events.DoNotPatch`, no patching will be done. These functions
are called before any event subscribers or plugins.
:keyword list items: A list of attribute names to replace. If
not given, this will be taken from the *source_module* ``__implements__``
attribute.
:return: A true value if patching was done, a false value if patching was canceled.
.. versionadded:: 1.3b1
"""
from
gevent
import
events
if
items
is
None
:
items
=
getattr
(
gevent
_module
,
'__implements__'
,
None
)
items
=
getattr
(
source
_module
,
'__implements__'
,
None
)
if
items
is
None
:
raise
AttributeError
(
'%r does not have __implements__'
%
gevent
_module
)
raise
AttributeError
(
'%r does not have __implements__'
%
source
_module
)
__call_module_hook
(
gevent_module
,
'will'
,
module
,
items
,
warn
)
try
:
__call_module_hook
(
source_module
,
'will'
,
target_module
,
items
,
_warnings
)
_notify_patch
(
events
.
GeventWillPatchModuleEvent
(
target_module
.
__name__
,
source_module
,
target_module
,
items
),
_warnings
)
except
events
.
DoNotPatch
:
return
False
for
attr
in
items
:
patch_item
(
module
,
attr
,
getattr
(
gevent_module
,
attr
))
patch_item
(
target_module
,
attr
,
getattr
(
source_module
,
attr
))
__call_module_hook
(
source_module
,
'did'
,
target_module
,
items
,
_warnings
)
if
_notify_did_subscribers
:
# We allow turning off the broadcast of the 'did' event for the benefit
# of our internal functions which need to do additional work (besides copying
# attributes) before their patch can be considered complete.
_notify_patch
(
events
.
GeventDidPatchModuleEvent
(
target_module
.
__name__
,
source_module
,
target_module
)
)
__call_module_hook
(
gevent_module
,
'did'
,
module
,
items
,
warn
)
return
True
return
module
def
_patch_module
(
name
,
items
=
None
,
_warnings
=
None
,
_notify_did_subscribers
=
True
):
gevent_module
=
getattr
(
__import__
(
'gevent.'
+
name
),
name
)
module_name
=
getattr
(
gevent_module
,
'__target__'
,
name
)
target_module
=
__import__
(
module_name
)
patch_module
(
target_module
,
gevent_module
,
items
=
items
,
_warnings
=
_warnings
,
_notify_did_subscribers
=
_notify_did_subscribers
)
return
gevent_module
,
target_module
def
_queue_warning
(
message
,
_warnings
):
...
...
@@ -233,7 +375,7 @@ def _patch_sys_std(name):
if
not
isinstance
(
orig
,
FileObjectThread
):
patch_item
(
sys
,
name
,
FileObjectThread
(
orig
))
@
_ignores_DoNotPatch
def
patch_sys
(
stdin
=
True
,
stdout
=
True
,
stderr
=
True
):
"""
Patch sys.std[in,out,err] to use a cooperative IO via a
...
...
@@ -254,16 +396,27 @@ def patch_sys(stdin=True, stdout=True, stderr=True):
# test__issue6.py demonstrates the hang if these lines are removed;
# strangely enough that test passes even without monkey-patching sys
if
PY3
:
items
=
None
else
:
items
=
set
([(
'stdin'
if
stdin
else
None
),
(
'stdout'
if
stdout
else
None
),
(
'stderr'
if
stderr
else
None
)])
items
.
discard
(
None
)
items
=
list
(
items
)
if
not
items
:
return
if
stdin
:
_patch_sys_std
(
'stdin'
)
if
stdout
:
_patch_sys_std
(
'stdout'
)
if
stderr
:
_patch_sys_std
(
'stderr'
)
from
gevent
import
events
_notify_patch
(
events
.
GeventWillPatchModuleEvent
(
'sys'
,
None
,
sys
,
items
))
for
item
in
items
:
_patch_sys_std
(
item
)
_notify_patch
(
events
.
GeventDidPatchModuleEvent
(
'sys'
,
None
,
sys
))
@
_ignores_DoNotPatch
def
patch_os
():
"""
Replace :func:`os.fork` with :func:`gevent.fork`, and, on POSIX,
...
...
@@ -278,14 +431,15 @@ def patch_os():
.. caution:: For `SIGCHLD` handling to work correctly, the event loop must run.
The easiest way to help ensure this is to use :func:`patch_all`.
"""
patch_module
(
'os'
)
_
patch_module
(
'os'
)
@
_ignores_DoNotPatch
def
patch_time
():
"""
Replace :func:`time.sleep` with :func:`gevent.sleep`.
"""
patch_module
(
'time'
)
_
patch_module
(
'time'
)
def
_patch_existing_locks
(
threading
):
...
...
@@ -325,7 +479,7 @@ def _patch_existing_locks(threading):
if
o
.
owner
is
not
None
:
o
.
owner
=
tid
@
_ignores_DoNotPatch
def
patch_thread
(
threading
=
True
,
_threading_local
=
True
,
Event
=
True
,
logging
=
True
,
existing_locks
=
True
,
_warnings
=
None
):
...
...
@@ -395,12 +549,15 @@ def patch_thread(threading=True, _threading_local=True, Event=True, logging=True
orig_current_thread
=
threading_mod
.
current_thread
()
else
:
threading_mod
=
None
gevent_threading_mod
=
None
orig_current_thread
=
None
patch_module
(
'thread'
,
_warnings
=
_warnings
)
gevent_thread_mod
,
thread_mod
=
_patch_module
(
'thread'
,
_warnings
=
_warnings
,
_notify_did_subscribers
=
False
)
if
threading
:
patch_module
(
'threading'
,
_warnings
=
_warnings
)
gevent_threading_mod
,
_
=
_patch_module
(
'threading'
,
_warnings
=
_warnings
,
_notify_did_subscribers
=
False
)
if
Event
:
from
gevent.event
import
Event
...
...
@@ -490,7 +647,11 @@ def patch_thread(threading=True, _threading_local=True, Event=True, logging=True
"threading.main_thread().join() will hang from a greenlet"
,
_warnings
)
from
gevent
import
events
_notify_patch
(
events
.
GeventDidPatchModuleEvent
(
'thread'
,
gevent_thread_mod
,
thread_mod
))
_notify_patch
(
events
.
GeventDidPatchModuleEvent
(
'threading'
,
gevent_threading_mod
,
threading_mod
))
@
_ignores_DoNotPatch
def
patch_socket
(
dns
=
True
,
aggressive
=
True
):
"""
Replace the standard socket object with gevent's cooperative
...
...
@@ -509,12 +670,12 @@ def patch_socket(dns=True, aggressive=True):
items
=
socket
.
__implements__
# pylint:disable=no-member
else
:
items
=
set
(
socket
.
__implements__
)
-
set
(
socket
.
__dns__
)
# pylint:disable=no-member
patch_module
(
'socket'
,
items
=
items
)
_
patch_module
(
'socket'
,
items
=
items
)
if
aggressive
:
if
'ssl'
not
in
socket
.
__implements__
:
# pylint:disable=no-member
remove_item
(
socket
,
'ssl'
)
@
_ignores_DoNotPatch
def
patch_dns
():
"""
Replace :doc:`DNS functions <dns>` in :mod:`socket` with
...
...
@@ -524,9 +685,9 @@ def patch_dns():
done automatically by that method if requested.
"""
from
gevent
import
socket
patch_module
(
'socket'
,
items
=
socket
.
__dns__
)
# pylint:disable=no-member
_patch_module
(
'socket'
,
items
=
socket
.
__dns__
)
# pylint:disable=no-member
@
_ignores_DoNotPatch
def
patch_ssl
(
_warnings
=
None
,
_first_time
=
True
):
"""
patch_ssl() -> None
...
...
@@ -546,9 +707,9 @@ def patch_ssl(_warnings=None, _first_time=True):
'Please monkey-patch earlier. '
'See https://github.com/gevent/gevent/issues/1016'
,
_warnings
)
patch_module
(
'ssl'
,
_warnings
=
_warnings
)
_patch_module
(
'ssl'
,
_warnings
=
_warnings
)
@
_ignores_DoNotPatch
def
patch_select
(
aggressive
=
True
):
"""
Replace :func:`select.select` with :func:`gevent.select.select`
...
...
@@ -567,9 +728,9 @@ def patch_select(aggressive=True):
- :class:`selectors.DevpollSelector` (Python 3.5+)
"""
patch_module
(
'select'
)
source_mod
,
target_mod
=
_patch_module
(
'select'
,
_notify_did_subscribers
=
False
)
if
aggressive
:
select
=
__import__
(
'select'
)
select
=
target_mod
# since these are blocking we're removing them here. This makes some other
# modules (e.g. asyncore) non-blocking, as they use select that we provide
# when none of these are available.
...
...
@@ -587,7 +748,7 @@ def patch_select(aggressive=True):
# Note that this obviously only happens if selectors was imported after we had patched
# select; but there is a code path that leads to it being imported first (but now we've
# patched select---so we can't compare them identically)
select
=
__import__
(
'select'
)
# Should be gevent-patched now
select
=
target_mod
# Should be gevent-patched now
orig_select_select
=
get_original
(
'select'
,
'select'
)
assert
select
.
select
is
not
orig_select_select
selectors
=
__import__
(
'selectors'
)
...
...
@@ -614,7 +775,10 @@ def patch_select(aggressive=True):
remove_item
(
selectors
,
'DevpollSelector'
)
selectors
.
DefaultSelector
=
selectors
.
SelectSelector
from
gevent
import
events
_notify_patch
(
events
.
GeventDidPatchModuleEvent
(
'select'
,
source_mod
,
target_mod
))
@
_ignores_DoNotPatch
def
patch_subprocess
():
"""
Replace :func:`subprocess.call`, :func:`subprocess.check_call`,
...
...
@@ -626,9 +790,9 @@ def patch_subprocess():
the standard library.
"""
patch_module
(
'subprocess'
)
_patch_module
(
'subprocess'
)
@
_ignores_DoNotPatch
def
patch_builtins
():
"""
Make the builtin :func:`__import__` function `greenlet safe`_ under Python 2.
...
...
@@ -641,9 +805,9 @@ def patch_builtins():
"""
if
sys
.
version_info
[:
2
]
<
(
3
,
3
):
patch_module
(
'builtins'
)
_patch_module
(
'builtins'
)
@
_ignores_DoNotPatch
def
patch_signal
():
"""
Make the :func:`signal.signal` function work with a :func:`monkey-patched os <patch_os>`.
...
...
@@ -656,7 +820,7 @@ def patch_signal():
.. seealso:: :mod:`gevent.signal`
"""
patch_module
(
"signal"
)
_
patch_module
(
"signal"
)
def
_check_repatching
(
**
module_settings
):
...
...
@@ -669,16 +833,29 @@ def _check_repatching(**module_settings):
first_time
=
key
not
in
saved
saved
[
key
]
=
module_settings
return
_warnings
,
first_time
return
_warnings
,
first_time
,
module_settings
def
_subscribe_signal_os
(
will_patch_all
):
if
will_patch_all
.
will_patch_module
(
'signal'
)
and
not
will_patch_all
.
will_patch_module
(
'os'
):
warnings
=
will_patch_all
.
_warnings
# Internal
_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
)
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
,
# Deprecated, to be removed.
subprocess
=
True
,
sys
=
False
,
aggressive
=
True
,
Event
=
True
,
builtins
=
True
,
signal
=
True
):
"""
Do all of the default monkey patching (calls every other applicable
function in this module).
:return: A true value if patching all modules wasn't cancelled, a false
value if it was.
.. 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
...
...
@@ -689,16 +866,24 @@ def patch_all(socket=True, dns=True, time=True, select=True, thread=True, os=Tru
be an error in the future.
.. versionchanged:: 1.3a2
``Event`` defaults to True.
.. versionchanged:: 1.3b1
Defined the return values.
"""
# pylint:disable=too-many-locals,too-many-branches
# Check to see if they're changing the patched list
_warnings
,
first_time
=
_check_repatching
(
**
locals
())
_warnings
,
first_time
,
modules_to_patch
=
_check_repatching
(
**
locals
())
if
not
_warnings
and
not
first_time
:
# Nothing to do, identical args to what we just
# did
return
from
gevent
import
events
try
:
_notify_patch
(
events
.
GeventWillPatchAllEvent
(
modules_to_patch
),
_warnings
)
except
events
.
DoNotPatch
:
return
False
# order is important
if
os
:
patch_os
()
...
...
@@ -723,15 +908,13 @@ def patch_all(socket=True, dns=True, time=True, select=True, thread=True, os=Tru
if
builtins
:
patch_builtins
()
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
()
_notify_patch
(
events
.
GeventDidPatchBuiltinModulesEvent
(
modules_to_patch
),
_warnings
)
_notify_patch
(
events
.
GeventDidPatchAllEvent
(),
_warnings
)
_process_warnings
(
_warnings
)
return
True
def
main
():
...
...
@@ -789,7 +972,7 @@ def _get_script_help():
modules
=
[
x
for
x
in
patch_all_args
if
'patch_'
+
x
in
globals
()]
script_help
=
"""gevent.monkey - monkey patch the standard modules to use gevent.
USAGE:
python -m gevent.monkey [MONKEY OPTIONS] script [SCRIPT OPTIONS]
USAGE:
``python -m gevent.monkey [MONKEY OPTIONS] script [SCRIPT OPTIONS]``
If no OPTIONS present, monkey patches all the modules it can patch.
You can exclude a module with --no-module, e.g. --no-thread. You can
...
...
@@ -802,7 +985,7 @@ case only the modules specified on the command line will be patched.
Previously it had to be the path to
a .py source file.
MONKEY OPTIONS:
--verbose %s
"""
%
', '
.
join
(
'--[no-]%s'
%
m
for
m
in
modules
)
MONKEY OPTIONS:
``--verbose %s``
"""
%
', '
.
join
(
'--[no-]%s'
%
m
for
m
in
modules
)
return
script_help
,
patch_all_args
,
modules
main
.
__doc__
=
_get_script_help
()[
0
]
...
...
src/greentest/greentest/testcase.py
View file @
57b465b3
...
...
@@ -120,7 +120,22 @@ class TestCaseMetaClass(type):
def
_noop
():
return
class
TestCase
(
TestCaseMetaClass
(
"NewBase"
,
(
TimeAssertMixin
,
BaseTestCase
,),
{})):
class
SubscriberCleanupMixin
(
object
):
def
setUp
(
self
):
super
(
SubscriberCleanupMixin
,
self
).
setUp
()
from
gevent
import
events
self
.
__old_subscribers
=
events
.
subscribers
[:]
def
tearDown
(
self
):
from
gevent
import
events
events
.
subscribers
[:]
=
self
.
__old_subscribers
super
(
SubscriberCleanupMixin
,
self
).
tearDown
()
class
TestCase
(
TestCaseMetaClass
(
"NewBase"
,
(
SubscriberCleanupMixin
,
TimeAssertMixin
,
BaseTestCase
,),
{})):
__timeout__
=
params
.
LOCAL_TIMEOUT
if
not
sysinfo
.
RUNNING_ON_CI
else
params
.
CI_TIMEOUT
switch_expected
=
'default'
...
...
@@ -137,8 +152,6 @@ class TestCase(TestCaseMetaClass("NewBase", (TimeAssertMixin, BaseTestCase,), {}
def
setUp
(
self
):
super
(
TestCase
,
self
).
setUp
()
from
gevent
import
events
self
.
__old_subscribers
=
events
.
subscribers
[:]
# Especially if we're running in leakcheck mode, where
# the same test gets executed repeatedly, we need to update the
# current time. Tests don't always go through the full event loop,
...
...
@@ -151,8 +164,6 @@ class TestCase(TestCaseMetaClass("NewBase", (TimeAssertMixin, BaseTestCase,), {}
def
tearDown
(
self
):
if
getattr
(
self
,
'skipTearDown'
,
False
):
return
from
gevent
import
events
events
.
subscribers
[:]
=
self
.
__old_subscribers
cleanup
=
getattr
(
self
,
'cleanup'
,
_noop
)
cleanup
()
...
...
src/greentest/test__core_timer.py
View file @
57b465b3
...
...
@@ -13,6 +13,7 @@ class Test(TestCase):
repeat
=
0
def
setUp
(
self
):
super
(
Test
,
self
).
setUp
()
self
.
called
=
[]
self
.
loop
=
config
.
loop
(
default
=
False
)
self
.
timer
=
self
.
loop
.
timer
(
0.001
,
repeat
=
self
.
repeat
)
...
...
src/greentest/test__issue330.py
View file @
57b465b3
...
...
@@ -10,6 +10,7 @@ class MyException(Exception):
class
TestSwitch
(
greentest
.
TestCase
):
def
setUp
(
self
):
super
(
TestSwitch
,
self
).
setUp
()
self
.
switched_to
=
[
False
,
False
]
self
.
caught
=
None
...
...
src/greentest/test__issue6.py
View file @
57b465b3
...
...
@@ -14,7 +14,7 @@ if not sys.argv[1:]:
# If warnings are enabled, Python 3 has started producing this:
# '...importlib/_bootstrap.py:219: ImportWarning: can't resolve package from __spec__
# or __package__, falling back on __name__ and __path__\n return f(*args, **kwds)\n'
assert
err
==
b''
or
b'sys.excepthook'
in
err
or
b'
Import
Warning'
in
err
,
(
out
,
err
,
code
)
assert
err
==
b''
or
b'sys.excepthook'
in
err
or
b'Warning'
in
err
,
(
out
,
err
,
code
)
elif
sys
.
argv
[
1
:]
==
[
'subprocess'
]:
import
gevent
...
...
src/greentest/test__monkey.py
View file @
57b465b3
...
...
@@ -5,8 +5,9 @@ monkey.patch_all()
import
sys
import
unittest
from
greentest.testcase
import
SubscriberCleanupMixin
class
TestMonkey
(
unittest
.
TestCase
):
class
TestMonkey
(
SubscriberCleanupMixin
,
unittest
.
TestCase
):
maxDiff
=
None
...
...
@@ -48,9 +49,9 @@ class TestMonkey(unittest.TestCase):
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
)
self
.
assertNotIn
(
'built-in'
,
repr
(
attr
)
)
self
.
assertNotIsInstance
(
attr
,
types
.
BuiltinFunctionType
)
self
.
assertIsInstance
(
attr
,
types
.
FunctionType
)
self
.
assertIs
(
attr
,
getattr
(
gos
,
name
))
def
test_saved
(
self
):
...
...
@@ -67,13 +68,23 @@ class TestMonkey(unittest.TestCase):
monkey
.
patch_subprocess
()
self
.
assertIs
(
Popen
,
monkey
.
get_original
(
'subprocess'
,
'Popen'
))
def
test_patch_twice
(
self
):
def
test_patch_twice
_warnings_events
(
self
):
import
warnings
orig_saved
=
{}
for
k
,
v
in
monkey
.
saved
.
items
():
orig_saved
[
k
]
=
v
.
copy
()
from
gevent
import
events
all_events
=
[]
events
.
subscribers
.
append
(
all_events
.
append
)
def
veto
(
event
):
if
isinstance
(
event
,
events
.
GeventWillPatchModuleEvent
)
and
event
.
module_name
==
'ssl'
:
raise
events
.
DoNotPatch
events
.
subscribers
.
append
(
veto
)
with
warnings
.
catch_warnings
(
record
=
True
)
as
issued_warnings
:
# Patch again, triggering three warnings, one for os=False/signal=True,
# one for repeated monkey-patching, one for patching after ssl (on python >= 2.7.9)
...
...
@@ -102,6 +113,16 @@ class TestMonkey(unittest.TestCase):
for
k
,
v
in
monkey
.
saved
[
'threading'
].
items
():
self
.
assertNotIn
(
'gevent'
,
str
(
v
))
self
.
assertIsInstance
(
all_events
[
0
],
events
.
GeventWillPatchAllEvent
)
self
.
assertIsInstance
(
all_events
[
1
],
events
.
GeventWillPatchModuleEvent
)
self
.
assertIsInstance
(
all_events
[
2
],
events
.
GeventDidPatchModuleEvent
)
self
.
assertIsInstance
(
all_events
[
-
2
],
events
.
GeventDidPatchBuiltinModulesEvent
)
self
.
assertIsInstance
(
all_events
[
-
1
],
events
.
GeventDidPatchAllEvent
)
for
e
in
all_events
:
self
.
assertFalse
(
isinstance
(
e
,
events
.
GeventDidPatchModuleEvent
)
and
e
.
module_name
==
'ssl'
)
if
__name__
==
'__main__'
:
unittest
.
main
()
src/greentest/test__monkey_scope.py
View file @
57b465b3
import
os
import
os.path
import
sys
import
unittest
...
...
@@ -6,10 +8,13 @@ from subprocess import Popen
from
subprocess
import
PIPE
class
TestRun
(
unittest
.
TestCase
):
maxDiff
=
None
def
_run
(
self
,
script
):
env
=
os
.
environ
.
copy
()
env
[
'PYTHONWARNINGS'
]
=
'ignore'
args
=
[
sys
.
executable
,
'-m'
,
'gevent.monkey'
,
script
,
'patched'
]
p
=
Popen
(
args
,
stdout
=
PIPE
,
stderr
=
PIPE
)
p
=
Popen
(
args
,
stdout
=
PIPE
,
stderr
=
PIPE
,
env
=
env
)
gout
,
gerr
=
p
.
communicate
()
self
.
assertEqual
(
0
,
p
.
returncode
,
(
gout
,
gerr
))
...
...
@@ -27,7 +32,6 @@ class TestRun(unittest.TestCase):
return
glines
,
gerr
def
test_run_simple
(
self
):
import
os.path
self
.
_run
(
os
.
path
.
join
(
'monkey_package'
,
'script.py'
))
def
test_run_package
(
self
):
...
...
@@ -38,7 +42,6 @@ class TestRun(unittest.TestCase):
self
.
assertEqual
(
lines
[
1
],
'__main__'
)
def
test_issue_302
(
self
):
import
os
lines
,
_
=
self
.
_run
(
os
.
path
.
join
(
'monkey_package'
,
'issue302monkey.py'
))
self
.
assertEqual
(
lines
[
0
],
'True'
)
...
...
src/greentest/test__monkey_sigchld_2.py
View file @
57b465b3
...
...
@@ -16,6 +16,12 @@ def handle(*_args):
os
.
waitpid
(
-
1
,
os
.
WNOHANG
)
# The signal watcher must be installed *before* monkey patching
if
hasattr
(
signal
,
'SIGCHLD'
):
# On Python 2, the signal handler breaks the platform
# module, because it uses os.popen. pkg_resources uses the platform
# module.
# Cache that info.
import
platform
platform
.
uname
()
signal
.
signal
(
signal
.
SIGCHLD
,
handle
)
pid
=
os
.
fork
()
...
...
@@ -28,15 +34,15 @@ if hasattr(signal, 'SIGCHLD'):
_
,
stat
=
os
.
waitpid
(
pid
,
0
)
assert
stat
==
0
,
stat
else
:
import
gevent.monkey
gevent
.
monkey
.
patch_all
()
signal
.
signal
(
signal
.
SIGCHLD
,
signal
.
SIG_DFL
)
# Under Python 2, os.popen() directly uses the popen call, and
# popen's file uses the pclose() system call to
# wait for the child. If it's already waited on,
# it raises the same exception.
# Python 3 uses the subprocess module directly which doesn't
# have this problem.
import
gevent.monkey
gevent
.
monkey
.
patch_all
()
signal
.
signal
(
signal
.
SIGCHLD
,
signal
.
SIG_DFL
)
f
=
os
.
popen
(
'true'
)
f
.
close
()
...
...
src/greentest/test__subprocess.py
View file @
57b465b3
...
...
@@ -33,6 +33,7 @@ python_universal_newlines_broken = PY3 and subprocess.mswindows
class
Test
(
greentest
.
TestCase
):
def
setUp
(
self
):
super
(
Test
,
self
).
setUp
()
gc
.
collect
()
gc
.
collect
()
...
...
src/greentest/test_threading_2.py
View file @
57b465b3
...
...
@@ -366,7 +366,10 @@ class ThreadTests(unittest.TestCase):
stderr
=
stderr
.
decode
(
'utf-8'
)
assert
re
.
match
(
'^Woke up, sleep function is: <.*?sleep.*?>$'
,
stdout
),
repr
(
stdout
)
stderr
=
re
.
sub
(
r"^\
[
\d+ refs\
]
", "", stderr, re.MULTILINE).strip()
self.assertEqual(stderr, "")
# On Python 2, importing pkg_resources tends to result in some 'ImportWarning'
# being printed to stderr about packages missing __init__.py; the -W ignore is...
# ignored.
# self.assertEqual(stderr, "")
def test_enumerate_after_join(self):
# Try hard to trigger #1703448: a thread is still returned in
...
...
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