Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/freevo/kaa-base
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirk Meyer committed May 16, 2012
2 parents c1715e3 + 2744df5 commit 928ba20
Show file tree
Hide file tree
Showing 52 changed files with 300 additions and 226 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -1,4 +1,7 @@
ChangeLog
MANIFEST
*.pyc
*.swp
build/
dist/
doc/.build/
Expand Down
7 changes: 0 additions & 7 deletions ChangeLog.in

This file was deleted.

52 changes: 27 additions & 25 deletions doc/async/coroutines.rst
Expand Up @@ -7,44 +7,46 @@
Coroutines
----------

Coroutines are used to break up large and computationally expensive
tasks into smaller tasks, where control is relinquished to the main
loop after each smaller task. Coroutines are also very useful in
constructing state machines. In the event where blocking is
unavoidable, and the duration of the block is unknown (for example,
connecting to a remote host, or scaling a very large image), threads
can be used. These two different approaches are unified with a very
similar API.

A function or method is designated a coroutine by using the @kaa.coroutine
Coroutines are special functions that have multiple entry points that allow
suspending and resuming execution at specified locations. They allow you to:

* write sequentially flowing code involving potentially blocking tasks (e.g.
socket IO) that is actually completely non-blocking
* "time slice" large, computationally expensive tasks to avoid blocking
* help solve complex problems involving state `without using explicit state machines <http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/>`_

In the event where blocking is unavoidable, and the duration of the block is
unknown (for example, connecting to a remote host, or scaling a very large
image), threads can be used. These two different approaches are unified with a
very similar API.

A function or method is designated a coroutine by using the ``@kaa.coroutine``
decorator. A coroutine allows a larger tasks to be broken down into smaller
ones by yielding control back to the "scheduler" (the :ref:`notifier
<notifier>`), implementing a kind of cooperative multitasking. More usefully,
coroutines can yield at points where they may otherwise block on resources
(e.g. disk or network), and when the resource becomes available, the coroutine
resumes where it left off. With coroutines and :ref:`InProgress <inprogress>`
objects, it is possible to construct non-trivial state machines, whose state is
modified by asynchronous events, using a single coroutine. Without coroutines,
this is typically implemented as a series of smaller callback functions. (For
more information on coroutines, see `Wikipedia's treatment of the subject
resumes where it left off. Without coroutines, this is typically implemented
as a series of smaller callback functions. (For more information on
coroutines, see `Wikipedia's treatment of the subject
<http://en.wikipedia.org/wiki/Coroutine>`_.)

Any function decorated with coroutine will return an InProgress object, and the
caller can connect a callback to the InProgress object in order to be notified
of its return value or any exception.
Coroutines return an InProgress object, and the caller can connect a callback
to the InProgress object in order to be notified of its return value or any
exception, or it can yield the InProgress object from other coroutines.

When a coroutine yields kaa.NotFinished, control is returned to the
When a coroutine yields ``kaa.NotFinished``, control is returned to the
main loop, and the coroutine will resume after the yield statement
at the next main loop iteration, or, if an interval is provided with the
decorator, after this time interval. Following the cooperative multitasking
analogy, yielding kaa.NotFinished can be thought of as the coroutine releasing
analogy, yielding ``kaa.NotFinished`` can be thought of as the coroutine releasing
a "time slice" so that other tasks may run.

When a coroutine yields any value other than kaa.NotFinished (including None),
When a coroutine yields any value other than ``kaa.NotFinished`` (including None),
the coroutine is considered finished and the InProgress returned to the caller
will be :ref:`emitted <emitting>` (i.e. it is finished). As with return, if no value is
explicitly yielded and the coroutine terminates, the InProgress is finished
with None.
will be :ref:`emitted <emitting>` (i.e. it is finished). As with return, if no
value is explicitly yielded and the coroutine terminates, the InProgress is
finished with None.

There is an important exception to the above rule: if the coroutine yields an
:class:`~kaa.InProgress` object, the coroutine will be resumed when the
Expand All @@ -53,7 +55,7 @@ other InProgress tasks, including other coroutines.

To recap, if a coroutine yields:

* `kaa.NotFinished`: control is returned to the main loop so that other tasks
* ``kaa.NotFinished``: control is returned to the main loop so that other tasks
can run (such as other timers, I/O handlers, etc.) and resumed on the next
main loop iteration.
* an :class:`~kaa.InProgress` object: control is returned to the main loop and
Expand Down
2 changes: 1 addition & 1 deletion doc/core/signals.rst
Expand Up @@ -181,7 +181,7 @@ object's :attr:`~kaa.Signal.changed_cb` property (or by passing it on the constr
... if action == kaa.Signal.CONNECTED:
... print 'New callback added, signal now has %d' % len(signal)
... else:
... print 'Callback added, signal now has %d' % len(signal)
... print 'Callback removed, signal now has %d' % len(signal)
...
>>> sig = kaa.Signal(changed_cb=signal_changed)
>>> callback = sig.connect(lambda: None)
Expand Down
35 changes: 29 additions & 6 deletions setup.py
Expand Up @@ -2,9 +2,6 @@
# -----------------------------------------------------------------------------
# setup.py - Setup script for kaa.base
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# Copyright 2005-2012 Dirk Meyer, Jason Tackaberry
#
# This library is free software; you can redistribute it and/or modify
Expand All @@ -23,6 +20,9 @@
#
# -----------------------------------------------------------------------------

MODULE = 'base'
VERSION = '0.99.2'

# python imports
import sys
import os
Expand Down Expand Up @@ -103,11 +103,33 @@
if utils_ext.has_python_h():
extensions.append(utils_ext)

# Automagically construct version. If installing from a git clone, the
# version is based on the number of revisions since the last tag. Otherwise,
# if PKG-INFO exists, assume it's a dist tarball and pull the version from
# that.
version = VERSION
if os.path.isdir('.git'):
# Fetch all revisions since last tag. The first item is the current HEAD object name
# and the last is the tag object name.
revs = os.popen('git rev-list --all | grep -B9999 `git rev-list --tags --max-count=1`').read().splitlines()
if len(revs) > 1:
# We're at least one commit past the last tag, so this is considered a
# dev release.
version = '%sdev-%d-%s' % (VERSION, len(revs)-1, revs[0][:8])
elif os.path.isfile('PKG-INFO'):
ver = [l.split(':')[1].strip() for l in open('PKG-INFO') if l.startswith('Version')]
if ver:
version = ver[0]
else:
# Lack of PKG-INFO means installation was not from an official dist bundle,
# so treat it as a development version.
version += 'dev'

setup(
module = 'base',
version = '0.99.2',
module = MODULE,
version = version,
license = 'LGPL',
url = 'http://doc.freevo.org/api/kaa/base/',
url = 'http://api.freevo.org/kaa-base/',
summary = 'An application framework specializing in asynchronous programming.',
description = 'kaa.base is an LGPL-licensed generic application framework, providing the '
'foundation for other modules within Kaa, and can be used in any type of project, '
Expand All @@ -128,6 +150,7 @@
'rpc.py': ['throw'],
}
},
auto_changelog = True,
# Don't declare kaa.base as part of the kaa namespace. Doing so will
# suppress installation of kaa/__init__.py when installing with pip. This
# needs to be installed with kaa.base in order to make our namespace hack
Expand Down
3 changes: 0 additions & 3 deletions src/__init__.py
Expand Up @@ -2,9 +2,6 @@
# -----------------------------------------------------------------------------
# __init__.py - main kaa init module
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# Copyright 2005-2012 Dirk Meyer, Jason Tackaberry
#
# Please see the file AUTHORS for a complete list of authors.
Expand Down
29 changes: 13 additions & 16 deletions src/async.py
Expand Up @@ -2,9 +2,6 @@
# -----------------------------------------------------------------------------
# async.py - Async callback handling (InProgress)
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# kaa.base - The Kaa Application Framework
# Copyright 2006-2012 Dirk Meyer, Jason Tackaberry, et al.
#
Expand Down Expand Up @@ -149,7 +146,7 @@ def percentage(self):
Return percentage of steps done.
"""
if self.max:
return (self.pos * 100) / self.max
return (self.pos * 100.0) / self.max
return 0

@property
Expand Down Expand Up @@ -365,7 +362,7 @@ def finish(self, result):
result passed to this method.
If *result* is an unfinished InProgress, then instead of finishing, we
wait for the result to finish.
wait for the result to finish via :meth:`waitfor`.
:param result: the result of the completed asynchronous task. (This can
be thought of as the return value of the task if it had
Expand Down Expand Up @@ -780,9 +777,8 @@ def __call__(self, *args, **kwargs):

class InProgressAny(InProgress):
"""
InProgress object that finishes when ANY of the supplied InProgress
objects (in constructor) finish. This functionality is useful when
building state machines using coroutines.
InProgress object that finishes when *any* of the supplied InProgress
objects (in constructor) finish.
Sequences or generators passed as arguments will be flattened, allowing
for this idiom::
Expand Down Expand Up @@ -935,14 +931,15 @@ def finish(self, is_exception, index, *result):

class InProgressAll(InProgressAny):
"""
InProgress object that finishes only when ALL of the supplied InProgress
objects (in constructor) finish. This functionality is useful when
building state machines using coroutines.
The InProgressAll object then finishes with itself (which is really only
useful when using the Python 2.5 feature of yield return values). The
finished InProgressAll is useful to fetch the results of the individual
InProgress objects. It can be treated as an iterator, and can be indexed.
InProgress object that finishes only when *all* of the supplied InProgress
objects (in constructor) finish.
The InProgressAll object then finishes with itself. The finished
InProgressAll is useful to fetch the results of the individual InProgress
objects. It can be treated as an iterator, and can be indexed::
for ip in (yield kaa.InProgressAll(sock1.read(), sock2.read())):
print(ip.result)
"""
def __init__(self, *objects):
super(InProgressAll, self).__init__(*objects)
Expand Down
3 changes: 0 additions & 3 deletions src/callable.py
Expand Up @@ -2,9 +2,6 @@
# -----------------------------------------------------------------------------
# callable.py - Callable classes
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# kaa.base - The Kaa Application Framework
# Copyright 2005-2012 Dirk Meyer, Jason Tackaberry, et al.
#
Expand Down
3 changes: 0 additions & 3 deletions src/config.py
Expand Up @@ -2,9 +2,6 @@
# -----------------------------------------------------------------------------
# config.py - config file reader
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# Copyright 2006-2012 Dirk Meyer, Jason Tackaberry
#
# Please see the file AUTHORS for a complete list of authors.
Expand Down
12 changes: 5 additions & 7 deletions src/core.py
Expand Up @@ -2,8 +2,6 @@
# -----------------------------------------------------------------------------
# core.py - provides core functionality needed by most kaa.base modules
# -----------------------------------------------------------------------------
# $Id$
# -----------------------------------------------------------------------------
# kaa.base - The Kaa Application Framework
# Copyright 2010-2012 Dirk Meyer, Jason Tackaberry, et al.
#
Expand Down Expand Up @@ -63,8 +61,8 @@ class CoreThreading:
# this variable in which case it is None.
python_shutting_down = False

# The amount of time the main thread can be blocked while executing
# callbacks that were queued to be invoked from the main loop.
# The amount of time (in seconds) the main thread can be blocked while
# executing callbacks that were queued to be invoked from the main loop.
mainthread_callback_max_time = 2.0

# Internal only attributes.
Expand Down Expand Up @@ -230,15 +228,15 @@ def run_queue(fd):


# It's possible that a thread is actively enqueuing callbacks faster
# than we can dequeue and invoke them. r3583 tried to fix this by
# than we can dequeue and invoke them. 207fc3af77 tried to fix this by
# locking the queue for the entire loop, but this introduced a
# deadlock caused when a thread enqueues a callback while a previously
# queued callback is being invoked and is blocked on a resource used
# by that thread (like a different mutex).
#
# So we don't lock the whole loop to avoid the deadlock, and we stop
# invoking callbacks after mainthread_callback_max_time seconds
# has elapsed in order to solve the problem r3583 tried to fix.
# has elapsed in order to solve the problem 207fc3af77 tried to fix.
#
# Now, there is a large upper bound on the queue to prevent memory
# exhaustion, which means a producer will block if it's adding
Expand All @@ -256,7 +254,7 @@ def run_queue(fd):
# queued callbacks, but we still have more. Poke the thread
# pipe so the next iteration of the main loop calls us back
# and abort the loop.
self._wakeup()
CoreThreading._wakeup()
break

callback, args, kwargs, in_progress = CoreThreading._queue.get()
Expand Down
2 changes: 0 additions & 2 deletions src/coroutine.py
Expand Up @@ -2,8 +2,6 @@
# -----------------------------------------------------------------------------
# coroutine.py - coroutine decorator and helper functions
# -----------------------------------------------------------------------------
# $Id$
#
# This file contains a decorator usefull for functions that may need more
# time to execute and that needs more than one step to fullfill the task.
#
Expand Down
3 changes: 0 additions & 3 deletions src/dateutils.py
Expand Up @@ -2,9 +2,6 @@
# -----------------------------------------------------------------------------
# dateutils.py - Date/time utility functions and objects.
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# Copyright 2009-2012 Dirk Meyer, Jason Tackaberry
#
# Please see the file AUTHORS for a complete list of authors.
Expand Down
7 changes: 2 additions & 5 deletions src/db.py
@@ -1,9 +1,6 @@
# -----------------------------------------------------------------------------
# db.py - db abstraction module
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# Copyright 2006-2012 Dirk Meyer, Jason Tackaberry
#
# Please see the file AUTHORS for a complete list of authors.
Expand Down Expand Up @@ -1959,8 +1956,8 @@ def query_one(self, **attrs):
"""
Like :meth:`~kaa.db.Database.query` but returns a single object only.
This is a convenience method, and query_one(...) is equivalent
to::
This is a convenience method, and query_one(...) is equivalent to::
results = db.query(...)
if results:
obj = results[0]
Expand Down
2 changes: 0 additions & 2 deletions src/distribution/__init__.py
Expand Up @@ -2,8 +2,6 @@
# -----------------------------------------------------------------------------
# __init__.py - kaa.distribution module initialization
# -----------------------------------------------------------------------------
# $Id$
# -----------------------------------------------------------------------------
# Copyright 2006-2012 Dirk Meyer, Jason Tackaberry
#
# Please see the file AUTHORS for a complete list of authors.
Expand Down
3 changes: 0 additions & 3 deletions src/distribution/build_py.py
Expand Up @@ -2,9 +2,6 @@
# -----------------------------------------------------------------------------
# build_py.py - kaa.config cxml install support
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# Copyright 2006-2012 Dirk Meyer, Jason Tackaberry
#
# Please see the file AUTHORS for a complete list of authors.
Expand Down

0 comments on commit 928ba20

Please sign in to comment.