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
This module is a wrapper around libev__ and follower the libev API pretty closely. Note,
that gevent creates an event loop transparently for the user and 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 version (gevent.core in
0.x has a completely different interface and 2.x will probably have yet another interface).
This module was originally a wrapper around libev_ and followed the
libev API pretty closely. Now that we support libuv, it also serves as
something of an event loop abstraction layer. Most people will not
need to use the objects defined in this module directly. If you need
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
interaction with the rest of the Python, since it requires Windows handles.
Note that gevent creates an event loop transparently for the user and
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
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
.. autoclass:: Hub
:members:
:undoc-members:
.. automethod:: 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
Exceptions
==========
.. 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
.. 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
=======
......
......@@ -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
functions not commonly used by many programs.
.. note:: These use the underlying libev ``io`` watchers, which means
that they share the same implementation limits. For example,
on some platforms they can be used with more than just
sockets, while on others the applicability is more limited
(POSIX platforms like Linux and OS X can use pipes and fifos
but Windows is limited to sockets).
.. note:: On Windows, gevent is limited to 1024 open sockets.
.. note::
These use the underlying event loop ``io`` watchers, which means
that they share the same implementation limits. For example,
on some platforms they can be used with more than just
sockets, while on others the applicability is more limited
(POSIX platforms like Linux and OS X can use pipes and fifos
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_write
......
......@@ -19,7 +19,7 @@ import os
# for better documentation extraction and ease of tweaking docs.
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,
# 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',
'pull request #')}
autodoc_default_flags = ['members', 'show-inheritance']
autodoc_member_order = 'bysource'
autodoc_member_order = 'groupwise'
autoclass_content = 'both'
# Add any paths that contain templates here, relative to this directory.
......
......@@ -4,5 +4,7 @@
Configuring gevent
====================
.. seealso:: :func:`gevent.setswitchinterval`
For additional runtime configuration.
.. autoclass:: gevent._config.Config
......@@ -24,16 +24,8 @@ Configuration
gevent includes four implementations of resolvers, and applications
can provide their own implementation. By default, gevent uses
:class:`gevent.resolver.thread.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.
:class:`a threadpool <gevent.resolver.thread.Resolver>`. This can
:attr:`be customized <gevent._config.Config.resolver>`.
Please see the documentation for each resolver class to understand the
relative performance and correctness tradeoffs.
......
......@@ -3,19 +3,22 @@
=================
gevent is a coroutine_ -based Python_ networking library that uses
greenlet_ to provide a high-level synchronous API on top of the libev
event loop.
greenlet_ to provide a high-level synchronous API on top of the `libev`_
or `libuv`_ event loop.
Features include:
* **Fast event loop** based on libev or libuv (epoll on Linux, kqueue on FreeBSD).
* **Lightweight execution units** based on greenlet.
* Fast event loop based on `libev`_ or `libuv`_
* Lightweight execution units based on greenlet_.
* API that re-uses concepts from the Python standard library (for
examples there are :class:`events <gevent.event.Event>` and
:class:`queues <gevent.queue.Queue>`).
* :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
* TCP/UDP/HTTP servers
* Subprocess support (through :mod:`gevent.subprocess`)
* Thread pools
gevent is `inspired by eventlet
......@@ -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/>`_.
Since version 1.1, gevent is maintained by `NextThought
<https://nextthought.com>`_ with help from the `contributors
<https://github.com/gevent/gevent/graphs/contributors>`_ and is
licensed under the MIT license.
Since version 1.1, gevent is maintained by Jason Madden for
`NextThought <https://nextthought.com>`_ with help from the
`contributors <https://github.com/gevent/gevent/graphs/contributors>`_.
gevent is licensed under the MIT license.
:ref:`Continue reading <installation>` »
......@@ -61,3 +65,5 @@ Mailing List
.. _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
......@@ -2,21 +2,33 @@
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:
* Fast event loop based on libev or libuv (epoll on Linux, kqueue on FreeBSD,
select on Mac OS X, IOCP on Windows).
* 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`).
* Cooperative :mod:`socket` and :mod:`ssl` modules.
* Ability to use standard library and 3rd party modules written for standard blocking sockets (:mod:`gevent.monkey`).
* DNS queries performed through threadpool (default) or through c-ares (enabled via GEVENT_RESOLVER=ares env var).
* Fast event loop based on `libev`_ or `libuv`_
* Lightweight execution units based on greenlet_.
* API that re-uses concepts from the Python standard library (for
examples there are :class:`events <gevent.event.Event>` and
:class:`queues <gevent.queue.Queue>`).
* :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
* Subprocess support (through :mod:`gevent.subprocess`)
* 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 and Requirements
......@@ -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
gevent blocking API from multiple threads (with care).
The event loop provided by libev uses the fastest polling mechanism
available on the system by default. Please read the `libev documentation`_ for more
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 event loop uses the best polling mechanism available on the system
by default.
The libev API is available under the :mod:`gevent.core` module. 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`.
.. note::
.. 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
......@@ -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.
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
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
:func:`gevent.sleep`, can temporarily take priority over
......
......@@ -493,14 +493,6 @@ class AbstractLoop(object):
raise NotImplementedError()
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:
try:
if not self._can_destroy_loop(self._ptr):
......@@ -588,17 +580,9 @@ class AbstractLoop(object):
pass
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()
def update_now(self):
"Update the loop's notion of the current time."
raise NotImplementedError()
def update(self):
......
......@@ -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
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`
:keyword timeout_exc: The exception to raise if the timeout expires.
By default, a :class:`socket.timeout` exception is raised.
If you pass a value for this keyword, it is interpreted as for
:class:`gevent.timeout.Timeout`.
:raises ~gevent.hub.ConcurrentObjectUseError: If the *watcher* is
already started.
"""
_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():
"""
import time
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:
subscriber = plugin.load()
subscriber(event)
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
from gevent._util import Interface
from gevent._util import implementer
from gevent._util import Attribute
def Attribute(s):
return s
class IPeriodicMonitorThread(Interface):
"""
......
......@@ -18,11 +18,12 @@ __all__ = [
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
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.
.. caution::
......@@ -37,16 +38,30 @@ class LoopExit(Exception):
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):
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):
# raised when an object is used (waited on) by two greenlets
# independently, meaning the object was entered into a blocking
# state by one greenlet and then another while still blocking in the
# first one
pass
"""
Raised when an object is used (waited on) by two greenlets
independently, meaning the object was entered into a blocking
state by one greenlet and then another while still blocking in the
first one.
This is usually a programming error.
.. seealso:: `gevent.socket.wait`
"""
......@@ -359,7 +359,8 @@ class Hub(WaitOperationsGreenlet):
#: do not get logged/printed when raised by the event loop.
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
# An instance of PeriodicMonitoringThread, if started.
......@@ -375,6 +376,10 @@ class Hub(WaitOperationsGreenlet):
#: .. versionadded:: 1.3b1
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):
WaitOperationsGreenlet.__init__(self, None, None)
self.thread_ident = get_thread_ident()
......@@ -465,6 +470,13 @@ class Hub(WaitOperationsGreenlet):
self.handle_system_error(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()
if current is self or current is self.parent or self.loop is None:
self.parent.throw(type, value)
......@@ -607,6 +619,12 @@ class Hub(WaitOperationsGreenlet):
return False
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:
self.periodic_monitoring_thread.kill()
self.periodic_monitoring_thread = None
......@@ -651,7 +669,12 @@ class Hub(WaitOperationsGreenlet):
def _del_resolver(self):
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
......@@ -670,7 +693,19 @@ class Hub(WaitOperationsGreenlet):
def _del_threadpool(self):
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)
......
......@@ -14,6 +14,9 @@ __all__ = [
'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
ffi = _corecffi.ffi # pylint:disable=no-member
......@@ -200,6 +203,8 @@ from gevent._ffi.loop import AbstractLoop
from gevent.libev import watcher as _watchers
_events_to_str = _watchers._events_to_str # exported
@implementer(ILoop)
class loop(AbstractLoop):
# pylint:disable=too-many-public-methods
......
......@@ -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._ffi.loop import assign_standard_callbacks
from gevent._ffi.loop import AbstractCallbacks
from gevent._util import implementer
from gevent._interfaces import ILoop
ffi = _corecffi.ffi
libuv = _corecffi.lib
......@@ -66,7 +68,7 @@ def get_header_version():
def supported_backends():
return ['default']
@implementer(ILoop)
class loop(AbstractLoop):
# 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