Skip to content

Commit

Permalink
fix(docs): add proper docstring documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
h2non committed Oct 23, 2016
1 parent a2b9d35 commit 920ecc4
Show file tree
Hide file tree
Showing 29 changed files with 272 additions and 209 deletions.
2 changes: 1 addition & 1 deletion Makefile
@@ -1,7 +1,7 @@
OK_COLOR=\033[32;01m
NO_COLOR=\033[0m

all: lint unit
all: test

export PYTHONPATH:=${PWD}
version=`python -c 'import paco; print(paco.version)'`
Expand Down
21 changes: 13 additions & 8 deletions README.rst
Expand Up @@ -11,14 +11,16 @@ Note: pyco is still beta.
Features
--------

- Simple and idiomatic API.
- Simple and idiomatic API, extending Python `stdlib` with async coroutines gotchas.
- Built-in configurable control-flow concurrency support.
- Built-in iterables and functors.
- Compose, throttle, partial, until, race and other functional helpers for coroutines.
- Useful iterables, decorators and functors.
- Provides coroutine-ready compose, throttle, partial, until, race and other functional helpers.
- Asynchronous coroutine port of Python built-in functions: `filter`, `map`, `dropwhile`, `filterfalse`, `reduce`...
- Coroutines control flow and higher-order functions goodness.
- Better `asyncio.gather()` and `asyncio.wait()` implementations with optional concurrency control and ordered results.
- Good interoperability with `asyncio` and Python `stdlib` functions.
- Ports Python stdlib higher-order functions and iterables for the coroutines world.
- Works with `async/await`_ and `yield from`_ coroutines syntax.
- Partially ports Python `stdlib` higher-order functions and iterables to be used in async coroutines world.
- Works with both `async/await`_ and `yield from`_ coroutines syntax.
- Small and dependency free.
- Compatible with Python +3.4.

Expand Down Expand Up @@ -99,7 +101,10 @@ API
.. _pyco.concurrent: http://pyco.readthedocs.io/api.html#pycoconcurrent
.. _pyco.ConcurrentExecutor: http://pyco.readthedocs.io/api.html#pycoConcurrentExecutor

Example:
Examples
^^^^^^^^

Asynchronously execute multiple HTTP requests concurrently.

.. code-block:: python
Expand All @@ -121,11 +126,11 @@ Example:
'https://duckduckgo.com',
]
# Map concurrent executor with 3 concurrent limit
# Map concurrent executor with concurrent limit of 3
responses = await pyco.map(fetch, urls, limit=3)
for res in responses:
print('Response:', res.status)
print('Status:', res.status)
loop = asyncio.get_event_loop()
loop.run_until_complete(fetch_urls())
Expand Down
6 changes: 5 additions & 1 deletion pyco/apply.py
Expand Up @@ -14,11 +14,15 @@ def apply(coro, *args, **kw):
Any arguments passed to the returned function are added to the arguments
originally passed to apply.
This is similar to partial.
This is similar to `pyco.partial()`.
This function can be used as decorator.
arguments:
coro (coroutinefunction): coroutine function to wrap.
*args (mixed): mixed variadic arguments for partial application.
*kwargs (mixed): mixed variadic keyword arguments for partial
application.
Raises:
TypeError: if coro argument is not a coroutine function.
Expand Down
8 changes: 4 additions & 4 deletions pyco/compose.py
Expand Up @@ -13,8 +13,8 @@ def compose(*coros):
Composing coroutine functions f(), g(), and h() would produce
the result of f(g(h())).
arguments:
*coros (coroutinefunction): coroutine function to compose.
Arguments:
*coros (coroutinefunction): variadic coroutine functions to compose.
Raises:
RuntimeError: if cannot execute a coroutine function.
Expand All @@ -24,8 +24,8 @@ def compose(*coros):
Usage::
coro = pyco.compose(sum, mul, sum)
coro(1)
coro = pyco.compose(sum1, mul2, sum1)
await coro(1)
=> 5
"""
# Make list to inherit built-in type methods
Expand Down
26 changes: 19 additions & 7 deletions pyco/concurrent.py
Expand Up @@ -25,6 +25,10 @@ async def fetch(url):

@asyncio.coroutine
def safe_run(coro, return_exceptions=False):
"""
Executes a given coroutine and optionally catches exceptions, returning
them as value. This function is intended to be used internally.
"""
try:
result = yield from coro
except Exception as err:
Expand All @@ -39,6 +43,10 @@ def safe_run(coro, return_exceptions=False):
def collect(coro, index, results,
preserve_order=False,
return_exceptions=False):
"""
Collect is used internally to execute coroutines and collect the returned
value. This function is intended to be used internally.
"""
result = yield from safe_run(coro, return_exceptions=return_exceptions)

if preserve_order:
Expand All @@ -52,12 +60,16 @@ class ConcurrentExecutor(object):
Concurrent executes a set of asynchronous coroutines
with a simple throttle concurrency configurable concurrency limit.
Implements an observer pub/sub interface, allowing API consumers to
subscribe functions or coroutines to events.
Provides an observer pub/sub interface, allowing API consumers to
subscribe normal functions or coroutines to certain events that happen
internally.
ConcurrentExecutor is a low-level implementation that powers most of the
utility functions provided in `pyco`.
ConcurrentExecutor is a low-level implementation that powers .
For most cases you won't need to rely on it, instead you can
use the utility functions that provides a higher and simpler abstraction.
use the high-level API functions that provides a simpler abstraction for
the majority of the use cases.
This class is not thread safe.
Expand Down Expand Up @@ -93,7 +105,7 @@ def __init__(self, limit=10, loop=None, coros=None):
self.pool = deque()
self.observer = Observer()
self.loop = loop or asyncio.get_event_loop()
self.throttler = asyncio.Semaphore(self.limit, loop=self.loop)
self.semaphore = asyncio.Semaphore(self.limit, loop=self.loop)

# Register coroutines in the pool
if isiter(coros):
Expand All @@ -111,7 +123,7 @@ def reset(self):

self.pool.clear()
self.observer.clear()
self.throttler = asyncio.Semaphore(self.limit, loop=self.loop)
self.semaphore = asyncio.Semaphore(self.limit, loop=self.loop)

def cancel(self):
"""
Expand Down Expand Up @@ -263,7 +275,7 @@ def _schedule_coro(self, task):
scheduling semaphore-based algorithm.
"""
# Run when a slot is available
with (yield from self.throttler):
with (yield from self.semaphore):
return (yield from self._run_coro(task))

@asyncio.coroutine
Expand Down
4 changes: 2 additions & 2 deletions pyco/constant.py
Expand Up @@ -7,8 +7,8 @@ def constant(value, delay=None):
Returns a coroutine function that when called, always returns
the provided value.
arguments:
value (mixed): value to constantly return.
Arguments:
value (mixed): value to constantly return when coroutine is called.
delay (int/float): optional return value delay in seconds.
Returns:
Expand Down
9 changes: 5 additions & 4 deletions pyco/decorator.py
Expand Up @@ -8,6 +8,8 @@ def decorate(fn):
Generic decorator for coroutines helper functions allowing
multiple variadic initialization arguments.
This function is intended to be used internally.
Arguments:
fn (function): target function to decorate.
Expand All @@ -31,10 +33,9 @@ def decorator(*args, **kw):

def wrapper(coro, *_args, **_kw):
assert_corofunction(coro=coro)
# Merge call arguments
_args = ((coro,) + (args + _args))
kwargs = kw.copy()
kwargs.update(_kw)
return fn(*_args, **kwargs)
kw.update(_kw)
return fn(*_args, **kw)
return wrapper

return decorator
29 changes: 13 additions & 16 deletions pyco/defer.py
@@ -1,27 +1,20 @@
# -*- coding: utf-8 -*-
import asyncio
from .decorator import decorate
from .assertions import assert_corofunction


@decorate
def defer(coro, seconds=None):
def defer(coro, delay=1):
"""
Make an iterator that drops elements from the iterable as long as the
predicate is true; afterwards, returns every element.
Note, the iterator does not produce any output until the predicate
first becomes false, so it may have a lengthy start-up time.
Returns a coroutine function wrapper that will defer the given coroutine
execution for a certain amount of seconds in a non-blocking way.
This function implements the same interface as Python standard
`itertools.dropwhile()` function.
All coroutines will be executed in the same loop.
This function can be used as decorator.
Arguments:
coro (coroutine function): coroutine function to call with values
to reduce.
iterable (iterable): an iterable collection yielding
coroutines functions.
loop (asyncio.BaseEventLoop): optional event loop to use.
coro (coroutinefunction): coroutine function to defer.
delay (int/float): number of seconds to defer execution.
Raises:
TypeError: if coro argument is not a coroutine function.
Expand All @@ -31,19 +24,23 @@ def defer(coro, seconds=None):
Usage::
# Usage as function
await pyco.defer(coro, 1)
await pyco.defer(coro, 0.5)
@pyco.defer(seconds=1)
# Usage as decorator
@pyco.defer(delay=1)
async def task(n):
return n * n
await task(2)
=> 4
"""
assert_corofunction(coro=coro)

@asyncio.coroutine
def wrapper(*args, **kw):
# Wait until we're done
yield from asyncio.sleep(seconds)
yield from asyncio.sleep(delay)
return (yield from coro(*args, **kw))

return wrapper
15 changes: 10 additions & 5 deletions pyco/dropwhile.py
Expand Up @@ -8,11 +8,12 @@ def dropwhile(coro, iterable, loop=None):
"""
Make an iterator that drops elements from the iterable as long as the
predicate is true; afterwards, returns every element.
Note, the iterator does not produce any output until the predicate
first becomes false, so it may have a lengthy start-up time.
This function implements the same interface as Python standard
`itertools.dropwhile()` function.
Note, the iterator does not produce any output until the predicate first
becomes false, so it may have a lengthy start-up time.
This function is pretty much equivalent to Python standard
`itertools.dropwhile()`, but designed to be used with async coroutines.
This function is a coroutine.
Expand All @@ -31,7 +32,11 @@ def dropwhile(coro, iterable, loop=None):
Usage::
await pyco.dropwhile(coro, [1, 2, 3, 4, 5])
async def filter(num):
return num < 4
await pyco.dropwhile(filter, [1, 2, 3, 4, 5, 1])
=> [4, 5, 1]
"""
drop = False

Expand Down
23 changes: 17 additions & 6 deletions pyco/each.py
Expand Up @@ -12,7 +12,7 @@ def each(coro, iterable, limit=0, loop=None,
an asynchronous coroutine.
You can optionally collect yielded values passing collect=True param,
which would be equivalent to .map().
which would be equivalent to `pyco.map()``.
Mapped values will be returned as an ordered list.
Items order is preserved based on origin iterable order.
Expand All @@ -21,27 +21,38 @@ def each(coro, iterable, limit=0, loop=None,
All coroutines will be executed in the same loop.
This function is not thread safe.
This function is a coroutine.
Arguments:
coro (coroutinefunction): coroutine iterator function that accepts
iterable values.
iterable (iter): an iterable collection yielding
coroutines functions. Asynchronous iterables are not supported.
limit (int): max concurrency execution limit.
limit (int): max iteration concurrency limit. Use ``0`` for no limit.
collect (bool): return yielded values from coroutines. Default False.
loop (asyncio.BaseEventLoop): optional event loop to use.
return_exceptions (bool): enable/disable returning exceptions in case
of error. `collect` param must be True.
timeout (int|float): timeout can be used to control the maximum number
of seconds to wait before returning. timeout can be an int or
float. If timeout is not specified or None, there is no limit to
the wait time.
*args (mixed): optional variadic argument to pass to the
coroutines function.
*args (mixed): optional variadic arguments to pass to the
coroutine iterable function.
Returns:
results (list): ordered list of values yielded by coroutines
Raises:
TypeError: in case of invalid input arguments.
Usage::
await pyco.each(pow, [1, 2, 3, 4, 5], limit=3)
async def mul2(num):
return mul * 2
await pyco.each(mul2, [1, 2, 3, 4, 5], limit=3)
=> [2, 4, 6, 8, 10]
"""
assert_corofunction(coro=coro)
assert_iter(iterable=iterable)
Expand Down

0 comments on commit 920ecc4

Please sign in to comment.