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
6fffdb4c
Commit
6fffdb4c
authored
Feb 06, 2018
by
Jason Madden
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
https://github.com/gevent/gevent
parents
55301ab2
ee227a40
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
401 additions
and
130 deletions
+401
-130
CHANGES.rst
CHANGES.rst
+4
-0
Makefile
Makefile
+1
-1
doc/conf.py
doc/conf.py
+1
-22
doc/gevent.rst
doc/gevent.rst
+1
-0
doc/reference.rst
doc/reference.rst
+1
-0
src/gevent/_patcher.py
src/gevent/_patcher.py
+125
-0
src/gevent/_socket2.py
src/gevent/_socket2.py
+2
-0
src/gevent/_util.py
src/gevent/_util.py
+10
-9
src/gevent/builtins.py
src/gevent/builtins.py
+10
-3
src/gevent/monkey.py
src/gevent/monkey.py
+1
-3
src/gevent/pywsgi.py
src/gevent/pywsgi.py
+3
-1
src/gevent/queue.py
src/gevent/queue.py
+4
-1
src/gevent/resolver/dnspython.py
src/gevent/resolver/dnspython.py
+58
-18
src/gevent/signal.py
src/gevent/signal.py
+1
-1
src/gevent/subprocess.py
src/gevent/subprocess.py
+7
-2
src/gevent/time.py
src/gevent/time.py
+27
-0
src/gevent/timeout.py
src/gevent/timeout.py
+116
-38
src/greentest/.coveragerc
src/greentest/.coveragerc
+3
-0
src/greentest/known_failures.py
src/greentest/known_failures.py
+7
-0
src/greentest/test__all__.py
src/greentest/test__all__.py
+18
-26
src/greentest/test__example_udp_client.py
src/greentest/test__example_udp_client.py
+1
-1
src/greentest/test__socket_dns.py
src/greentest/test__socket_dns.py
+0
-4
No files found.
CHANGES.rst
View file @
6fffdb4c
...
@@ -42,6 +42,10 @@
...
@@ -42,6 +42,10 @@
``AttributeError``, now it once again raises the correct
``AttributeError``, now it once again raises the correct
``socket.error``. Reported in :issue:`1089` by André Cimander.
``socket.error``. Reported in :issue:`1089` by André Cimander.
- Add the module :mod:`gevent.time` that can be imported instead of
:mod:`time`, much like :mod:`gevent.socket` can be imported instead
of :mod:`socket`.
1.3a1 (2018-01-27)
1.3a1 (2018-01-27)
==================
==================
...
...
Makefile
View file @
6fffdb4c
...
@@ -67,7 +67,7 @@ alltest: basictest
...
@@ -67,7 +67,7 @@ alltest: basictest
cd
src/greentest
&&
GEVENT_RESOLVER
=
ares
GEVENTARES_SERVERS
=
8.8.8.8
${PYTHON}
testrunner.py
--config
known_failures.py
--ignore
tests_that_dont_use_resolver.txt
--quiet
cd
src/greentest
&&
GEVENT_RESOLVER
=
ares
GEVENTARES_SERVERS
=
8.8.8.8
${PYTHON}
testrunner.py
--config
known_failures.py
--ignore
tests_that_dont_use_resolver.txt
--quiet
${PYTHON}
scripts/travis.py fold_end ares
${PYTHON}
scripts/travis.py fold_end ares
${PYTHON}
scripts/travis.py fold_start dnspython
"Running dnspython tests"
${PYTHON}
scripts/travis.py fold_start dnspython
"Running dnspython tests"
cd
src/greentest
&&
GEVENT_RESOLVER
=
dnspython
${PYTHON}
testrunner.py
--config
known_failures.py
--ignore
tests_that_dont_use_resolver.txt
,tests_that_dont_monkeypatch.txt
--quiet
cd
src/greentest
&&
GEVENT_RESOLVER
=
dnspython
${PYTHON}
testrunner.py
--config
known_failures.py
--ignore
tests_that_dont_use_resolver.txt
--quiet
${PYTHON}
scripts/travis.py fold_end dnspython
${PYTHON}
scripts/travis.py fold_end dnspython
# In the past, we included all test files that had a reference to 'subprocess'' somewhere in their
# In the past, we included all test files that had a reference to 'subprocess'' somewhere in their
# text. The monkey-patched stdlib tests were specifically included here.
# text. The monkey-patched stdlib tests were specifically included here.
...
...
doc/conf.py
View file @
6fffdb4c
...
@@ -40,6 +40,7 @@ extlinks = {'issue': ('https://github.com/gevent/gevent/issues/%s',
...
@@ -40,6 +40,7 @@ extlinks = {'issue': ('https://github.com/gevent/gevent/issues/%s',
'pull request #'
)}
'pull request #'
)}
autodoc_default_flags
=
[
'members'
,
'show-inheritance'
]
autodoc_default_flags
=
[
'members'
,
'show-inheritance'
]
autodoc_member_order
=
'bysource'
autoclass_content
=
'both'
autoclass_content
=
'both'
# Add any paths that contain templates here, relative to this directory.
# Add any paths that contain templates here, relative to this directory.
...
@@ -226,25 +227,3 @@ del gevent.Greenlet.throw
...
@@ -226,25 +227,3 @@ del gevent.Greenlet.throw
for
item
in
gevent
.
socket
.
__all__
[:]:
for
item
in
gevent
.
socket
.
__all__
[:]:
if
getattr
(
gevent
.
socket
,
item
)
is
getattr
(
socket
,
item
,
None
):
if
getattr
(
gevent
.
socket
,
item
)
is
getattr
(
socket
,
item
,
None
):
gevent
.
socket
.
__all__
.
remove
(
item
)
gevent
.
socket
.
__all__
.
remove
(
item
)
# order the methods in the class documentation the same way they are ordered in the source code
from
sphinx.ext
import
autodoc
from
sphinx.ext.autodoc
import
ClassDocumenter
class
MyClassDocumenter
(
ClassDocumenter
):
def
get_object_members
(
self
,
want_all
):
members_check_module
,
members
=
super
(
MyClassDocumenter
,
self
).
get_object_members
(
want_all
)
def
key
((
name
,
obj
)):
try
:
return
obj
.
im_func
.
func_code
.
co_firstlineno
except
AttributeError
:
return
0
members
.
sort
(
key
=
key
)
return
members_check_module
,
members
autodoc
.
ClassDocumenter
=
MyClassDocumenter
doc/gevent.rst
View file @
6fffdb4c
...
@@ -188,5 +188,6 @@ Timeouts
...
@@ -188,5 +188,6 @@ Timeouts
.. autoclass:: Timeout
.. autoclass:: Timeout
:members:
:members:
:undoc-members:
:undoc-members:
:special-members: __enter__, __exit__
.. autofunction:: with_timeout
.. autofunction:: with_timeout
doc/reference.rst
View file @
6fffdb4c
...
@@ -21,5 +21,6 @@ API reference
...
@@ -21,5 +21,6 @@ API reference
gevent.thread
gevent.thread
gevent.threading
gevent.threading
gevent.threadpool
gevent.threadpool
gevent.time
gevent.util
gevent.util
lowlevel
lowlevel
src/gevent/_patcher.py
0 → 100644
View file @
6fffdb4c
# Copyright 2018 gevent. See LICENSE for details.
# Portions of the following are inspired by code from eventlet. I
# believe they are distinct enough that no eventlet copyright would
# apply (they are not a copy or substantial portion of the eventlot
# code).
# Added in gevent 1.3a2. Not public in that release.
from
__future__
import
absolute_import
,
print_function
import
imp
import
importlib
import
sys
from
gevent._compat
import
PY3
from
gevent._compat
import
iteritems
from
gevent.builtins
import
__import__
as
_import
MAPPING
=
{
'gevent.local'
:
'_threading_local'
,
'gevent.socket'
:
'socket'
,
'gevent.select'
:
'select'
,
'gevent.ssl'
:
'ssl'
,
'gevent.thread'
:
'_thread'
if
PY3
else
'thread'
,
'gevent.subprocess'
:
'subprocess'
,
'gevent.os'
:
'os'
,
'gevent.threading'
:
'threading'
,
'gevent.builtins'
:
'builtins'
if
PY3
else
'__builtin__'
,
'gevent.signal'
:
'signal'
,
'gevent.time'
:
'time'
,
'gevent.queue'
:
'queue'
if
PY3
else
'Queue'
,
}
_PATCH_PREFIX
=
'__g_patched_module_'
class
_SysModulesPatcher
(
object
):
def
__init__
(
self
,
importing
):
self
.
_saved
=
{}
self
.
importing
=
importing
self
.
green_modules
=
{
stdlib_name
:
importlib
.
import_module
(
gevent_name
)
for
gevent_name
,
stdlib_name
in
iteritems
(
MAPPING
)
}
self
.
orig_imported
=
frozenset
(
sys
.
modules
)
def
_save
(
self
):
for
modname
in
self
.
green_modules
:
self
.
_saved
[
modname
]
=
sys
.
modules
.
get
(
modname
,
None
)
self
.
_saved
[
self
.
importing
]
=
sys
.
modules
.
get
(
self
.
importing
,
None
)
# Anything we've already patched regains its original name during this
# process
for
mod_name
,
mod
in
iteritems
(
sys
.
modules
):
if
mod_name
.
startswith
(
_PATCH_PREFIX
):
orig_mod_name
=
mod_name
[
len
(
_PATCH_PREFIX
):]
self
.
_saved
[
mod_name
]
=
sys
.
modules
.
get
(
orig_mod_name
,
None
)
self
.
green_modules
[
orig_mod_name
]
=
mod
def
_replace
(
self
):
# Cover the target modules so that when you import the module it
# sees only the patched versions
for
name
,
mod
in
iteritems
(
self
.
green_modules
):
sys
.
modules
[
name
]
=
mod
def
_restore
(
self
):
for
modname
,
mod
in
iteritems
(
self
.
_saved
):
if
mod
is
not
None
:
sys
.
modules
[
modname
]
=
mod
else
:
try
:
del
sys
.
modules
[
modname
]
except
KeyError
:
pass
# Anything from the same package tree we imported this time
# needs to be saved so we can restore it later, and so it doesn't
# leak into the namespace.
pkg_prefix
=
self
.
importing
.
split
(
'.'
,
1
)[
0
]
for
modname
,
mod
in
list
(
iteritems
(
sys
.
modules
)):
if
(
modname
not
in
self
.
orig_imported
and
modname
!=
self
.
importing
and
not
modname
.
startswith
(
_PATCH_PREFIX
)
and
modname
.
startswith
(
pkg_prefix
)):
sys
.
modules
[
_PATCH_PREFIX
+
modname
]
=
mod
del
sys
.
modules
[
modname
]
def
__exit__
(
self
,
t
,
v
,
tb
):
try
:
self
.
_restore
()
finally
:
imp
.
release_lock
()
def
__enter__
(
self
):
imp
.
acquire_lock
()
self
.
_save
()
self
.
_replace
()
def
import_patched
(
module_name
):
"""
Import *module_name* with gevent monkey-patches active,
and return the greened module.
Any sub-modules that were imported by the package are also
saved.
"""
patched_name
=
_PATCH_PREFIX
+
module_name
if
patched_name
in
sys
.
modules
:
return
sys
.
modules
[
patched_name
]
# Save the current module state, and restore on exit,
# capturing desirable changes in the modules package.
with
_SysModulesPatcher
(
module_name
):
sys
.
modules
.
pop
(
module_name
,
None
)
module
=
_import
(
module_name
,
{},
{},
module_name
.
split
(
'.'
)[:
-
1
])
sys
.
modules
[
patched_name
]
=
module
return
module
src/gevent/_socket2.py
View file @
6fffdb4c
...
@@ -2,6 +2,8 @@
...
@@ -2,6 +2,8 @@
"""
"""
Python 2 socket module.
Python 2 socket module.
"""
"""
from
__future__
import
absolute_import
# Our import magic sadly makes this warning useless
# Our import magic sadly makes this warning useless
# pylint: disable=undefined-variable
# pylint: disable=undefined-variable
...
...
src/gevent/_util.py
View file @
6fffdb4c
...
@@ -29,21 +29,22 @@ def copy_globals(source,
...
@@ -29,21 +29,22 @@ def copy_globals(source,
dunder_names_to_keep
=
(
'__implements__'
,
'__all__'
,
'__imports__'
),
dunder_names_to_keep
=
(
'__implements__'
,
'__all__'
,
'__imports__'
),
cleanup_globs
=
True
):
cleanup_globs
=
True
):
"""
"""
Copy attributes defined in `
source.__dict__` to the dictionary in globs
Copy attributes defined in `
`source.__dict__`` to the dictionary
(which should be the caller's globals()
).
in globs (which should be the caller's :func:`globals`
).
Names that start with `
__
` are ignored (unless they are in
Names that start with `
`__`
` are ignored (unless they are in
*dunder_names_to_keep*). Anything found in *names_to_ignore* is
*dunder_names_to_keep*). Anything found in *names_to_ignore* is
also ignored.
also ignored.
If *only_names* is given, only those attributes will be considered.
If *only_names* is given, only those attributes will be
In this case, *ignore_missing_names* says whether or not to raise an AttributeError
considered. In this case, *ignore_missing_names* says whether or
if one of those names can't be found.
not to raise an :exc:`AttributeError` if one of those names can't
be found.
If
cleanup_globs has a true value, then common things imported but not used
If
*cleanup_globs* has a true value, then common things imported but
at runtime are removed, including this function.
not used
at runtime are removed, including this function.
Returns a list of the names copied
Returns a list of the names copied
; this should be assigned to ``__imports__``.
"""
"""
if
only_names
:
if
only_names
:
if
ignore_missing_names
:
if
ignore_missing_names
:
...
...
src/gevent/builtins.py
View file @
6fffdb4c
...
@@ -14,15 +14,15 @@ from gevent.lock import RLock
...
@@ -14,15 +14,15 @@ from gevent.lock import RLock
# So we test for the old, deprecated version first
# So we test for the old, deprecated version first
try
:
# Py2
try
:
# Py2
import
__builtin__
as
builtins
import
__builtin__
as
__gbuiltins__
_allowed_module_name_types
=
(
basestring
,)
# pylint:disable=undefined-variable
_allowed_module_name_types
=
(
basestring
,)
# pylint:disable=undefined-variable
__target__
=
'__builtin__'
__target__
=
'__builtin__'
except
ImportError
:
except
ImportError
:
import
builtins
# pylint: disable=import-error
import
builtins
as
__gbuiltins__
# pylint: disable=import-error
_allowed_module_name_types
=
(
str
,)
_allowed_module_name_types
=
(
str
,)
__target__
=
'builtins'
__target__
=
'builtins'
_import
=
builtins
.
__import__
_import
=
__gbuiltins__
.
__import__
# We need to protect imports both across threads and across greenlets.
# We need to protect imports both across threads and across greenlets.
# And the order matters. Note that under 3.4, the global import lock
# And the order matters. Note that under 3.4, the global import lock
...
@@ -120,6 +120,13 @@ def _lock_imports():
...
@@ -120,6 +120,13 @@ def _lock_imports():
if
sys
.
version_info
[:
2
]
>=
(
3
,
3
):
if
sys
.
version_info
[:
2
]
>=
(
3
,
3
):
__implements__
=
[]
__implements__
=
[]
__import__
=
_import
else
:
else
:
__implements__
=
[
'__import__'
]
__implements__
=
[
'__import__'
]
__all__
=
__implements__
__all__
=
__implements__
from
gevent._util
import
copy_globals
__imports__
=
copy_globals
(
__gbuiltins__
,
globals
(),
names_to_ignore
=
__implements__
)
src/gevent/monkey.py
View file @
6fffdb4c
...
@@ -250,9 +250,7 @@ def patch_os():
...
@@ -250,9 +250,7 @@ def patch_os():
def
patch_time
():
def
patch_time
():
"""Replace :func:`time.sleep` with :func:`gevent.sleep`."""
"""Replace :func:`time.sleep` with :func:`gevent.sleep`."""
from
gevent.hub
import
sleep
patch_module
(
'time'
)
import
time
patch_item
(
time
,
'sleep'
,
sleep
)
def
_patch_existing_locks
(
threading
):
def
_patch_existing_locks
(
threading
):
...
...
src/gevent/pywsgi.py
View file @
6fffdb4c
# Copyright (c) 2005-2009, eventlet contributors
# Copyright (c) 2005-2009, eventlet contributors
# Copyright (c) 2009-201
5
, gevent contributors
# Copyright (c) 2009-201
8
, gevent contributors
"""
"""
A pure-Python, gevent-friendly WSGI server.
A pure-Python, gevent-friendly WSGI server.
...
@@ -9,6 +9,8 @@ created for each request. The server can be customized to use
...
@@ -9,6 +9,8 @@ created for each request. The server can be customized to use
different subclasses of :class:`WSGIHandler`.
different subclasses of :class:`WSGIHandler`.
"""
"""
from
__future__
import
absolute_import
# FIXME: Can we refactor to make smallor?
# FIXME: Can we refactor to make smallor?
# pylint:disable=too-many-lines
# pylint:disable=too-many-lines
...
...
src/gevent/queue.py
View file @
6fffdb4c
...
@@ -42,7 +42,10 @@ from gevent.hub import get_hub, Waiter, getcurrent
...
@@ -42,7 +42,10 @@ from gevent.hub import get_hub, Waiter, getcurrent
from
gevent.hub
import
InvalidSwitchError
from
gevent.hub
import
InvalidSwitchError
__all__
=
[
'Empty'
,
'Full'
,
'Queue'
,
'PriorityQueue'
,
'LifoQueue'
,
'JoinableQueue'
,
'Channel'
]
__implements__
=
[
'Queue'
,
'PriorityQueue'
,
'LifoQueue'
]
__extensions__
=
[
'JoinableQueue'
,
'Channel'
]
__imports__
=
[
'Empty'
,
'Full'
]
__all__
=
__implements__
+
__extensions__
+
__imports__
def
_safe_remove
(
deq
,
item
):
def
_safe_remove
(
deq
,
item
):
...
...
src/gevent/resolver/dnspython.py
View file @
6fffdb4c
# Copyright (c) 2018 gevent contributors. See LICENSE for details.
# Copyright (c) 2018 gevent contributors. See LICENSE for details.
import
_socket
import
_socket
from
_socket
import
AI_NUMERICHOST
from
_socket
import
AI_NUMERICHOST
from
_socket
import
error
from
_socket
import
error
...
@@ -9,14 +10,43 @@ import socket
...
@@ -9,14 +10,43 @@ import socket
from
.
import
AbstractResolver
from
.
import
AbstractResolver
from
dns
import
resolver
from
gevent._patcher
import
import_patched
import
dns
__all__
=
[
__all__
=
[
'Resolver'
,
'Resolver'
,
]
]
# Import the DNS packages to use the gevent modules,
# even if the system is not monkey-patched.
def
_patch_dns
():
top
=
import_patched
(
'dns'
)
for
pkg
in
(
'dns'
,
'dns.rdtypes'
,
'dns.rdtypes.IN'
,
'dns.rdtypes.ANY'
):
mod
=
import_patched
(
pkg
)
for
name
in
mod
.
__all__
:
setattr
(
mod
,
name
,
import_patched
(
pkg
+
'.'
+
name
))
return
top
dns
=
_patch_dns
()
def
_dns_import_patched
(
name
):
assert
name
.
startswith
(
'dns'
)
import_patched
(
name
)
return
dns
# This module tries to dynamically import classes
# using __import__, and it's important that they match
# the ones we just created, otherwise exceptions won't be caught
# as expected. It uses a one-arg __import__ statement and then
# tries to walk down the sub-modules using getattr, so we can't
# directly use import_patched as-is.
dns
.
rdata
.
__import__
=
_dns_import_patched
resolver
=
dns
.
resolver
# This is a copy of resolver._getaddrinfo with the crucial change that it
# This is a copy of resolver._getaddrinfo with the crucial change that it
# doesn't have a bare except:, because that breaks Timeout and KeyboardInterrupt
# doesn't have a bare except:, because that breaks Timeout and KeyboardInterrupt
# See https://github.com/rthalley/dnspython/pull/300
# See https://github.com/rthalley/dnspython/pull/300
...
@@ -123,6 +153,8 @@ def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0,
...
@@ -123,6 +153,8 @@ def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0,
return
tuples
return
tuples
resolver
.
_getaddrinfo
=
_getaddrinfo
class
Resolver
(
AbstractResolver
):
class
Resolver
(
AbstractResolver
):
"""
"""
An *experimental* resolver that uses `dnspython`_.
An *experimental* resolver that uses `dnspython`_.
...
@@ -136,17 +168,15 @@ class Resolver(AbstractResolver):
...
@@ -136,17 +168,15 @@ class Resolver(AbstractResolver):
resolver can resolve Unicode host names that the system resolver
resolver can resolve Unicode host names that the system resolver
cannot.
cannot.
This uses thread locks and sockets, so it only functions if the
.. note::
system has been monkey-patched. Otherwise it will raise a
``ValueError``.
This uses dnspython's default resolver object. This object has
This **does not** use dnspython's default resolver object, or share any
several useful attributes that can be used to adjust the behaviour
classes with ``import dns``. A separate copy of the objects is imported to
of the DNS system; in particular, the ``cache`` attribute could be
be able to function in a non monkey-patched process. The documentation for the resolver
set to an instance of :class:`dns.resolver.Cache` or
object still applies.
:class:`dns.resolver.LRUCache` (by default a ``LRUCache`` is
used), and ``nameservers`` controls which nameservers to talk to,
The resolver that we use is available as the :attr:`resolver` attribute
and ``lifetime`` configures a timeout for each individual query
.
of this object (typically ``gevent.get_hub().resolver.resolver``)
.
.. caution::
.. caution::
...
@@ -169,15 +199,25 @@ class Resolver(AbstractResolver):
...
@@ -169,15 +199,25 @@ class Resolver(AbstractResolver):
"""
"""
def
__init__
(
self
,
hub
=
None
):
# pylint: disable=unused-argument
def
__init__
(
self
,
hub
=
None
):
# pylint: disable=unused-argument
from
gevent
import
monkey
if
not
all
(
monkey
.
is_module_patched
(
m
)
for
m
in
[
'threading'
,
'socket'
,
'select'
]):
raise
ValueError
(
"Can only be used when monkey-patched"
)
if
resolver
.
_resolver
is
None
:
if
resolver
.
_resolver
is
None
:
resolver
.
_resolver
=
resolver
.
get_default_resolver
()
resolver
.
_resolver
=
resolver
.
get_default_resolver
()
# Add a default cache
# Add a default cache
resolver
.
_resolver
.
cache
=
resolver
.
LRUCache
()
resolver
.
_resolver
.
cache
=
resolver
.
LRUCache
()
if
resolver
.
_getaddrinfo
is
not
_getaddrinfo
:
resolver
.
_getaddrinfo
=
_getaddrinfo
@
property
def
resolver
(
self
):
"""
The dnspython resolver object we use.
This object has several useful attributes that can be used to
adjust the behaviour of the DNS system:
* ``cache`` is a :class:`dns.resolver.LRUCache`. Its maximum size
can be configured by calling :meth:`resolver.cache.set_max_size`
* ``nameservers`` controls which nameservers to talk to
* ``lifetime`` configures a timeout for each individual query.
"""
return
resolver
.
_resolver
def
close
(
self
):
def
close
(
self
):
pass
pass
...
...
src/gevent/signal.py
View file @
6fffdb4c
...
@@ -33,7 +33,7 @@ _signal_getsignal = _signal.getsignal
...
@@ -33,7 +33,7 @@ _signal_getsignal = _signal.getsignal
def
getsignal
(
signalnum
):
def
getsignal
(
signalnum
):
"""
"""
Exactly the same as :func:`signal.signal` except where
Exactly the same as :func:`signal.
get
signal` except where
:const:`signal.SIGCHLD` is concerned.
:const:`signal.SIGCHLD` is concerned.
For :const:`signal.SIGCHLD`, this cooperates with :func:`signal`
For :const:`signal.SIGCHLD`, this cooperates with :func:`signal`
...
...
src/gevent/subprocess.py
View file @
6fffdb4c
...
@@ -360,12 +360,17 @@ if 'TimeoutExpired' not in globals():
...
@@ -360,12 +360,17 @@ if 'TimeoutExpired' not in globals():
.. versionadded:: 1.2a1
.. versionadded:: 1.2a1
"""
"""
def
__init__
(
self
,
cmd
,
timeout
,
output
=
None
):
def
__init__
(
self
,
cmd
,
timeout
,
output
=
None
):
_Timeout
.
__init__
(
self
,
timeout
,
_use_timer
=
Fals
e
)
_Timeout
.
__init__
(
self
,
Non
e
)
self
.
cmd
=
cmd
self
.
cmd
=
cmd
self
.
timeout
=
timeout
self
.
seconds
=
timeout
self
.
output
=
output
self
.
output
=
output
@
property
def
timeout
(
self
):
return
self
.
seconds
def
__str__
(
self
):
def
__str__
(
self
):
return
(
"Command '%s' timed out after %s seconds"
%
return
(
"Command '%s' timed out after %s seconds"
%
(
self
.
cmd
,
self
.
timeout
))
(
self
.
cmd
,
self
.
timeout
))
...
...
src/gevent/time.py
0 → 100644
View file @
6fffdb4c
# Copyright (c) 2018 gevent. See LICENSE for details.
"""
The standard library :mod:`time` module, but :func:`sleep` is
gevent-aware.
.. versionadded:: 1.3a2
"""
from
__future__
import
absolute_import
__implements__
=
[
'sleep'
,
]
__all__
=
__implements__
import
time
as
__time__
from
gevent._util
import
copy_globals
__imports__
=
copy_globals
(
__time__
,
globals
(),
names_to_ignore
=
__implements__
)
from
gevent.hub
import
sleep
sleep
=
sleep
# pylint
src/gevent/timeout.py
View file @
6fffdb4c
...
@@ -17,8 +17,10 @@ from __future__ import absolute_import, print_function, division
...
@@ -17,8 +17,10 @@ from __future__ import absolute_import, print_function, division
from
gevent._compat
import
string_types
from
gevent._compat
import
string_types
from
gevent.hub
import
getcurrent
,
_NONE
,
get_hub
from
gevent.hub
import
getcurrent
,
_NONE
,
get_hub
__all__
=
[
'Timeout'
,
__all__
=
[
'with_timeout'
]
'Timeout'
,
'with_timeout'
,
]
class
_FakeTimer
(
object
):
class
_FakeTimer
(
object
):
...
@@ -26,8 +28,22 @@ class _FakeTimer(object):
...
@@ -26,8 +28,22 @@ class _FakeTimer(object):
# without allocating any native resources. This is useful for timeouts
# without allocating any native resources. This is useful for timeouts
# that will never expire.
# that will never expire.
# Also partially mimics the API of Timeout itself for use in _start_new_or_dummy
# Also partially mimics the API of Timeout itself for use in _start_new_or_dummy
pending
=
False
active
=
False
# This object is used as a singleton, so it should be
# immutable.
__slots__
=
()
@
property
def
pending
(
self
):
return
False
active
=
pending
@
property
def
seconds
(
self
):
return
None
timer
=
exception
=
seconds
def
start
(
self
,
*
args
,
**
kwargs
):
def
start
(
self
,
*
args
,
**
kwargs
):
# pylint:disable=unused-argument
# pylint:disable=unused-argument
...
@@ -51,23 +67,30 @@ _FakeTimer = _FakeTimer()
...
@@ -51,23 +67,30 @@ _FakeTimer = _FakeTimer()
class
Timeout
(
BaseException
):
class
Timeout
(
BaseException
):
"""
"""
Raise *exception* in the current greenlet after given time period::
Timeout(seconds=None, exception=None, ref=True, priority=-1)
Raise *exception* in the current greenlet after *seconds*
have elapsed::
timeout = Timeout(seconds, exception)
timeout = Timeout(seconds, exception)
timeout.start()
timeout.start()
try:
try:
... # exception will be raised here, after *seconds* passed since start() call
... # exception will be raised here, after *seconds* passed since start() call
finally:
finally:
timeout.c
ancel
()
timeout.c
lose
()
.. note:: If the code that the timeout was protecting finishes
.. note::
executing before the timeout elapses, be sure to ``cancel`` the
timeout so it is not unexpectedly raised in the future. Even if
it is raised, it is a best practice to cancel it. This
``try/finally`` construct or a ``with`` statement is a
recommended pattern.
When *exception* is omitted or ``None``, the :class:`Timeout` instance itself is raised:
If the code that the timeout was protecting finishes
executing before the timeout elapses, be sure to ``close`` the
timeout so it is not unexpectedly raised in the future. Even if it
is raised, it is a best practice to close it. This ``try/finally``
construct or a ``with`` statement is a recommended pattern. (If
the timeout object will be started again, use ``cancel`` instead
of ``close``; this is rare.)
When *exception* is omitted or ``None``, the ``Timeout`` instance
itself is raised::
>>> import gevent
>>> import gevent
>>> gevent.Timeout(0.1).start()
>>> gevent.Timeout(0.1).start()
...
@@ -76,14 +99,41 @@ class Timeout(BaseException):
...
@@ -76,14 +99,41 @@ class Timeout(BaseException):
...
...
Timeout: 0.1 seconds
Timeout: 0.1 seconds
To simplify starting and canceling timeouts, the ``with`` statement can be used::
If the *seconds* argument is not given or is ``None`` (e.g.,
``Timeout()``), then the timeout will never expire and never raise
*exception*. This is convenient for creating functions which take
an optional timeout parameter of their own. (Note that this is **not**
the same thing as a *seconds* value of ``0``.)
::
def function(args, timeout=None):
"A function with an optional timeout."
timer = Timeout(timeout)
with timer:
...
.. caution::
A *seconds* value less than ``0.0`` (e.g., ``-1``) is poorly defined. In the future,
support for negative values is likely to do the same thing as a value
of ``None`` or ``0``
A *seconds* value of ``0`` requests that the event loop spin and poll for I/O;
it will immediately expire as soon as control returns to the event loop.
.. rubric:: Use As A Context Manager
To simplify starting and canceling timeouts, the ``with``
statement can be used::
with gevent.Timeout(seconds, exception) as timeout:
with gevent.Timeout(seconds, exception) as timeout:
pass # ... code block ...
pass # ... code block ...
This is equivalent to the try/finally block above with one additional feature:
This is equivalent to the try/finally block above with one
if *exception* is the literal ``False``, the timeout is still raised, but the context manager
additional feature: if *exception* is the literal ``False``, the
suppresses it, so the code outside the with-block won't see it.
timeout is still raised, but the context manager suppresses it, so
the code outside the with-block won't see it.
This is handy for adding a timeout to the functions that don't
This is handy for adding a timeout to the functions that don't
support a *timeout* parameter themselves::
support a *timeout* parameter themselves::
...
@@ -96,9 +146,14 @@ class Timeout(BaseException):
...
@@ -96,9 +146,14 @@ class Timeout(BaseException):
else:
else:
... # a line was read within 5 seconds
... # a line was read within 5 seconds
.. caution:: If ``readline()`` above catches and doesn't re-raise :class:`BaseException`
.. caution::
(for example, with a bare ``except:``), then your timeout will fail to function and control
won't be returned to you when you expect.
If ``readline()`` above catches and doesn't re-raise
:exc:`BaseException` (for example, with a bare ``except:``), then
your timeout will fail to function and control won't be returned
to you when you expect.
.. rubric:: Catching Timeouts
When catching timeouts, keep in mind that the one you catch may
When catching timeouts, keep in mind that the one you catch may
not be the one you have set (a calling function may have set its
not be the one you have set (a calling function may have set its
...
@@ -112,41 +167,49 @@ class Timeout(BaseException):
...
@@ -112,41 +167,49 @@ class Timeout(BaseException):
except Timeout as t:
except Timeout as t:
if t is not timeout:
if t is not timeout:
raise # not my timeout
raise # not my timeout
finally:
timeout.close()
If the *seconds* argument is not given or is ``None`` (e.g.,
``Timeout()``), then the timeout will never expire and never raise
*exception*. This is convenient for creating functions which take
an optional timeout parameter of their own. (Note that this is not the same thing
as a *seconds* value of 0.)
.. caution::
A *seconds* value less than 0.0 (e.g., -1) is poorly defined. In the future,
support for negative values is likely to do the same thing as a value
of ``None``.
.. versionchanged:: 1.1b2
.. versionchanged:: 1.1b2
If *seconds* is not given or is ``None``, no longer allocate a libev
timer that will never be started.
If *seconds* is not given or is ``None``, no longer allocate a
native timer object that will never be started.
.. versionchanged:: 1.1
.. versionchanged:: 1.1
Add warning about negative *seconds* values.
Add warning about negative *seconds* values.
.. versionchanged:: 1.3a1
Timeout objects now have a :meth:`close`
method that must be called when the timeout will no longer be
used to properly clean up native resources.
The ``with`` statement does this automatically.
"""
"""
# We inherit a __dict__ from BaseException, so __slots__ actually
# makes us larger.
def
__init__
(
self
,
seconds
=
None
,
exception
=
None
,
ref
=
True
,
priority
=-
1
,
def
__init__
(
self
,
seconds
=
None
,
exception
=
None
,
ref
=
True
,
priority
=-
1
,
_
use_timer
=
True
,
_
one_shot
=
False
):
_one_shot
=
False
):
BaseException
.
__init__
(
self
)
BaseException
.
__init__
(
self
)
self
.
seconds
=
seconds
self
.
seconds
=
seconds
self
.
exception
=
exception
self
.
exception
=
exception
self
.
_one_shot
=
_one_shot
self
.
_one_shot
=
_one_shot
if
seconds
is
None
or
not
_use_timer
:
if
seconds
is
None
:
# Avoid going through the timer codepath if no timeout is
# Avoid going through the timer codepath if no timeout is
# desired; this avoids some CFFI interactions on PyPy that can lead to a
# desired; this avoids some CFFI interactions on PyPy that can lead to a
# RuntimeError if this implementation is used during an `import` statement. See
# RuntimeError if this implementation is used during an `import` statement. See
# https://bitbucket.org/pypy/pypy/issues/2089/crash-in-pypy-260-linux64-with-gevent-11b1
# https://bitbucket.org/pypy/pypy/issues/2089/crash-in-pypy-260-linux64-with-gevent-11b1
# and https://github.com/gevent/gevent/issues/618.
# and https://github.com/gevent/gevent/issues/618.
# Plus, in general, it should be more efficient
# Plus, in general, it should be more efficient
self
.
timer
=
_FakeTimer
self
.
timer
=
_FakeTimer
else
:
else
:
# XXX: A zero second timer could cause libuv to block the loop.
# XXX: A timer <= 0 could cause libuv to block the loop; we catch
# that case in libuv/loop.py
self
.
timer
=
get_hub
().
loop
.
timer
(
seconds
or
0.0
,
ref
=
ref
,
priority
=
priority
)
self
.
timer
=
get_hub
().
loop
.
timer
(
seconds
or
0.0
,
ref
=
ref
,
priority
=
priority
)
def
start
(
self
):
def
start
(
self
):
...
@@ -207,18 +270,29 @@ class Timeout(BaseException):
...
@@ -207,18 +270,29 @@ class Timeout(BaseException):
@
property
@
property
def
pending
(
self
):
def
pending
(
self
):
"""
Return
True if the timeout is scheduled to be raised."""
"""True if the timeout is scheduled to be raised."""
return
self
.
timer
.
pending
or
self
.
timer
.
active
return
self
.
timer
.
pending
or
self
.
timer
.
active
def
cancel
(
self
):
def
cancel
(
self
):
"""If the timeout is pending, cancel it. Otherwise, do nothing."""
"""
If the timeout is pending, cancel it. Otherwise, do nothing.
The timeout object can be :meth:`started <start>` again. If
you will not start the timeout again, you should use
:meth:`close` instead.
"""
self
.
timer
.
stop
()
self
.
timer
.
stop
()
if
self
.
_one_shot
:
if
self
.
_one_shot
:
self
.
close
()
self
.
close
()
def
close
(
self
):
def
close
(
self
):
"""
Close the timeout and free resources. The timer cannot be started again
after this method has been used.
"""
self
.
timer
.
stop
()
self
.
timer
.
stop
()
self
.
timer
.
close
()
self
.
timer
.
close
()
self
.
timer
=
_FakeTimer
def
__repr__
(
self
):
def
__repr__
(
self
):
classname
=
type
(
self
).
__name__
classname
=
type
(
self
).
__name__
...
@@ -251,6 +325,9 @@ class Timeout(BaseException):
...
@@ -251,6 +325,9 @@ class Timeout(BaseException):
return
'%s second%s: %s'
%
(
self
.
seconds
,
suffix
,
self
.
exception
)
return
'%s second%s: %s'
%
(
self
.
seconds
,
suffix
,
self
.
exception
)
def
__enter__
(
self
):
def
__enter__
(
self
):
"""
Start and return the timer. If the timer is already started, just return it.
"""
if
not
self
.
pending
:
if
not
self
.
pending
:
self
.
start
()
self
.
start
()
return
self
return
self
...
@@ -258,6 +335,7 @@ class Timeout(BaseException):
...
@@ -258,6 +335,7 @@ class Timeout(BaseException):
def
__exit__
(
self
,
typ
,
value
,
tb
):
def
__exit__
(
self
,
typ
,
value
,
tb
):
"""
"""
Stop the timer.
Stop the timer.
.. versionchanged:: 1.3a1
.. versionchanged:: 1.3a1
The underlying native timer is also stopped. This object cannot be
The underlying native timer is also stopped. This object cannot be
used again.
used again.
...
...
src/greentest/.coveragerc
View file @
6fffdb4c
...
@@ -21,6 +21,9 @@ omit =
...
@@ -21,6 +21,9 @@ omit =
# local.so sometimes gets included, and it can't be parsed
# local.so sometimes gets included, and it can't be parsed
# as source, so it fails the whole process.
# as source, so it fails the whole process.
*.so
*.so
src/gevent/libev/*.so
src/gevent/libuv/*.so
src/gevent/resolver/*.so
[report]
[report]
...
...
src/greentest/known_failures.py
View file @
6fffdb4c
...
@@ -157,6 +157,13 @@ if PYPY:
...
@@ -157,6 +157,13 @@ if PYPY:
'test__socket_dns.py'
,
'test__socket_dns.py'
,
]
]
if
TRAVIS
:
FAILING_TESTS
+=
[
# This fails to get the correct results, sometimes. I can't reproduce locally
'FLAKY test__example_udp_server.py'
,
'FLAKY test__example_udp_client.py'
,
]
if
PY3
and
TRAVIS
:
if
PY3
and
TRAVIS
:
FAILING_TESTS
+=
[
FAILING_TESTS
+=
[
## ---
## ---
...
...
src/greentest/test__all__.py
View file @
6fffdb4c
...
@@ -7,20 +7,7 @@ import types
...
@@ -7,20 +7,7 @@ import types
from
greentest.modules
import
walk_modules
from
greentest.modules
import
walk_modules
from
greentest.sysinfo
import
PLATFORM_SPECIFIC_SUFFIXES
from
greentest.sysinfo
import
PLATFORM_SPECIFIC_SUFFIXES
from
gevent._patcher
import
MAPPING
MAPPING
=
{
'gevent.local'
:
'_threading_local'
,
'gevent.socket'
:
'socket'
,
'gevent.select'
:
'select'
,
'gevent.ssl'
:
'ssl'
,
'gevent.thread'
:
'_thread'
if
six
.
PY3
else
'thread'
,
'gevent.subprocess'
:
'subprocess'
,
'gevent.os'
:
'os'
,
'gevent.threading'
:
'threading'
,
'gevent.builtins'
:
'builtins'
if
six
.
PY3
else
'__builtin__'
,
'gevent.signal'
:
'signal'
,
}
class
ANY
(
object
):
class
ANY
(
object
):
def
__contains__
(
self
,
item
):
def
__contains__
(
self
,
item
):
...
@@ -43,13 +30,17 @@ COULD_BE_MISSING = {
...
@@ -43,13 +30,17 @@ COULD_BE_MISSING = {
'subprocess'
:
[
'_posixsubprocess'
],
'subprocess'
:
[
'_posixsubprocess'
],
}
}
NO_ALL
=
[
'gevent.threading'
,
NO_ALL
=
[
'gevent._util'
,
'gevent.threading'
,
'gevent._compat'
,
'gevent._util'
,
'gevent._socketcommon'
,
'gevent._compat'
,
'gevent._fileobjectcommon'
,
'gevent._fileobjectposix'
,
'gevent._socketcommon'
,
'gevent._tblib'
,
'gevent._fileobjectcommon'
,
'gevent._corecffi'
]
'gevent._fileobjectposix'
,
'gevent._tblib'
,
'gevent._corecffi'
,
'gevent._patcher'
,
]
# A list of modules that may contain things that aren't actually, technically,
# A list of modules that may contain things that aren't actually, technically,
# extensions, but that need to be in __extensions__ anyway due to the way,
# extensions, but that need to be in __extensions__ anyway due to the way,
...
@@ -71,7 +62,7 @@ class Test(unittest.TestCase):
...
@@ -71,7 +62,7 @@ class Test(unittest.TestCase):
def
check_all
(
self
):
def
check_all
(
self
):
"Check that __all__ is present and does not contain invalid entries"
"Check that __all__ is present and does not contain invalid entries"
if
not
hasattr
(
self
.
module
,
'__all__'
):
if
not
hasattr
(
self
.
module
,
'__all__'
):
assert
self
.
modname
in
NO_ALL
self
.
assertIn
(
self
.
modname
,
NO_ALL
)
return
return
names
=
{}
names
=
{}
six
.
exec_
(
"from %s import *"
%
self
.
modname
,
names
)
six
.
exec_
(
"from %s import *"
%
self
.
modname
,
names
)
...
@@ -86,10 +77,11 @@ class Test(unittest.TestCase):
...
@@ -86,10 +77,11 @@ class Test(unittest.TestCase):
def
check_implements_presence_justified
(
self
):
def
check_implements_presence_justified
(
self
):
"Check that __implements__ is present only if the module is modeled after a module from stdlib (like gevent.socket)."
"Check that __implements__ is present only if the module is modeled after a module from stdlib (like gevent.socket)."
if
self
.
__implements__
is
not
None
and
self
.
stdlib_module
is
None
:
if
self
.
__implements__
is
not
None
and
self
.
stdlib_module
is
None
:
raise
AssertionError
(
'%r has __implements__ but no stdlib counterpart'
%
self
.
modname
)
raise
AssertionError
(
'%r has __implements__ but no stdlib counterpart (%s)'
%
(
self
.
modname
,
self
.
stdlib_name
))
def
set_stdlib_all
(
self
):
def
set_stdlib_all
(
self
):
assert
self
.
stdlib_module
is
not
None
self
.
assertIsNotNone
(
self
.
stdlib_module
)
self
.
stdlib_has_all
=
True
self
.
stdlib_has_all
=
True
self
.
stdlib_all
=
getattr
(
self
.
stdlib_module
,
'__all__'
,
None
)
self
.
stdlib_all
=
getattr
(
self
.
stdlib_module
,
'__all__'
,
None
)
if
self
.
stdlib_all
is
None
:
if
self
.
stdlib_all
is
None
:
...
@@ -115,7 +107,7 @@ class Test(unittest.TestCase):
...
@@ -115,7 +107,7 @@ class Test(unittest.TestCase):
item
=
getattr
(
self
.
module
,
name
)
item
=
getattr
(
self
.
module
,
name
)
try
:
try
:
stdlib_item
=
getattr
(
self
.
stdlib_module
,
name
)
stdlib_item
=
getattr
(
self
.
stdlib_module
,
name
)
assert
item
is
not
stdlib_item
,
(
name
,
item
,
stdlib_item
)
self
.
assertIsNot
(
item
,
stdlib_item
)
except
AttributeError
:
except
AttributeError
:
if
name
not
in
COULD_BE_MISSING
.
get
(
self
.
stdlib_name
,
[]):
if
name
not
in
COULD_BE_MISSING
.
get
(
self
.
stdlib_name
,
[]):
raise
raise
...
@@ -125,7 +117,7 @@ class Test(unittest.TestCase):
...
@@ -125,7 +117,7 @@ class Test(unittest.TestCase):
for
name
in
self
.
__imports__
:
for
name
in
self
.
__imports__
:
item
=
getattr
(
self
.
module
,
name
)
item
=
getattr
(
self
.
module
,
name
)
stdlib_item
=
getattr
(
self
.
stdlib_module
,
name
)
stdlib_item
=
getattr
(
self
.
stdlib_module
,
name
)
assert
item
is
stdlib_item
,
(
name
,
item
,
stdlib_item
)
self
.
assertIs
(
item
,
stdlib_item
)
def
check_extensions_actually_extend
(
self
):
def
check_extensions_actually_extend
(
self
):
"""Check that the module actually defines new entries in __extensions__"""
"""Check that the module actually defines new entries in __extensions__"""
...
...
src/greentest/test__example_udp_client.py
View file @
6fffdb4c
...
@@ -17,7 +17,7 @@ class Test_udp_client(TestCase):
...
@@ -17,7 +17,7 @@ class Test_udp_client(TestCase):
server
=
DatagramServer
(
'127.0.0.1:9000'
,
handle
)
server
=
DatagramServer
(
'127.0.0.1:9000'
,
handle
)
server
.
start
()
server
.
start
()
try
:
try
:
run
([
sys
.
executable
,
'-W'
,
'ignore'
'-u'
,
'udp_client.py'
,
'Test_udp_client'
],
run
([
sys
.
executable
,
'-W'
,
'ignore'
,
'-u'
,
'udp_client.py'
,
'Test_udp_client'
],
timeout
=
10
,
cwd
=
'../../examples/'
)
timeout
=
10
,
cwd
=
'../../examples/'
)
finally
:
finally
:
server
.
close
()
server
.
close
()
...
...
src/greentest/test__socket_dns.py
View file @
6fffdb4c
...
@@ -4,10 +4,6 @@
...
@@ -4,10 +4,6 @@
import
gevent
import
gevent
from
gevent
import
monkey
from
gevent
import
monkey
if
[
'gevent.resolver.dnspython.Resolver'
]
==
gevent
.
get_hub
().
resolver_class
:
# dnspython requires monkey-patching
monkey
.
patch_all
()
import
os
import
os
import
re
import
re
import
greentest
import
greentest
...
...
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