Commits

Anonymous committed 1a2e0c9

moving from google-code to git hub

  • Participants

Comments (0)

Files changed (112)

+0.3.2
+- added support for POST to HTTP Client
+- removed profile function, added profile cmdline argument
+- changed Tasklet.yield_() to use sleep(0.0) instead of stackless.schedule in order to give other IO bound tasks a change to run
+- added application support (IOC container) (currently undocumented)
+- refactored pool and sqlalchemy pool adaptor to include 'nullpool'.
+
+0.3.1
+- now uses standard python EOFError 
+- improved implementation of CompatibleFile (reader.file(), writer.file())
+- no longer imports greenlets from py.lib as they don't provide it anymore, now imports from greenlet packet. updated INSTALL documentation.
+- documentation updates
+- fixed compilation errors and unittest for OSX
+- http client now sends concurrence verion in AGENT string
+- fixed issue #6 (HTTPResponse.get_header did not return default, but None when header not found)
+
+0.3
+---
+- renamed API buffer.read_bytes_until_crlf -> buffer.read_line
+- changed HTTP client API
+- changed concurrence.web API: @web(path = 'status') -> @web.route('/status')
+- added filter decorator to concurrence.web: @web.filter(MyFilter())
+- added controller filters to concurrence.web
+- added TCP_NODELAY to all tcp sockets by default
+- documentation updates
+        
+
+Installing Concurrence
+======================
+
+The quick and easy way
+----------------------
+Here we assume you want install Concurrence for use with 'Normal Python' as opposed to 'Stackless Python'. 
+This is the easiest way to get started with Concurrence. Later you may want to try out Stackless because it
+delivers a bit better performance (typicaly about 10-25% faster).
+We will be using `setup tools <http://pypi.python.org/pypi/setuptools>`_ to install all dependencies.
+
+Note that Linux users might need to make sure they are running as root, or prefix the commands with 'sudo'.
+
+First we need to install `Pyrex <http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex>`_::
+
+    easy_install pyrex 
+
+Next we will install concurrence::
+
+    easy_install concurrence
+
+If might give an error: Setup script exited with error: SandboxViolation: ... 
+This is a known problem with Pyrex, please do::
+
+    python -c "from Pyrex.Compiler import Scanning;Scanning.get_lexicon()"
+
+And then try install concurrence again
+
+Finally we need to install the `Greenlet package <http://pypi.python.org/pypi/greenlet>`_:: 
+
+    easy_install greenlet
+
+If you run into compiler problems on Linux, this is most probably caused by missing development headers
+for Python and or libevent. On Ubuntu these packages can be installed using::
+
+    apt-get install python-dev
+    apt-get install libevent-dev
+
+If you run into compiler problems on OSX, this is most probably casued by missing libevent and or its headers
+
+   1. `Download latest stable release of libevent <http://www.monkey.org/~provos/libevent>`_.
+   2. Unzip and cd into the libevent directory
+   3. Run ./configure && make
+   4. Run sudo make install
+   5. Try installing concurrence again
+
+That should be it!, you should now be able to start playing with the examples
+
+Optional dependencies
+---------------------
+
+If you want to use the simple web framework included with Concurrence, you need to install
+`Routes <http://routes.groovie.org>`_ and `WebOb <http://pythonpaste.org/webob>`_::
+
+    easy_install routes  
+    easy_install webob
+
+If you use Python2.5, you might want to install 'simplejson' as well::
+
+    easy_install simplejson
+
+
+Running the examples
+--------------------
+First make sure you have the source distibution of Concurrence::
+
+    svn checkout http://concurrence.googlecode.com/svn/trunk/ concurrence
+
+The examples can be found in the examples directory and are documented on the Concurrence
+website at http://opensource.hyves.org/concurrence
+
+Installing stackless
+--------------------
+If you want to run Concurrence on top of stackless (which is a bit faster), you will need to
+install Stackless Python.
+Download the latest stackless tarball from www.stackless.com. unpack it somewhere (/tmp/stackless), and execute
+the folowing commands:
+
+UBUNTU:
+Make sure you have <libreadline-dev> package. Otherwise you have no history support in the python interactive shell
+(on ubuntu: apt-get install libreadline-dev)
+
+We will install stackless in /opt/stackless using the prefix argument to configure. This will prevent any existing python installation
+from being overwritten::
+
+    UBUNTU:
+	    ./configure --prefix=/opt/stackless --with-readline --with-zlib=/usr/include
+	    make
+	    make install
+
+    OSX (Leopard):
+	    ./configure --prefix=/opt/stackless --enable-framework --enable-stacklessfewerregisters --with-readline --with-zlib=/usr/include
+	    echo '#define SETPGRP_HAVE_ARG' >> pyconfig.h
+	    make
+	    make install
+
+I always like to make a symlink to stackless for everyday usage and to differentiate it from 'normal' python:
+ 
+ln -s /opt/stackless/bin/python2.5 /usr/bin/stackless
+
+
+
+Copyright (C) 2009, Hyves (Startphone Ltd.)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    * Neither the name of Hyves (Startphone Ltd.) nor the names of its
+      contributors may be used to endorse or promote products derived from this
+      software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+

File LICENSE.pyevent

+Copyright (c) 2004 Dug Song <dugsong@monkey.org>
+Copyright (c) 2003 Martin Murray <murrayma@citi.umich.edu>
+All rights reserved, all wrongs reversed.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. The names of the authors and copyright holders may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+build:
+	$(PYTHON) setup.py build
+
+ext:
+	$(PYTHON) setup.py build_ext --inplace
+	
+egg:
+	$(PYTHON) setup.py bdist_egg
+				
+install:
+	$(PYTHON) setup.py install
+
+sdist:
+	$(PYTHON) setup.py sdist
+
+_doc:
+	cd doc; make html
+ 
+doc: _doc
+
+clean:
+	-find . -name *.pyc -exec rm -rf {} \;
+	-find . -name *.so -exec rm -rf {} \;
+	-find . -name *.dep -exec rm -rf {} \;
+	-find . -name *~ -exec rm -rf {} \;
+	-find . -name *.egg-info -exec rm -rf {} \;
+	rm -rf setuptools*.egg
+	rm -rf doc/_build
+	rm -rf build dist
+	rm -rf lib/concurrence/database/mysql/concurrence.database.mysql._mysql.c
+	rm -rf lib/concurrence/concurrence._event.c
+	rm -rf lib/concurrence/io/concurrence.io._io.c
+	
+dist_clean: clean
+	find . -name .svn -exec rm -rf {} \;
+
+_test: 
+	cd test; make test
+
+test: _test 
+This is the README for the Concurrence Framework.
+
+For more information, please refer to http://opensource.hyves.org/concurrence
+
+Please refer to INSTALL.TXT for instructions on installing Concurrence
+
+if you want to run the tests please type
+
+PYTHON=python make test
+
+or
+
+PYTHON=stackless make test
+
+If you want the mysql tests to succeed, you need to have mysql installed
+and you have to create the concurrence mysql test database (from the test directory):
+
+mysqladmin create concurrence_test -u root -p
+cat concurrence_test.sql | mysql -u root -p
+
+

File doc/Makefile

+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview over all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+
+clean:
+	-rm -rf _build/*
+
+html:
+	mkdir -p _build/html _build/doctrees
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
+	@echo
+	@echo "Build finished. The HTML pages are in _build/html."
+
+pickle:
+	mkdir -p _build/pickle _build/doctrees
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+web: pickle
+
+json:
+	mkdir -p _build/json _build/doctrees
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	mkdir -p _build/htmlhelp _build/doctrees
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in _build/htmlhelp."
+
+latex:
+	mkdir -p _build/latex _build/doctrees
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in _build/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	mkdir -p _build/changes _build/doctrees
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
+	@echo
+	@echo "The overview file is in _build/changes."
+
+linkcheck:
+	mkdir -p _build/linkcheck _build/doctrees
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in _build/linkcheck/output.txt."

File doc/_static/hyves-logo.png

Added
New image

File doc/_static/hyves.css

+a {
+	color: #4C6072;
+}
+
+div.sphinxsidebar h3 {
+	color: #4C6072;
+}
+
+div.sphinxsidebar h4 {
+	color: #4C6072;
+}
+
+div.sphinxsidebar p {
+	color: #4C6072;
+}
+
+div.sphinxsidebar ul {
+	color: #4C6072;
+	list-style-image:none;
+	list-style-position:outside;
+	list-style-type:none;
+	margin:10px;
+	padding:0pt;
+}
+
+div.sphinxsidebar a {
+	color: #4C6072;
+}
+
+div.sphinxsidebar h3 a {
+	color: #4C6072;
+}
+
+div.sphinxsidebar input {
+	border:1px solid #6699CC;
+}
+
+body {
+	background-color: #6699CC;
+}
+
+div.document {
+	background-color: #EBF2FA;
+}
+
+div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 {
+	background-color: #ffffff;
+	border-bottom:0px solid #CCCCCC;
+	border-top:0px solid #CCCCCC;
+	color:#6699CC;
+	font-family:'Trebuchet MS',sans-serif;
+	font-weight:normal;
+	margin:20px -20px 10px;
+	padding:3px 0pt 3px 10px;
+}
+
+div.body h1 {
+	font-size:185%;
+	margin-top:0pt;
+	background-color: #EBF2FA;
+	color: #4C6072;
+	border-bottom:1px solid #CCCCCC;
+	border-top: 0px solid #CCCCCC;
+}
+
+div.header {
+	background: url(page-hdr-dflt.png);
+}
+
+div.related {
+	background-color:#6699CC;
+	color:#FFFFFF;
+	font-size:90%;
+	line-height:30px;
+	width:100%;
+}

File doc/_static/page-hdr-dflt.png

Added
New image

File doc/_templates/layout.html

+{% extends "!layout.html" %}
+
+{%- block extrahead %}
+<link rel="stylesheet" href="{{ pathto('_static/hyves.css', 1) }}" type="text/css" />
+{% endblock %}
+
+{%- block relbar1 %}
+<div class='header'>
+<a href="{{ pathto(master_doc) }}">
+<img class="logo" src="{{ pathto('_static/hyves-logo.png', 1) }}" alt="Logo"/>
+</a>
+</div>
+{{ super() }}
+{% endblock %}
+
+.. _api:
+
+API Documentation
+=================
+
+.. toctree::
+	:maxdepth: 1
+	
+	concurrence.core
+	concurrence.io
+	concurrence.timer
+	concurrence.http
+	concurrence.database.mysql.client
+	concurrence.web
+

File doc/concurrence.core.rst

+:mod:`concurrence.core` -- The concurrence core module
+======================================================
+
+.. module:: concurrence.core
+   :platform: Unix
+   :synopsis: Provides the basic abstractions of the Concurrence Framework.
+.. moduleauthor:: Henk Punt <henk@hyves.nl>
+
+.. autofunction:: dispatch
+.. autofunction:: quit
+ 
+.. autoclass:: Tasklet
+   :members:
+   
+.. autoclass:: Channel
+   :members:
+
+.. autoclass:: Message
+   :members:
+
+.. autoclass:: Mailbox
+   :members: pop, popleft, append, appendleft
+   
+   
+.. autoexception:: JoinError
+
+.. autoexception:: TimeoutError
+
+

File doc/concurrence.database.mysql.client.rst

+:mod:`concurrence.database.mysql.client` -- The concurrence mysql driver module
+===============================================================================
+
+.. module:: concurrence.database.mysql.client
+   :platform: Unix
+   :synopsis: Provides the low-level Concurrence MySQL client.
+.. moduleauthor:: Henk Punt <henk@hyves.nl>
+ 
+.. autoclass:: Connection
+   :members:
+   
+.. autoclass:: ResultSet
+   :members:
+      
+   

File doc/concurrence.http.rst

+:mod:`concurrence.http` -- The concurrence http module
+===============================================================
+
+.. module:: concurrence.http
+   :platform: Unix
+   :synopsis: Provides a HTTP1.1 client and server
+.. moduleauthor:: Henk Punt <henk@hyves.nl>
+ 
+.. autoclass:: WSGIServer
+   :members:
+   
+.. autoclass:: HTTPConnection
+   :members:
+
+.. autoclass:: HTTPRequest
+   :members:
+
+.. autoclass:: HTTPResponse
+   :members:
+   

File doc/concurrence.io.rst

+:mod:`concurrence.io` -- The concurrence io module
+==================================================
+
+.. module:: concurrence.io
+   :platform: Unix
+   :synopsis: Provides the basic IO facilities of the Concurrence Framework.
+.. moduleauthor:: Henk Punt <henk@hyves.nl>
+
+.. autoclass:: Buffer
+    :members:
+
+    .. automethod:: copy(src, src_start, dst_start, length)
+
+.. autoclass:: Socket
+    :members:
+   
+.. autoclass:: SocketServer
+    :members:
+   
+.. autoclass:: BufferedStream
+    :members:   
+   
+
+   
+
+
+
+
+

File doc/concurrence.timer.rst

+:mod:`concurrence.timer` -- A timer module
+==================================================
+
+.. module:: concurrence.timer
+   :platform: Unix
+   :synopsis: A task based Timeout timer
+.. moduleauthor:: Henk Punt <henk@hyves.nl>
+ 
+.. autoclass:: Timeout
+   :members:
+   
+   
+   

File doc/concurrence.web.rst

+:mod:`concurrence.web` -- The concurrence web module
+====================================================
+
+.. module:: concurrence.web
+   :platform: Unix
+   :synopsis: Provides a simple Web framework for build web-appications at a higher abstraction level than WSGI.
+.. moduleauthor:: Henk Punt <henk@hyves.nl>
+ 
+.. autoclass:: Application
+   :members:
+   
+.. autoclass:: Filter
+   :members:
+   
+.. autoclass:: Controller
+   :members:   
+
+   
+   
+# -*- coding: utf-8 -*-
+#
+# Concurrence documentation build configuration file, created by
+# sphinx-quickstart on Wed Dec 24 14:40:01 2008.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# General configuration
+# ---------------------
+
+autoclass_content = "both"
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+source_encoding = 'latin-1'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Concurrence Framework'
+copyright = u'Copyright (C) 2009, Hyves (Startphone Ltd.)'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+from concurrence import __version__
+
+version = __version__
+# The full version, including alpha/beta/rc tags.
+release = __version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+html_style = 'default.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Concurrencedoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+  ('index', 'Concurrence.tex', ur'Concurrence Documentation',
+   ur'Henk Punt', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True

File doc/examples.rst

+.. Examples
+
+Examples
+===========
+
+A collection examples on how to use the Concurrence Framework
+
+A Simple Chat Server
+--------------------
+
+In an example of how Tasks and Messages are typically used together.
+This example implements a simple multi-user chat server:
+
+.. literalinclude:: ../examples/chat.py
+	:linenos:
+	
+You can start it with ``stackless chat.py`` and then start 1 or more sessions using ``telnet localhost 9010``. 
+If you type some message in one session, it will be multi-cast to all other currently connected sessions.
+A quick overview of the code:
+
+	* First a new tcp 'server' is started. 
+	* For each incoming connection a new client task is created that will execute the 'handle' function.
+	* The client task will in turn start 2 child tasks; 'reader' and 'writer' that are responsible for reading and writing lines from/to the corresponding client.
+	* The client task then will enter a 'receive' loop and repond to messages until it receives the quit message.
+ 	* Coorperation between the tasks is handled by the 3 messages defined at the top
+	* Incoming lines are read by 'reader', it messages the client_task, which in turn multi-casts the line to all other connected client_tasks. 
+ 	 
+	
+  

File doc/http.rst

+.. _http:
+
+HTTP
+====
+
+Server
+------
+
+.. literalinclude:: ../examples/http.py
+
+
+Client
+------
+
+A Simple HTTP request.
+
+.. literalinclude:: ../examples/http_client.py
+    
+
+Multiple HTTP requests using `Pipelining <http://en.wikipedia.org/wiki/HTTP_pipelining>`_.
+
+.. literalinclude:: ../examples/http_client_pipeline.py
+
+

File doc/index.rst

+.. _index:
+
+Concurrence
+===========
+
+Concurrence is a framework for creating massively concurrent network applications in Python.
+
+It takes a `Lightweight-tasks-with-message-passing` approach to concurrency. 
+
+The goal of Concurrence is to provide an easier programming model for writing high performance network applications than existing solutions (Multi-threading, Twisted, asyncore etc). 
+
+Concurrence uses `Lightweight tasks` in combination with `libevent <http://monkey.org/~provos/libevent/>`_ to expose a high-level synchronous API to low-level asynchronous IO.
+
+Some features:
+
+  * Lightweight Tasks as the basic unit of execution.
+  * Message passing as the basic unit of communication (between tasks).
+  * Cooperative scheduling of tasks triggered by IO events and Timeouts.
+  * Libevent based, always uses the most optimal asynchronous IO multiplexer for the given platform (epoll on linux, KQueue on BSD/OSX).
+  * Fast low-level IO buffers implemented in Pyrex.
+  * Socket API.
+  * DBAPI 2.0 compatible MySQL driver implementation (native & asynchronous, with optimized protocol support written in Pyrex).
+  * HTTP / WSGI Server.
+  * HTTP Client.
+  * Timeouts. All blocking API's provide a timeout parameter.
+  * Remoting. Message passing between tasks in diffent processes.
+  
+.. note::
+	Concurrence requires python support for tasklets and channels. These can be provided by either 'Stackless Python' or 'Python + Greenlets'. The latter is easier to work with as it only requires the greenlets package be installed. Stackless Python runs Concurrence programs a bit faster, but the difference is quite small (10-25%) compared to normal Python + Greenlets.	  
+
+Hello World
+----------- 
+
+This example can be found in examples/hello.py.
+
+.. literalinclude:: ../examples/hello.py
+
+The basic unit of execution in Concurrence is the 'Tasklet' or 'task'.
+
+Every concurrence program starts with a `dispatch` loop.
+
+The dispatch loop will run forever. It waits
+for events (IO, timers, etc) and (re)schedules tasks in response to those events. 
+
+As a convenience the dispatch function takes a 'callable' parameter and will 
+start a new task to execute it. 
+
+In the above example 2 tasks are present, e.g. the 'main' task that runs the
+dispatch loop (forever), and an (anonymous) task that will execute the 'hello' function and
+then exit.
+
+.. note::
+	The program will not exit when the 'hello' function is finished. 
+	This is because the main task will continue to run in the dispatch loop. 
+	You can stop the program with Ctrl-C.
+
+
+A Highly Concurrent Greeting
+----------------------------
+The above example is rather boring. In this section we will introduce a variation
+that is designed to show off the capabilities of the Concurrence framework for easily
+creating highly concurrent network applications:
+
+.. literalinclude:: ../examples/greeter.py
+
+Somebody who is familiar with Socket programming will probably recognize the function of this little program;
+
+It implements a very minimal webserver that outputs the traditional greeting.
+You can start this program and point your webbrowser to ``http://localhost:8080`` to see it.
+
+The ``server`` task first creates a `server socket` on port 8080. Then it will loop forever and ``accept`` connections
+coming in trough the socket. When a client connection is accepted, a new task is started to handle it. 
+The server task itself will continue and wait for more connections to arrive.
+
+The remarkable thing about this example program is that it is able to serve `many thousands of requests per second
+to thousands of concurrently connected clients`::
+    
+    Benchmark of greeter.py (running with stackless python). 
+    In this session it handled about 8300 request per second on 1000 concurrent connections:
+
+	httperf --uri=/test --port=8080 --num-calls=1 --num-conns=100000 --rate=10000
+	httperf --client=0/1 --server=localhost --port=8080 --uri=/test --rate=10000 
+            --send-buffer=4096 --recv-buffer=16384 --num-conns=100000 --num-calls=1
+	
+	Total: connections 98506 requests 98506 replies 98506 test-duration 11.815 s
+	
+	Connection rate: >> 8337.1 << conn/s (0.1 ms/conn, <=1022 concurrent connections)
+	...	
+	Request rate: >> 8337.1 req/s << (0.1 ms/req)
+	Request size [B]: 64.0
+	
+	Reply rate [replies/s]: min 9707.9 avg 9850.2 max 9992.5 stddev 201.3 (2 samples)
+	Reply time [ms]: response 8.0 transfer 0.0
+	...
+
+
+The reason for this is that at it's core this program will use `asynchronous IO` ('non-blocking') instead of
+more traditional `blocking IO + threads`. The nice thing about using the Concurrence framework is that the complexity normally associated
+with asynchronous IO is not visible to the programmer. Concurrence presents a familiar blocking IO model, 
+while at the same time achieving high concurrency and low latency by using asynchronous IO in the background.
+
+.. note::
+	This is just a simple example of a http server. Concurrence provides a more feature complete HTTP/1.1 webserver (:class:`~concurrence.http.WSGIServer`)
+	that exposes a WSGI Interface for creating web-applications. 
+
+Download & Installation
+-----------------------
+
+Installation instructions can be found here: :ref:`install`.
+
+Concurrence is available from the `Python package index (PyPI) <http://pypi.python.org/pypi/concurrence>`_.
+
+Source code & Issues
+--------------------
+
+The Concurrence Framework source code and issue tracker are hosted at `Google code <http://concurrence.googlecode.com>`_.
+
+
+Documentation
+-------------
+
+.. toctree::
+	:maxdepth: 1
+	
+	tasklets
+	messages
+	http
+	web
+	examples
+	install
+	api
+
+API Reference
+-------------
+.. toctree::
+	:maxdepth: 1
+
+	concurrence.core
+	concurrence.io
+	concurrence.timer
+	concurrence.http
+	concurrence.database.mysql.client
+	concurrence.web
+
+
+
+
+  	

File doc/install.rst

+.. _install:
+
+.. include:: ../INSTALL.TXT

File doc/messages.rst

+.. Messages.
+
+Messages
+========
+
+The basic unit of *execution* in the Concurrence framework is the :class:`~concurrence.core.Tasklet`.
+
+The basic unit of *communication* between tasks is the :class:`~concurrence.core.Message`.
+
+A message is defined like this::
+
+	class MSG_XXX(Message): pass
+
+.. note::
+    By convention, Messages should have UPPERCASE names that start with `MSG_` .
+
+Every Tasklet has a :attr:`~concurrence.core.Tasklet.mailbox` where it receives messages from other tasks.
+
+A Tasklet receives and processes pending messages using the following pattern::
+	
+    for msg, args, kwargs in Tasklet.receive():
+        if msg.match(MSG_XXX):
+            ... 
+        elif msg.match(MSG_YYY):
+            ...
+        else:
+            ...
+
+The Tasklet will call the :func:`~concurrence.core.Tasklet.receive` iterator in order to receive any pending messages. 
+If there are no pending messages, the Tasklet will block until a message arrives. Each message is accompanied by a tuple *args* of positional
+arguments and a dictionary *kwargs* of named arguments, both of which may be empty.
+The Tasklet will then determine what to do by matching the *msg* using the :func:`~concurrence.core.Message.match` method.
+
+An example of using messages to communicate between tasks:
+	
+.. literalinclude:: ../examples/message.py
+
+In this example the ``main`` task starts a new task ``printer`` that forever listens for messages using the :func:`~concurrence.core.Tasklet.receive` iterator.
+
+The main task then sends 2 messages to the printer task, which will respond by printing the appropriate message to the console.
+
+.. note::
+	Messages by default are 'asynchronous' e.g., the sender does not wait for the receiver task to finish processing it.  
+	There is also support for 'synchronous' messages by using the :func:`~concurrence.core.Message.call` method of the Message class. 
+	In that case, the *receiver* will have to :func:`~concurrence.core.Message.reply` to 
+	the incoming message and the *caller* will block until the reply has been received.
+	   
+
+	
+
+	

File doc/tasklets.rst

+.. Tasklet documentation.
+
+Tasklets
+========
+
+The basic unit of execution in the Concurrence Framework is the :class:`~concurrence.core.Tasklet`.
+The full source documentation for tasklets can be found in :mod:`concurrence.core`. Examples
+are documented below
+
+Starting a new task
+-------------------
+
+
+You can start a new task by calling :func:`~concurrence.core.Tasklet.new`::
+
+	from concurrence import dispatch, Tasklet
+	
+	def greeting(msg):
+	    print msg
+	
+	def start():
+	    
+	    Tasklet.new(greeting)("Hello")
+	    Tasklet.new(greeting)("World")
+	    
+	    print 'start done.'
+	
+	if __name__ == '__main__':
+	    dispatch(start)  
+
+
+This should print the following to the console::
+
+	start done.
+	Hello
+	World
+
+This output is explained as follows:
+
+The dispatcher will create a new task for the start function. The start function
+will itself create 2 new tasks based on the greeting function.
+The :func:`~concurrence.core.Tasklet.new` call is not blocking, so the start function will print that it is done and will exit (its task 
+will also be finished at this point). 
+Then the dispatcher will scheduled the 2 newly created tasks and run them. Each of them will display their
+greetings in turn.
+
+Modifying the example a bit, we can show that the 2 greeters really are two seperate execution units.
+This example also introduces the :func:`~concurrence.core.Tasklet.sleep` function.
+The sleep function will block the execution of the calling task for the given amount of seconds::
+
+	from concurrence import dispatch, Tasklet
+	
+	def greeting(msg):
+	    while True:
+	        print msg
+	        Tasklet.sleep(1)
+	
+	def start():
+	    
+	    Tasklet.new(greeting)("Hello")
+	    Tasklet.new(greeting)("World")
+	
+	if __name__ == '__main__':
+	    dispatch(start)  
+
+This example will alternately show ``Hello`` and ``World`` indefinitly as the 2 tasks themselves do not return
+from the ``greeting`` function.
+ 
+Waiting for a task
+------------------
+Sometimes you will want to halt the current task and wait for 1 or more subtasks to finish and return
+their result(s). This can be done using the :func:`~concurrence.core.Tasklet.join` function::
+
+	from concurrence import dispatch, Tasklet
+	
+	def sum(a, b):
+	    #... potentially long calculation, involving calls to databases etc...
+	    return a + b
+	
+	def start():
+	    
+	    t1 = Tasklet.new(sum)(10, 20)
+	    t2 = Tasklet.new(sum)(30, 40)
+	    
+	    res1 = Tasklet.join(t1)
+	    res2 = Tasklet.join(t2)
+	    
+	    print res1
+	    print res2
+	
+	if __name__ == '__main__':
+	    dispatch(start)
+	      
+In this example 2 subtasks are created by ``start``. 
+
+The start task will then block and wait for each subtask
+to finish by *joining* the subtasks using `Tasklet.join` 
+
+The result of the join is the return value of the subtask function.
+
+There are 2 convenient variations for joining tasks: 
+    * :func:`~concurrence.core.Tasklet.join_all` which takes a lists of tasks to join
+    * :func:`~concurrence.core.Tasklet.join_children` which joins all the children of the current task.
+
+Loop, Interval, Later
+---------------------
+
+The functions:
+
+    * :func:`~concurrence.core.Tasklet.loop`
+    * :func:`~concurrence.core.Tasklet.interval`
+    * :func:`~concurrence.core.Tasklet.later`
+
+Are provided to run a task in a loop, at a specified interval or at a later
+time respectively::
+
+	from concurrence import dispatch, Tasklet
+	
+	def hello(msg):
+	    print msg
+	    tasklet.sleep(0.5)
+	    
+	def greeting(msg):
+	    print msg
+	
+	def start():
+	    
+	    Tasklet.loop(hello)("Hello World")
+	    Tasklet.interval(1.0, greeting)("Hi There!")
+	    Tasklet.later(5.0, greeting)("Nice to see You!")
+    
+	if __name__ == '__main__':
+	    dispatch(start)  
+
+The current task
+----------------
+
+The current task can be retrieved by calling :func:`~concurrence.core.Tasklet.current`. 
+
+This function returns the task of the caller::
+
+	from concurrence import dispatch, Tasklet
+	
+	def greeting(msg):
+	    print Tasklet.current(), msg
+	
+	def start():
+	    
+	    Tasklet.interval(1.0, greeting)("Task 1")
+	    Tasklet.interval(1.0, greeting)("Task 2")
+	    
+	if __name__ == '__main__':
+	    dispatch(start)  
+
+Task tree and Task names
+------------------------
+
+Every task maintains a reference to the task that created it (its parent Task).
+You can get the parent with the :func:`~concurrence.core.Tasklet.parent` method.
+
+Every task also maintains a list of subtasks (:func:`~concurrence.core.Tasklet.children`) that it has spawned. 
+When a child exits, it is removed from its parents list of children.
+
+Thus a tree of tasks is maintained that can be traversed using the :func:`~concurrence.core.Tasklet.tree` method.
+
+A task can optionally be given a name by passing it to the :func:`~concurrence.core.Tasklet.new` method::
+
+	from concurrence import dispatch, Tasklet
+	
+	def greeting(msg):
+	    print msg
+	    Tasklet.sleep(2)
+	    print 'done'
+	
+	def start():
+	
+	    Tasklet.new(greeting, name = 'task1')("Hello")
+	    Tasklet.new(greeting, name = 'task2')("World")
+	    Tasklet.new(greeting, name = 'task3')("Hi There")
+	
+	    while True:
+	        Tasklet.sleep(1.0)
+	        #print a nice tree of tasks and their subtasks
+	        for task, level in Tasklet.current().tree():
+	            print "\t" * level, task.name            
+	        
+	if __name__ == '__main__':
+	    dispatch(start)  
+
+
+	    
+.. _web:
+
+Web
+===
+
+Example
+-------
+
+.. literalinclude:: ../examples/web.py
+
+

File examples/chat.py

+from concurrence import dispatch, Tasklet, Message
+from concurrence.io import BufferedStream, Socket, Server
+
+class MSG_WRITE_LINE(Message): pass
+class MSG_QUIT(Message): pass
+class MSG_LINE_READ(Message): pass
+
+connected_clients = set() #set of currently connected clients (tasks)
+        
+def handle(client_socket):
+    """handles a single client connected to the chat server"""
+    stream = BufferedStream(client_socket)
+
+    client_task = Tasklet.current() #this is the current task as started by server
+    connected_clients.add(client_task)
+    
+    def writer():
+        for msg, args, kwargs in Tasklet.receive():
+            if msg.match(MSG_WRITE_LINE):
+                stream.writer.write_bytes(args[0] + '\n')
+                stream.writer.flush()
+            
+    def reader():
+        for line in stream.reader.read_lines():
+            line = line.strip()
+            if line == 'quit': 
+                MSG_QUIT.send(client_task)()
+            else:
+                MSG_LINE_READ.send(client_task)(line)
+    
+    reader_task = Tasklet.new(reader)()
+    writer_task = Tasklet.new(writer)()
+
+    MSG_WRITE_LINE.send(writer_task)("type 'quit' to exit..")
+    
+    for msg, args, kwargs in Tasklet.receive():
+        if msg.match(MSG_QUIT):
+            break
+        elif msg.match(MSG_LINE_READ):
+            #a line was recv from our client, multicast it to the other clients
+            for task in connected_clients:
+                if task != client_task: #don't echo the line back to myself
+                    MSG_WRITE_LINE.send(task)(args[0])
+        elif msg.match(MSG_WRITE_LINE):
+            MSG_WRITE_LINE.send(writer_task)(args[0])
+        
+    connected_clients.remove(client_task)
+    reader_task.kill()
+    writer_task.kill()
+    client_socket.close()
+           
+def server():
+    """accepts connections on a socket, and dispatches
+    new tasks for handling the incoming requests"""
+    print 'listening for connections on port 9010'
+    Server.serve(('localhost', 9010), handle)
+
+if __name__ == '__main__':
+    dispatch(server)

File examples/greeter.py

+from concurrence import dispatch, Tasklet
+from concurrence.io import BufferedStream, Socket
+
+def handler(client_socket):
+    """writes the familiar greeting to client"""
+    stream = BufferedStream(client_socket)
+    writer = stream.writer    
+    writer.write_bytes("HTTP/1.0 200 OK\r\n")
+    writer.write_bytes("Content-Length: 12\r\n")    
+    writer.write_bytes("\r\n")
+    writer.write_bytes("Hello World!")
+    writer.flush()
+    stream.close()
+       
+def server():
+    """accepts connections on a socket, and dispatches
+    new tasks for handling the incoming requests"""
+    server_socket = Socket.new()
+    server_socket.bind(('localhost', 8080))
+    server_socket.listen()
+
+    while True:
+        client_socket = server_socket.accept()
+        Tasklet.new(handler)(client_socket)
+
+if __name__ == '__main__':
+    dispatch(server)

File examples/hello.py

+from concurrence import dispatch
+
+def hello():
+    print "Hello World!"
+    
+if __name__ == '__main__':
+    dispatch(hello)  

File examples/http.py

+from concurrence import dispatch
+from concurrence.http import WSGIServer
+
+def hello_world(environ, start_response):
+    start_response("200 OK", [])
+    return ["<html>Hello, world!</html>"]
+
+def main():
+    server = WSGIServer(hello_world)
+    server.serve(('localhost', 8080))
+
+if __name__ == '__main__':
+    dispatch(main)

File examples/http_client.py

+from concurrence import dispatch
+from concurrence.http import HTTPConnection
+
+def main():
+    
+    cnn = HTTPConnection()
+    cnn.connect(('www.google.com', 80))
+
+    request = cnn.get('/')
+    response = cnn.perform(request)
+    
+    print response.status
+    print response.headers
+    print response.body
+
+    cnn.close()
+
+if __name__ == '__main__':
+    dispatch(main)

File examples/http_client_pipeline.py

+from concurrence import dispatch
+from concurrence.http import HTTPConnection
+
+def main():
+    
+    cnn = HTTPConnection()
+    cnn.connect(('www.google.com', 80))
+
+    request = cnn.get('/')
+
+    #you can send multiple http requests on the same connection:
+    cnn.send(request) #request 1
+    cnn.send(request) #request 2
+
+    #and receive the corresponding responses    
+    response1 = cnn.receive()
+    response2 = cnn.receive()
+
+    print response1.status
+    print response1.headers
+    print response1.body
+
+    print response2.status
+    print response2.headers
+    print response2.body
+
+    cnn.close()
+
+if __name__ == '__main__':
+    dispatch(main)

File examples/message.py

+from concurrence import Tasklet, Message, dispatch
+
+class MSG_GREETING(Message): pass
+class MSG_FAREWELL(Message): pass
+
+def printer():
+    for msg, args, kwargs in Tasklet.receive():
+        if msg.match(MSG_GREETING):
+            print 'Hello', args[0]
+        elif msg.match(MSG_FAREWELL):
+            print 'Goodbye', args[0]
+        else:
+            pass #unknown msg
+    
+def main():
+    
+    printer_task = Tasklet.new(printer)()
+    
+    MSG_GREETING.send(printer_task)('World')
+    MSG_FAREWELL.send(printer_task)('World')
+        
+if __name__ == '__main__':
+    dispatch(main)  

File examples/post.py

+from concurrence import dispatch
+from concurrence.wsgi import WSGIServer, WSGISimpleRouter
+
+def show_form(environ, start_response):
+    print environ
+    start_response("200 OK", [('Content-type', 'text/html')])
+
+    html = """
+    <html>
+    <body>
+        <form method='post' action='/process'>
+            <input name='a'></input>
+            <input name='b'></input>
+            <input type='submit' value='click!'></input>
+        </form>
+    </body>
+    </html>
+    """
+    return [html]
+
+def upload_form(environ, start_response):
+    print environ
+    start_response("200 OK", [])
+
+    html = """
+    <html>
+    <body>
+        <form method='post' action='/process' enctype='multipart/form-data'>
+            <input type='file' name='thefile'></input>
+            <input type='a' name='a' value='klaas'></input>
+            <input type='b' name='b' value='piet'></input>
+            <input type='submit' value='click!'></input>
+        </form>
+    </body>
+    </html>
+    """
+    return [html]
+
+def process_form(environ, start_response):
+    print 'proc form'    
+    print environ
+    
+    fp = environ['wsgi.input']
+    while True:
+        data = fp.read(1024)
+        if not data: break
+        print 'read', repr(data)
+
+    start_response("200 OK", [])
+
+    html = """
+    <html>
+    <body>
+        <h1>blaat</h1>
+    </body>
+    </html>
+    """
+    return [html]
+
+def main():
+    application = WSGISimpleRouter()
+
+    from wsgiref.validate import validator
+
+    application.map('/form', validator(show_form))
+    application.map('/upload', upload_form)
+    application.map('/process', process_form)
+
+    server = WSGIServer(application)
+    server.serve(('localhost', 8080))
+
+if __name__ == '__main__':
+    import logging
+    logging.basicConfig(level = logging.DEBUG)
+    dispatch(main)

File examples/web.py

+from concurrence import dispatch
+from concurrence.web import Application, Controller, Filter, web
+
+class PageFilter(Filter):
+    """A filter that surrounds the upstream response with a complete html page"""
+    def __call__(self, next, *args, **kwargs):
+        #A Filter is a callable object and is part of a chain of filters configured for
+        #a certain Action. A filter uses the `next` argument to call the next filter in the chain.
+        return """
+        <html>
+            <head>
+                <title>Example Page</title>
+            </head>
+            <body style='background-color: #a0f0f0'>
+            %s
+            </body>
+        </html>""" % next(*args, **kwargs)
+    
+class WrapperFilter(Filter):
+    """A filter that surrounds the upstream response with a tag"""
+    def __init__(self, tag):
+        self.tag = tag
+        
+    def __call__(self, next, *args, **kwargs):
+        return "<%s>%s</%s>" % (self.tag,  next(*args, **kwargs), self.tag)
+    
+class ExampleController(Controller):
+    """A Controller contains multiple Actions. A controller
+    method becomes an Action by adding a `web.route` decorator that links the method to an url."""
+    
+    #controller level filters are applied to all actions in the controller
+    __filters__ = [PageFilter()]  
+    
+    #a action may be linked to multiple urls
+    @web.route('/greeting')
+    @web.route('/welcome')
+    def hello(self):
+        return "Hello World" 
+
+    @web.route('/farewell')
+    def goodbye(self):
+        return "Goodbye" 
+	
+    @web.route('/sum') 
+    def sum(self):
+	
+        msg = self.request.params.getone('msg')
+        a = int(self.request.params.getone('a'))
+        b = int(self.request.params.getone('b'))
+
+        return '%s %d' % (msg, a + b)
+
+    #in addition to the controller level filters, an action may also supply its own filters
+    @web.route('/wrapper')
+    @web.filter(WrapperFilter('h1'))
+    @web.filter(WrapperFilter('strong'))    
+    def wrapper(self):
+        return "Testing 132"
+    
+def main():
+    #create a web application
+    application = Application()
+    application.add_controller(ExampleController())
+    application.configure()
+    application.serve(('localhost', 8080))
+
+if __name__ == '__main__':
+    dispatch(main)
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c9"
+DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>="+version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+        else:
+            del pkg_resources, sys.modules['pkg_resources']    # reload ok
+            return do_download()
+    except pkg_resources.DistributionNotFound:
+        return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+

File lib/concurrence/__init__.py

+# Copyright (C) 2009, Hyves (Startphone Ltd.)
+#
+# This module is part of the Concurrence Framework and is released under
+# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
+__version__ = '0.3.1' #remember to update setup.py
+__version_info__ = tuple([ int(num) for num in __version__.split('.')])
+
+from concurrence.core import dispatch, quit, disable_threading, get_version_info
+from concurrence.core import Channel, Tasklet, Message, FileDescriptorEvent, SignalEvent
+from concurrence.core import TimeoutError, TaskletError, JoinError
+from concurrence.local import TaskLocal, TaskInstance
+
+import concurrence._unittest as unittest
+
+try:
+    import json
+except:
+    try:
+        import simplejson as json
+    except:
+        import logging
+        logging.exception("could not import json library!', pls install simplejson or use python 2.6+")
+

File lib/concurrence/_stackless.py

+# Copyright (C) 2009, Hyves (Startphone Ltd.)
+#
+# This module is part of the Concurrence Framework and is released under
+# the New BSD License: http://www.opensource.org/licenses/bsd-license.php
+
+"""This module implements the stackless API on top of py.magic greenlet API
+This way it is possible to run concurrence applications on top of normal python
+using the greenlet module.
+Because the greenlet module uses only 'hard' switching as opposed to stackless 'soft' switching
+it is a bit slower (about 35%), but very usefull because you don't need to install stackless.
+Note that this does not aim to be a complete implementation of stackless on top of greenlets,
+just enough of the stackless API to make concurrence run.
+This code was inspired by:
+http://aigamedev.com/programming-tips/round-robin-multi-tasking and
+also by the pypy implementation of the same thing (buggy, not being maintained?) at 
+https://codespeak.net/viewvc/pypy/dist/pypy/lib/stackless.py?view=markup
+"""
+
+try:
+    from py.magic import greenlet #as of version 1.0 of py, it does not supply greenlets anymore
+except ImportError:
+    from greenlet import greenlet #there is an older package containing just the greenlet lib
+
+from collections import deque
+
+class TaskletExit(SystemExit):pass
+
+import __builtin__
+__builtin__.TaskletExit = TaskletExit
+
+
+class bomb(object):
+    """used as a result value for sending exceptions trough a channel"""
+    def __init__(self, exc_type = None, exc_value = None, exc_traceback = None):
+        self.type = exc_type
+        self.value = exc_value
+        self.traceback = exc_traceback
+
+    def raise_(self):
+        raise self.type, self.value, self.traceback
+
+class channel(object):
+    """implementation of stackless's channel object"""
+    def __init__(self):
+        self.balance = 0
+        self.queue = deque()
+        
+    def receive(self):
+        return _scheduler._receive(self)
+
+    def send(self, data):
+        return _scheduler._send(self, data)
+
+    def send_exception(self, exp_type, *args):
+        self.send(bomb(exp_type, exp_type(*args)))
+
+    def send_sequence(self, iterable):
+        for item in iterable:
+            self.send(item)
+        
+
+            
+class tasklet(object):
+    """implementation of stackless's tasklet object"""
+    
+    def __init__(self, f = None, greenlet = None, alive = False):
+        self.greenlet = greenlet
+        self.func = f
+        self.alive = alive
+        self.blocked = False
+        self.data = None
+        
+    def bind(self, func):
+        if not callable(func):
+            raise TypeError('tasklet function must be a callable')
+        self.func = func
+
+    def __call__(self, *args, **kwargs):
+        """this is where the new task starts to run, e.g. it is where the greenlet is created
+        and the 'task' is first scheduled to run"""
+        if self.func is None:
+            raise TypeError('tasklet function must be a callable')
+
+        def _func(*_args, **_kwargs):
+            try:
+                self.func(*args, **kwargs)
+            except TaskletExit:
+                pass #let it pass silently
+            except:
+                import logging
+                logging.exception('unhandled exception in greenlet')
+                #don't propagate to parent
+            finally:
+                assert _scheduler.current == self
+                _scheduler.remove(self)