Commit 6e2ab023 authored by Jason Madden's avatar Jason Madden

Documenting libuv and 1.3 updates [skip ci]

This addresses the first few points of #1060.

We now have a basic documentation for what a loop looks like, and we
have documented watcher.close()

Hub is better documented (only the parts that should be documented are
documented).

get/setswitchinterval is documnted.

Several xrefs are fixed up.
parent 383c1c19
:mod:`gevent.core` - event loop based on libev ==========================================================
============================================== :mod:`gevent.core` - (deprecated) event loop abstraction
==========================================================
.. automodule:: gevent.core .. automodule:: gevent.core
This module is a wrapper around libev__ and follower the libev API pretty closely. Note, This module was originally a wrapper around libev_ and followed the
that gevent creates an event loop transparently for the user and runs it in a dedicated libev API pretty closely. Now that we support libuv, it also serves as
greenlet (called hub), so using this module is not necessary. In fact, if you do use it, something of an event loop abstraction layer. Most people will not
chances are that your program is not compatible across different gevent version (gevent.core in need to use the objects defined in this module directly. If you need
0.x has a completely different interface and 2.x will probably have yet another interface). to create watcher objects, you should use the methods defined on the
current event loop.
On Windows, this wrapper will accept Windows handles rather than stdio file descriptors which libev requires. This is to simplify Note that gevent creates an event loop transparently for the user and
interaction with the rest of the Python, since it requires Windows handles. runs it in a dedicated greenlet (called hub), so using this module is
not necessary. In fact, if you do use it, chances are that your
program is not compatible across different gevent versions (gevent.core
in 0.x has a completely different interface and 2.x will probably have
yet another interface) and implementations (the libev, libev CFFI and
libuv implementations all have different contents in this module).
The current event loop can be obtained with ``gevent.get_hub().loop``.
.. caution::
__ http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod Never instantiate the watcher classes defined in this module (if
they are defined in this module; the various event loop
implementations do something very different with them). Always use
the :class:`watcher methods defined <gevent._interfaces.ILoop>`
on :attr:`the current loop <gevent.hub.Hub.loop>`, i.e.,
``get_hub().loop``.
events On Windows, this wrapper will accept Windows handles rather than stdio
------ file descriptors which libev requires. This is to simplify interaction
with the rest of the Python, since it requires Windows handles.
.. autoclass:: loop(flags=None, default=True)
:members:
:undoc-members:
misc functions
--------------
.. autofunction:: get_version
.. autofunction:: get_header_version
.. autofunction:: supported_backends
.. autofunction:: recommended_backends
.. autofunction:: embeddable_backends
.. autofunction:: time
.. _libev: http://software.schmorp.de/pkg/libev.html
==================================== ============================
:mod:`gevent.hub` - Event-loop hub The Event Loop and the Hub
==================================== ============================
.. module:: gevent.hub .. module:: gevent.hub
The hub is a special greenlet created automatically to run the event loop.
The current hub can be retrieved with `get_hub`.
.. autofunction:: get_hub .. autofunction:: get_hub
.. autoclass:: Hub .. autoclass:: Hub
:members: :members:
:undoc-members:
.. automethod:: wait .. automethod:: wait
.. automethod:: cancel_wait .. automethod:: cancel_wait
.. attribute:: loop
the event loop object (`ILoop`) associated with this hub and thus
this native thread.
The Event Loop
==============
The current event loop can be obtained with ``get_hub().loop``.
All implementations of the loop provide a common minimum interface.
.. autointerface:: gevent._interfaces.ILoop
.. autointerface:: gevent._interfaces.IWatcher
Utilities
=========
.. autoclass:: Waiter .. autoclass:: Waiter
Exceptions
==========
.. autoclass:: LoopExit .. autoclass:: LoopExit
The following exceptions *are not* expected to be thrown and *are not*
meant to be caught; if they are raised to user code it is generally a
serious programming error or a bug in gevent, greenlet, or its event
loop implementation. They are presented here for documentation
purposes only.
.. autoclass:: gevent.exceptions.ConcurrentObjectUseError
.. autoclass:: gevent.exceptions.BlockingSwitchOutError
.. autoclass:: gevent.exceptions.InvalidSwitchError
...@@ -48,6 +48,33 @@ Sleeping ...@@ -48,6 +48,33 @@ Sleeping
.. autofunction:: idle .. autofunction:: idle
Switching
=========
.. function:: getswitchinterval() -> current switch interval
See :func:`setswitchinterval`
.. versionadded:: 1.3
.. function:: setswitchinterval(seconds)
Set the approximate maximum amount of time that callback functions
will be allowed to run before the event loop is cycled to
poll for events. This prevents code that does things like the
following from monopolizing the loop::
while True: # Burning CPU!
gevent.sleep(0) # But yield to other greenlets
Prior to this, this code could prevent the event loop from running.
On Python 3, this uses the native :func:`sys.setswitchinterval`
and :func:`sys.getswitchinterval`.
.. versionadded:: 1.3
Waiting Waiting
======= =======
......
...@@ -69,14 +69,19 @@ These functions are used to block the current greenlet until an open ...@@ -69,14 +69,19 @@ These functions are used to block the current greenlet until an open
file (socket) is ready to perform I/O operations. These are low-level file (socket) is ready to perform I/O operations. These are low-level
functions not commonly used by many programs. functions not commonly used by many programs.
.. note:: These use the underlying libev ``io`` watchers, which means .. note::
that they share the same implementation limits. For example,
on some platforms they can be used with more than just These use the underlying event loop ``io`` watchers, which means
sockets, while on others the applicability is more limited that they share the same implementation limits. For example,
(POSIX platforms like Linux and OS X can use pipes and fifos on some platforms they can be used with more than just
but Windows is limited to sockets). sockets, while on others the applicability is more limited
(POSIX platforms like Linux and OS X can use pipes and fifos
.. note:: On Windows, gevent is limited to 1024 open sockets. but Windows is limited to sockets).
.. note::
On Windows with the libev event loop, gevent is limited to 1024
open sockets.
.. autofunction:: gevent.socket.wait_read .. autofunction:: gevent.socket.wait_read
.. autofunction:: gevent.socket.wait_write .. autofunction:: gevent.socket.wait_write
......
...@@ -19,7 +19,7 @@ import os ...@@ -19,7 +19,7 @@ import os
# for better documentation extraction and ease of tweaking docs. # for better documentation extraction and ease of tweaking docs.
os.environ['PURE_PYTHON'] = '1' os.environ['PURE_PYTHON'] = '1'
sys.path.append('.') # for mysphinxext sys.path.append(os.path.dirname(__file__)) # for mysphinxext
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
...@@ -54,7 +54,7 @@ extlinks = {'issue': ('https://github.com/gevent/gevent/issues/%s', ...@@ -54,7 +54,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' autodoc_member_order = 'groupwise'
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.
......
...@@ -4,5 +4,7 @@ ...@@ -4,5 +4,7 @@
Configuring gevent Configuring gevent
==================== ====================
.. seealso:: :func:`gevent.setswitchinterval`
For additional runtime configuration.
.. autoclass:: gevent._config.Config .. autoclass:: gevent._config.Config
...@@ -24,16 +24,8 @@ Configuration ...@@ -24,16 +24,8 @@ Configuration
gevent includes four implementations of resolvers, and applications gevent includes four implementations of resolvers, and applications
can provide their own implementation. By default, gevent uses can provide their own implementation. By default, gevent uses
:class:`gevent.resolver.thread.Resolver`. :class:`a threadpool <gevent.resolver.thread.Resolver>`. This can
:attr:`be customized <gevent._config.Config.resolver>`.
Configuration can be done through the ``GEVENT_RESOLVER`` environment
variable. Specify ``ares``, ``thread``, ``dnspython``, or ``block`` to use the
:class:`gevent.resolver.ares.Resolver`,
:class:`gevent.resolver.thread.Resolver`, or
:class:`gevent.resolver.dnspython.Resolver`, or
:class:`gevent.resolver.blocking.Resolver`, respectively, or set it to
the fully-qualified name of an implementation of the standard
functions.
Please see the documentation for each resolver class to understand the Please see the documentation for each resolver class to understand the
relative performance and correctness tradeoffs. relative performance and correctness tradeoffs.
......
...@@ -3,19 +3,22 @@ ...@@ -3,19 +3,22 @@
================= =================
gevent is a coroutine_ -based Python_ networking library that uses gevent is a coroutine_ -based Python_ networking library that uses
greenlet_ to provide a high-level synchronous API on top of the libev greenlet_ to provide a high-level synchronous API on top of the `libev`_
event loop. or `libuv`_ event loop.
Features include: Features include:
* **Fast event loop** based on libev or libuv (epoll on Linux, kqueue on FreeBSD). * Fast event loop based on `libev`_ or `libuv`_
* **Lightweight execution units** based on greenlet. * Lightweight execution units based on greenlet_.
* API that re-uses concepts from the Python standard library (for * API that re-uses concepts from the Python standard library (for
examples there are :class:`events <gevent.event.Event>` and examples there are :class:`events <gevent.event.Event>` and
:class:`queues <gevent.queue.Queue>`). :class:`queues <gevent.queue.Queue>`).
* :ref:`Cooperative sockets with SSL support <networking>` * :ref:`Cooperative sockets with SSL support <networking>`
* DNS queries performed through threadpool or c-ares. * :doc:`Cooperative DNS queries <dns>` performed through a threadpool, dnspython, or c-ares.
* :ref:`Monkey patching utility <monkey-patching>` to get 3rd party modules to become cooperative * :ref:`Monkey patching utility <monkey-patching>` to get 3rd party modules to become cooperative
* TCP/UDP/HTTP servers
* Subprocess support (through :mod:`gevent.subprocess`)
* Thread pools
gevent is `inspired by eventlet gevent is `inspired by eventlet
...@@ -27,10 +30,11 @@ and check out the list of the `open source projects based on gevent. <https://gi ...@@ -27,10 +30,11 @@ and check out the list of the `open source projects based on gevent. <https://gi
gevent was written by `Denis Bilenko <http://denisbilenko.com/>`_. gevent was written by `Denis Bilenko <http://denisbilenko.com/>`_.
Since version 1.1, gevent is maintained by `NextThought Since version 1.1, gevent is maintained by Jason Madden for
<https://nextthought.com>`_ with help from the `contributors `NextThought <https://nextthought.com>`_ with help from the
<https://github.com/gevent/gevent/graphs/contributors>`_ and is `contributors <https://github.com/gevent/gevent/graphs/contributors>`_.
licensed under the MIT license.
gevent is licensed under the MIT license.
:ref:`Continue reading <installation>` » :ref:`Continue reading <installation>` »
...@@ -61,3 +65,5 @@ Mailing List ...@@ -61,3 +65,5 @@ Mailing List
.. _coroutine: https://en.wikipedia.org/wiki/Coroutine .. _coroutine: https://en.wikipedia.org/wiki/Coroutine
.. _Python: http://python.org .. _Python: http://python.org
.. _greenlet: https://greenlet.readthedocs.io .. _greenlet: https://greenlet.readthedocs.io
.. _libev: http://software.schmorp.de/pkg/libev.html
.. _libuv: http://libuv.org
...@@ -2,21 +2,33 @@ ...@@ -2,21 +2,33 @@
Introduction Introduction
============== ==============
gevent is a coroutine-based Python networking library. gevent is a coroutine_ -based Python_ networking library that uses
greenlet_ to provide a high-level synchronous API on top of the `libev`_
or `libuv`_ event loop.
Features include: Features include:
* Fast event loop based on libev or libuv (epoll on Linux, kqueue on FreeBSD,
select on Mac OS X, IOCP on Windows). * Fast event loop based on `libev`_ or `libuv`_
* Lightweight execution units based on greenlet. * Lightweight execution units based on greenlet_.
* API that re-uses concepts from the Python standard library (e.g. :class:`gevent.event.Event`, :class:`gevent.queue.Queue`). * API that re-uses concepts from the Python standard library (for
* Cooperative :mod:`socket` and :mod:`ssl` modules. examples there are :class:`events <gevent.event.Event>` and
* Ability to use standard library and 3rd party modules written for standard blocking sockets (:mod:`gevent.monkey`). :class:`queues <gevent.queue.Queue>`).
* DNS queries performed through threadpool (default) or through c-ares (enabled via GEVENT_RESOLVER=ares env var). * :ref:`Cooperative sockets with SSL support <networking>`
* :doc:`Cooperative DNS queries <dns>` performed through a threadpool,
dnspython, or c-ares.
* :ref:`Monkey patching utility <monkey-patching>` to get 3rd party modules to become cooperative
* TCP/UDP/HTTP servers * TCP/UDP/HTTP servers
* Subprocess support (through :mod:`gevent.subprocess`) * Subprocess support (through :mod:`gevent.subprocess`)
* Thread pools * Thread pools
.. _coroutine: https://en.wikipedia.org/wiki/Coroutine
.. _Python: http://python.org
.. _greenlet: https://greenlet.readthedocs.io
.. _libev: http://software.schmorp.de/pkg/libev.html
.. _libuv: http://libuv.org
.. _installation: .. _installation:
Installation and Requirements Installation and Requirements
...@@ -185,31 +197,20 @@ that the greenlet *yielded* control to the Hub). If there's no ...@@ -185,31 +197,20 @@ that the greenlet *yielded* control to the Hub). If there's no
:class:`~gevent.hub.Hub`. This makes it possible to use the :class:`~gevent.hub.Hub`. This makes it possible to use the
gevent blocking API from multiple threads (with care). gevent blocking API from multiple threads (with care).
The event loop provided by libev uses the fastest polling mechanism The event loop uses the best polling mechanism available on the system
available on the system by default. Please read the `libev documentation`_ for more by default.
information.
.. caution:: This section needs to be updated for gevent 1.3 and libuv.
.. As of 1.1 or before, we set the EVFLAG_NOENV so this isn't possible any more.
It is possible to command libev to
use a particular polling mechanism by setting the ``LIBEV_FLAGS``
environment variable. Possible values include ``LIBEV_FLAGS=1`` for
the select backend, ``LIBEV_FLAGS=2`` for the poll backend,
``LIBEV_FLAGS=4`` for the epoll backend and ``LIBEV_FLAGS=8`` for the
kqueue backend.
.. _`libev documentation`: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#FUNCTIONS_CONTROLLING_EVENT_LOOPS
The libev API is available under the :mod:`gevent.core` module. Note .. note::
that the callbacks supplied to the libev API are run in the
:class:`~gevent.hub.Hub` greenlet and thus cannot use the synchronous
gevent API. It is possible to use the asynchronous API there, like
:func:`gevent.spawn` and :meth:`gevent.event.Event.set`.
.. caution:: This section needs to be updated for gevent 1.3 and libuv. A low-level event loop API is available under the
:mod:`gevent.core` module. This module is not documented, not meant
for general purpose usage, and it's exact contents and semantics
change slightly depending on whether the libev or libuv event loop
is being used. The callbacks supplied to the event loop API are run
in the :class:`~gevent.hub.Hub` greenlet and thus cannot use the
synchronous gevent API. It is possible to use the asynchronous API
there, like :func:`gevent.spawn` and
:meth:`gevent.event.Event.set`.
Cooperative multitasking Cooperative multitasking
...@@ -223,7 +224,7 @@ control, (by calling a blocking function that will switch to the ...@@ -223,7 +224,7 @@ control, (by calling a blocking function that will switch to the
:class:`~gevent.hub.Hub`), other greenlets won't get a chance to run. :class:`~gevent.hub.Hub`), other greenlets won't get a chance to run.
This is typically not an issue for an I/O bound app, but one should be This is typically not an issue for an I/O bound app, but one should be
aware of this when doing something CPU intensive, or when calling aware of this when doing something CPU intensive, or when calling
blocking I/O functions that bypass the libev event loop. blocking I/O functions that bypass the event loop.
.. tip:: Even some apparently cooperative functions, like .. tip:: Even some apparently cooperative functions, like
:func:`gevent.sleep`, can temporarily take priority over :func:`gevent.sleep`, can temporarily take priority over
......
...@@ -493,14 +493,6 @@ class AbstractLoop(object): ...@@ -493,14 +493,6 @@ class AbstractLoop(object):
raise NotImplementedError() raise NotImplementedError()
def destroy(self): def destroy(self):
"""
Clean up resources used by this loop.
Note that, unlike the libev C loop implementation, this object
*does not* have a finalizer (``__del__``). If you create loops
(especially loops that are not the default) you *should* call
this method when you are done with the loop.
"""
if self._ptr: if self._ptr:
try: try:
if not self._can_destroy_loop(self._ptr): if not self._can_destroy_loop(self._ptr):
...@@ -588,17 +580,9 @@ class AbstractLoop(object): ...@@ -588,17 +580,9 @@ class AbstractLoop(object):
pass pass
def now(self): def now(self):
"""
Return the loop's notion of the current time.
This may not necessarily be related to :func:`time.time` (it
may have a different starting point), but it must be expressed
in fractional seconds (the same *units* used by :func:`time.time`).
"""
raise NotImplementedError() raise NotImplementedError()
def update_now(self): def update_now(self):
"Update the loop's notion of the current time."
raise NotImplementedError() raise NotImplementedError()
def update(self): def update(self):
......
...@@ -281,12 +281,15 @@ def wait_on_watcher(watcher, timeout=None, timeout_exc=_NONE, hub=None): ...@@ -281,12 +281,15 @@ def wait_on_watcher(watcher, timeout=None, timeout_exc=_NONE, hub=None):
(``socket.error(EBADF, 'File descriptor was closed in another (``socket.error(EBADF, 'File descriptor was closed in another
greenlet')`` by default). greenlet')`` by default).
:param io: A libev watcher, most commonly an IO watcher obtained from :param io: An event loop watcher, most commonly an IO watcher obtained from
:meth:`gevent.core.loop.io` :meth:`gevent.core.loop.io`
:keyword timeout_exc: The exception to raise if the timeout expires. :keyword timeout_exc: The exception to raise if the timeout expires.
By default, a :class:`socket.timeout` exception is raised. By default, a :class:`socket.timeout` exception is raised.
If you pass a value for this keyword, it is interpreted as for If you pass a value for this keyword, it is interpreted as for
:class:`gevent.timeout.Timeout`. :class:`gevent.timeout.Timeout`.
:raises ~gevent.hub.ConcurrentObjectUseError: If the *watcher* is
already started.
""" """
_primitive_wait(watcher, timeout, timeout_exc, hub) _primitive_wait(watcher, timeout, timeout_exc, hub)
......
# -*- coding: utf-8 -*-
# Copyright (c) 2018 gevent contributors. See LICENSE for details.
"""
Interfaces gevent uses that don't belong any one place.
This is not a public module, these interfaces are not
currently exposed to the public, they mostly exist for
documentation and testing purposes.
.. versionadded:: 1.3b2
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from gevent._util import Interface
from gevent._util import Attribute
# pylint:disable=no-method-argument, unused-argument, no-self-argument
__all__ = [
'ILoop',
'IWatcher',
]
class ILoop(Interface):
"""
The common interface expected for all event loops.
.. caution::
This is an internal, low-level interface. It may change
between minor versions of gevent.
.. rubric:: Watchers
The methods that create event loop watchers are `io`, `timer`,
`signal`, `idle`, `prepare`, `check`, `fork`, `async_`, `child`,
`stat`. These all return various types of :class:`IWatcher`.
All of those methods have one or two common arguments. *ref* is a
boolean saying whether the event loop is allowed to exit even if
this watcher is still started. *priority* is event loop specific.
"""
default = Attribute("Boolean indicating whether this is the default loop")
def run(nowait=False, once=False):
"""
Run the event loop.
This is usually called automatically by the hub greenlet, but
in special cases (when the hub is *not* running) you can use
this to control how the event loop runs (for example, to integrate
it with another event loop).
"""
def now():
"""
now() -> float
Return the loop's notion of the current time.
This may not necessarily be related to :func:`time.time` (it
may have a different starting point), but it must be expressed
in fractional seconds (the same *units* used by :func:`time.time`).
"""
def update_now():
"""
Update the loop's notion of the current time.
.. versionadded:: 1.3
In the past, this available as ``update``. This is still available as
an alias but will be removed in the future.
"""
def destroy():
"""
Clean up resources used by this loop.
If you create loops
(especially loops that are not the default) you *should* call
this method when you are done with the loop.
.. caution::
As an implementation note, the libev C loop implementation has a
finalizer (``__del__``) that destroys the object, but the libuv
and libev CFFI implementations do not. The C implementation may change.
"""
def io(fd, events, ref=True, priority=None):
"""
Create and return a new IO watcher for the given *fd*.
*events* is a bitmask specifying which events to watch
for. 1 means read, and 2 means write.
"""
def timer(after, repeat=0.0, ref=True, priority=None):
"""
Create and return a timer watcher that will fire after *after* seconds.
If *repeat* is given, the timer will continue to fire every *repeat* seconds.
"""
def signal(signum, ref=True, priority=None):
"""
Create and return a signal watcher for the signal *signum*,
one of the constants defined in :mod:`signal`.
This is platform and event loop specific.
"""
def idle(ref=True, priority=None):
"""
Create and return a watcher that fires when the event loop is idle.
"""
def prepare(ref=True, priority=None):
"""
Create and return a watcher that fires before the event loop
polls for IO.
"""
def check(ref=True, priority=None):
"""
Create and return a watcher that fires after the event loop
polls for IO.
"""
def fork(ref=True, priority=None):
"""
Create a watcher that fires when the process forks.
Availability: POSIX
"""
def async_(ref=True, priority=None):
"""
Create a watcher that fires when triggered, possibly
from another thread.
.. versionchanged:: 1.3
This was previously just named ``async``; for compatibility
with Python 3.7 where ``async`` is a keyword it was renamed.
On older versions of Python the old name is still around, but
it will be removed in the future.
"""
def child(pid, trace=0, ref=True):
"""
Create a watcher that fires for events on the child with process ID *pid*.
This is platform specific.
"""
def stat(path, interval=0.0, ref=True, priority=None):
"""
Create a watcher that monitors the filesystem item at *path*.
If the operating system doesn't support event notifications
from the filesystem, poll for changes every *interval* seconds.
"""
def run_callback(func, *args):
"""
Run the *func* passing it *args* at the next opportune moment.
This is a way of handing control to the event loop and deferring
an action.
"""
class IWatcher(Interface):
"""
An event loop watcher.
These objects call their *callback* function when the event
loop detects the event has happened.
.. important:: You *must* call :meth:`close` when you are
done with this object to avoid leaking native resources.
"""
def start(callback, *args, **kwargs):
"""
Have the event loop begin watching for this event.
When the event is detected, *callback* will be called with
*args*.
.. caution::
Not all watchers accept ``**kwargs``,
and some watchers define special meanings for certain keyword args.
"""
def stop():
"""
Have the event loop stop watching this event.
In the future you may call :meth:`start` to begin watching
again.
"""
def close():
"""
Dispose of any native resources associated with the watcher.
If we were active, stop.
Attempting to operate on this object after calling close is
undefined. You should dispose of any references you have to it
after calling this method.
"""
...@@ -157,3 +157,22 @@ def gmctime(): ...@@ -157,3 +157,22 @@ def gmctime():
""" """
import time import time
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
try:
from zope.interface import Interface
from zope.interface import implementer
from zope.interface import Attribute
except ImportError:
class Interface(object):
pass
def implementer(_iface):
def dec(c):
return c
return dec
def Attribute(s):
return s
Interface = Interface
implementer = implementer
Attribute = Attribute
...@@ -112,20 +112,10 @@ else: ...@@ -112,20 +112,10 @@ else:
subscriber = plugin.load() subscriber = plugin.load()
subscriber(event) subscriber(event)
try: from gevent._util import Interface
from zope.interface import Interface from gevent._util import implementer
from zope.interface import implementer from gevent._util import Attribute
from zope.interface import Attribute
except ImportError:
class Interface(object):
pass
def implementer(_iface):
def dec(c):
return c
return dec
def Attribute(s):
return s
class IPeriodicMonitorThread(Interface): class IPeriodicMonitorThread(Interface):
""" """
......
...@@ -18,11 +18,12 @@ __all__ = [ ...@@ -18,11 +18,12 @@ __all__ = [
class LoopExit(Exception): class LoopExit(Exception):
""" """
Exception thrown when the hub finishes running. Exception thrown when the hub finishes running (`gevent.hub.Hub.run`
would return).
In a normal application, this is never thrown or caught In a normal application, this is never thrown or caught
explicitly. The internal implementation of functions like explicitly. The internal implementation of functions like
:func:`join` and :func:`joinall` may catch it, but user code :meth:`gevent.hub.Hub.join` and :func:`gevent.joinall` may catch it, but user code
generally should not. generally should not.
.. caution:: .. caution::
...@@ -37,16 +38,30 @@ class LoopExit(Exception): ...@@ -37,16 +38,30 @@ class LoopExit(Exception):
class BlockingSwitchOutError(AssertionError): class BlockingSwitchOutError(AssertionError):
pass """
Raised when a gevent synchronous function is called from a
low-level event loop callback.
This is usually a programming error.
"""
class InvalidSwitchError(AssertionError): class InvalidSwitchError(AssertionError):
pass """
Raised when the event loop returns control to a greenlet in an
unexpected way.
This is usually a bug in gevent, greenlet, or the event loop.
"""
class ConcurrentObjectUseError(AssertionError): class ConcurrentObjectUseError(AssertionError):
# raised when an object is used (waited on) by two greenlets """
# independently, meaning the object was entered into a blocking Raised when an object is used (waited on) by two greenlets
# state by one greenlet and then another while still blocking in the independently, meaning the object was entered into a blocking
# first one state by one greenlet and then another while still blocking in the
pass first one.
This is usually a programming error.
.. seealso:: `gevent.socket.wait`
"""
...@@ -359,7 +359,8 @@ class Hub(WaitOperationsGreenlet): ...@@ -359,7 +359,8 @@ class Hub(WaitOperationsGreenlet):
#: do not get logged/printed when raised by the event loop. #: do not get logged/printed when raised by the event loop.
NOT_ERROR = (GreenletExit, SystemExit) NOT_ERROR = (GreenletExit, SystemExit)
#: The size we use for our threadpool. Either use a subclass
#: for this, or change it immediately after creating the hub.
threadpool_size = 10 threadpool_size = 10
# An instance of PeriodicMonitoringThread, if started. # An instance of PeriodicMonitoringThread, if started.
...@@ -375,6 +376,10 @@ class Hub(WaitOperationsGreenlet): ...@@ -375,6 +376,10 @@ class Hub(WaitOperationsGreenlet):
#: .. versionadded:: 1.3b1 #: .. versionadded:: 1.3b1
name = '' name = ''
# NOTE: We cannot define a class-level 'loop' attribute
# because that conflicts with the slot we inherit from the
# Cythonized-bases.
def __init__(self, loop=None, default=None): def __init__(self, loop=None, default=None):
WaitOperationsGreenlet.__init__(self, None, None) WaitOperationsGreenlet.__init__(self, None, None)
self.thread_ident = get_thread_ident() self.thread_ident = get_thread_ident()
...@@ -465,6 +470,13 @@ class Hub(WaitOperationsGreenlet): ...@@ -465,6 +470,13 @@ class Hub(WaitOperationsGreenlet):
self.handle_system_error(type, value) self.handle_system_error(type, value)
def handle_system_error(self, type, value): def handle_system_error(self, type, value):
"""
Called from `handle_error` when the exception type is determined
to be a :attr:`system error <SYSTEM_ERROR>`.
System errors cause the exception to be raised in the main
greenlet (the parent of this hub).
"""
current = getcurrent() current = getcurrent()
if current is self or current is self.parent or self.loop is None: if current is self or current is self.parent or self.loop is None:
self.parent.throw(type, value) self.parent.throw(type, value)
...@@ -607,6 +619,12 @@ class Hub(WaitOperationsGreenlet): ...@@ -607,6 +619,12 @@ class Hub(WaitOperationsGreenlet):
return False return False
def destroy(self, destroy_loop=None): def destroy(self, destroy_loop=None):
"""
Destroy this hub and clean up its resources.
If you manually create hubs, you *should* call this
method before disposing of the hub object reference.
"""
if self.periodic_monitoring_thread is not None: if self.periodic_monitoring_thread is not None:
self.periodic_monitoring_thread.kill() self.periodic_monitoring_thread.kill()
self.periodic_monitoring_thread = None self.periodic_monitoring_thread = None
...@@ -651,7 +669,12 @@ class Hub(WaitOperationsGreenlet): ...@@ -651,7 +669,12 @@ class Hub(WaitOperationsGreenlet):
def _del_resolver(self): def _del_resolver(self):
self._resolver = None self._resolver = None
resolver = property(_get_resolver, _set_resolver, _del_resolver) resolver = property(_get_resolver, _set_resolver, _del_resolver,
"""
The DNS resolver that the socket functions will use.
.. seealso:: :doc:`/dns`
""")
@property @property
...@@ -670,7 +693,19 @@ class Hub(WaitOperationsGreenlet): ...@@ -670,7 +693,19 @@ class Hub(WaitOperationsGreenlet):
def _del_threadpool(self): def _del_threadpool(self):
self._threadpool = None self._threadpool = None
threadpool = property(_get_threadpool, _set_threadpool, _del_threadpool) threadpool = property(_get_threadpool, _set_threadpool, _del_threadpool,
"""
The threadpool associated with this hub.
Usually this is a
:class:`gevent.threadpool.ThreadPool`, but
you :attr:`can customize that
<gevent._config.Config.threadpool>`.
Use this object to schedule blocking
(non-cooperative) operations in a different
thread to prevent them from halting the event loop.
""")
set_default_hub_class(Hub) set_default_hub_class(Hub)
......
...@@ -14,6 +14,9 @@ __all__ = [ ...@@ -14,6 +14,9 @@ __all__ = [
'loop', 'loop',
] ]
from gevent._util import implementer
from gevent._interfaces import ILoop
import gevent.libev._corecffi as _corecffi # pylint:disable=no-name-in-module,import-error import gevent.libev._corecffi as _corecffi # pylint:disable=no-name-in-module,import-error
ffi = _corecffi.ffi # pylint:disable=no-member ffi = _corecffi.ffi # pylint:disable=no-member
...@@ -200,6 +203,8 @@ from gevent._ffi.loop import AbstractLoop ...@@ -200,6 +203,8 @@ from gevent._ffi.loop import AbstractLoop
from gevent.libev import watcher as _watchers from gevent.libev import watcher as _watchers
_events_to_str = _watchers._events_to_str # exported _events_to_str = _watchers._events_to_str # exported
@implementer(ILoop)
class loop(AbstractLoop): class loop(AbstractLoop):
# pylint:disable=too-many-public-methods # pylint:disable=too-many-public-methods
......
...@@ -15,6 +15,8 @@ from gevent._ffi.loop import AbstractLoop ...@@ -15,6 +15,8 @@ from gevent._ffi.loop import AbstractLoop
from gevent.libuv import _corecffi # pylint:disable=no-name-in-module,import-error from gevent.libuv import _corecffi # pylint:disable=no-name-in-module,import-error
from gevent._ffi.loop import assign_standard_callbacks from gevent._ffi.loop import assign_standard_callbacks
from gevent._ffi.loop import AbstractCallbacks from gevent._ffi.loop import AbstractCallbacks
from gevent._util import implementer
from gevent._interfaces import ILoop
ffi = _corecffi.ffi ffi = _corecffi.ffi
libuv = _corecffi.lib libuv = _corecffi.lib
...@@ -66,7 +68,7 @@ def get_header_version(): ...@@ -66,7 +68,7 @@ def get_header_version():
def supported_backends(): def supported_backends():
return ['default'] return ['default']
@implementer(ILoop)
class loop(AbstractLoop): class loop(AbstractLoop):
# XXX: Undocumented. Maybe better named 'timer_resolution'? We can't # XXX: Undocumented. Maybe better named 'timer_resolution'? We can't
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment