oops_wsgi-0.0.10/ 0000755 0001750 0001750 00000000000 11712140505 013457 5 ustar robertc robertc oops_wsgi-0.0.10/README 0000644 0001750 0001750 00000010123 11712137446 014346 0 ustar robertc robertc ************************************************************
python-oops-wsgi: Error report integration into wsgi servers
************************************************************
Copyright (c) 2011, Canonical Ltd
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
GNU Lesser General Public License version 3 (see the file LICENSE).
The oops_wsgi package provides integration glue between wsgi web servers and
the oops error reporting system (http://pypi.python.org/pypi/oops).
Dependencies
============
* Python 2.6+
* oops (http://pypi.python.org/pypi/oops)
* paste
Testing Dependencies
====================
* subunit (http://pypi.python.org/pypi/python-subunit) (optional)
* testtools (http://pypi.python.org/pypi/testtools)
Usage
=====
oops_wsgi provides integration with an oops.Config, permitting errors in your
web application to be gathered centrally, with tracebacks and other diagnostic
information.
Typically, something like this:
* Setup your configuration::
>>> from oops import Config
>>> config = Config()
Note that you will probably want at least one publisher, or your reports will
be discarded.
* Add in wsgi specific hooks to the config::
>>> oops_wsgi.install_hooks(config)
This is a convenience function - you are welcome to pick and choose the creation
or filter hooks you want from oops_wsgi.hooks.
* Create your wsgi app as normal, and then wrap it::
>>> app = oops_wsgi.make_app(app, config)
If any exception bubbles up through this middleware, an oops will be logged. If
the body of the request had not started, then a custom page is shown that
shows the OOPS id, and the exception is swallowed. Exceptions that indicate
normal situations like end-of-file on a socket do not trigger OOPSes. If the
OOPS is filtered, or no publishers are configured, then the exception will
propogate up the stack - the oops middleware cannot do anything useful in these
cases. (For instance, if you have a custom 404 middleware above the oops
middleware in the wsgi stack, and filter 404 exceptions so they do not create
reports, then if the oops middleware did anything other than propogate the
exception, your custom 404 middleware would not work.
If the body had started, then there is no way to communicate the OOPS id to the
client and the exception will propogate up the wsgi app stack.
You can customise the error page if you supply a helper that accepts (environ,
report) and returns HTML to be sent to the client.
>>> def myerror_html(environ, report):
... return '
OOPS! %s
' % report['id']
>>> app = oops_wsgi.make_app(app, config, error_render=myerror_html)
Or you can supply a string template to be formatted with the report.
>>> json_template='{"oopsid" : "%(id)s"}'
>>> app = oops_wsgi.make_app(app, config, error_template=json_template)
For more information see pydoc oops_wsgi.
Installation
============
Either run setup.py in an environment with all the dependencies available, or
add the working directory to your PYTHONPATH.
Development
===========
Upstream development takes place at https://launchpad.net/python-oops-wsgi.
To setup a working area for development, if the dependencies are not
immediately available, you can use ./bootstrap.py to create bin/buildout, then
bin/py to get a python interpreter with the dependencies available.
To run the tests use the runner of your choice, the test suite is
oops_wsgi.tests.test_suite.
For instance::
$ bin/py -m testtools.run oops_wsgi.tests.test_suite
If you have testrepository you can run the tests with that::
$ testr run
oops_wsgi-0.0.10/PKG-INFO 0000644 0001750 0001750 00000013041 11712140505 014553 0 ustar robertc robertc Metadata-Version: 1.0
Name: oops_wsgi
Version: 0.0.10
Summary: OOPS wsgi middleware.
Home-page: https://launchpad.net/python-oops-wsgi
Author: Launchpad Developers
Author-email: launchpad-dev@lists.launchpad.net
License: UNKNOWN
Description: ************************************************************
python-oops-wsgi: Error report integration into wsgi servers
************************************************************
Copyright (c) 2011, Canonical Ltd
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
GNU Lesser General Public License version 3 (see the file LICENSE).
The oops_wsgi package provides integration glue between wsgi web servers and
the oops error reporting system (http://pypi.python.org/pypi/oops).
Dependencies
============
* Python 2.6+
* oops (http://pypi.python.org/pypi/oops)
* paste
Testing Dependencies
====================
* subunit (http://pypi.python.org/pypi/python-subunit) (optional)
* testtools (http://pypi.python.org/pypi/testtools)
Usage
=====
oops_wsgi provides integration with an oops.Config, permitting errors in your
web application to be gathered centrally, with tracebacks and other diagnostic
information.
Typically, something like this:
* Setup your configuration::
>>> from oops import Config
>>> config = Config()
Note that you will probably want at least one publisher, or your reports will
be discarded.
* Add in wsgi specific hooks to the config::
>>> oops_wsgi.install_hooks(config)
This is a convenience function - you are welcome to pick and choose the creation
or filter hooks you want from oops_wsgi.hooks.
* Create your wsgi app as normal, and then wrap it::
>>> app = oops_wsgi.make_app(app, config)
If any exception bubbles up through this middleware, an oops will be logged. If
the body of the request had not started, then a custom page is shown that
shows the OOPS id, and the exception is swallowed. Exceptions that indicate
normal situations like end-of-file on a socket do not trigger OOPSes. If the
OOPS is filtered, or no publishers are configured, then the exception will
propogate up the stack - the oops middleware cannot do anything useful in these
cases. (For instance, if you have a custom 404 middleware above the oops
middleware in the wsgi stack, and filter 404 exceptions so they do not create
reports, then if the oops middleware did anything other than propogate the
exception, your custom 404 middleware would not work.
If the body had started, then there is no way to communicate the OOPS id to the
client and the exception will propogate up the wsgi app stack.
You can customise the error page if you supply a helper that accepts (environ,
report) and returns HTML to be sent to the client.
>>> def myerror_html(environ, report):
... return 'OOPS! %s
' % report['id']
>>> app = oops_wsgi.make_app(app, config, error_render=myerror_html)
Or you can supply a string template to be formatted with the report.
>>> json_template='{"oopsid" : "%(id)s"}'
>>> app = oops_wsgi.make_app(app, config, error_template=json_template)
For more information see pydoc oops_wsgi.
Installation
============
Either run setup.py in an environment with all the dependencies available, or
add the working directory to your PYTHONPATH.
Development
===========
Upstream development takes place at https://launchpad.net/python-oops-wsgi.
To setup a working area for development, if the dependencies are not
immediately available, you can use ./bootstrap.py to create bin/buildout, then
bin/py to get a python interpreter with the dependencies available.
To run the tests use the runner of your choice, the test suite is
oops_wsgi.tests.test_suite.
For instance::
$ bin/py -m testtools.run oops_wsgi.tests.test_suite
If you have testrepository you can run the tests with that::
$ testr run
Platform: UNKNOWN
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
oops_wsgi-0.0.10/oops_wsgi/ 0000755 0001750 0001750 00000000000 11712140505 015470 5 ustar robertc robertc oops_wsgi-0.0.10/oops_wsgi/hooks.py 0000644 0001750 0001750 00000005105 11712137446 017200 0 ustar robertc robertc # Copyright (c) 2010, 2011, Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
# GNU Lesser General Public License version 3 (see the file LICENSE).
"""oops creation and filtering hooks for working with WSGI."""
__all__ = [
'copy_environ',
'hide_cookie',
'install_hooks',
'update_report',
]
_wsgi_standard_env_keys = set([
'REQUEST_METHOD',
'SCRIPT_NAME',
'PATH_INFO',
'QUERY_STRING',
'CONTENT_TYPE',
'CONTENT_LENGTH',
'SERVER_NAME',
'SERVER_PORT',
'SERVER_PROTOCOL',
'wsgi.version',
'wsgi.url_scheme',
])
def copy_environ(report, context):
"""Copy useful variables from the wsgi environment if it is present.
This should be in the context as 'wsgi_environ'.
e.g.
report = config.create(context=dict(wsgi_environ=environ))
"""
environ = context.get('wsgi_environ', {})
if 'req_vars' not in report:
report['req_vars'] = {}
req_vars = report['req_vars']
for key, value in sorted(environ.items()):
if (key in _wsgi_standard_env_keys or
key.startswith('HTTP_')):
req_vars[key] = value
def hide_cookie(report, context):
"""If there is an HTTP_COOKIE entry in the report, hide its value.
The entry is looked for either as a top level key or in the req_vars dict.
The COOKIE header is often used to carry session tokens and thus permits
folk analyzing crash reports to log in as an arbitrary user (e.g. your
sysadmin users).
"""
if 'HTTP_COOKIE' in report:
report['HTTP_COOKIE'] = ''
if 'HTTP_COOKIE' in report.get('req_vars', {}):
report['req_vars']['HTTP_COOKIE'] = ''
def install_hooks(config):
"""Install the default wsgi hooks into config."""
config.on_create.extend([copy_environ, hide_cookie])
config.on_create.insert(0, update_report)
def update_report(report, context):
"""Copy the oops.report contents from the wsgi environment to report."""
report.update(context.get('wsgi_environ', {}).get('oops.report', {}))
oops_wsgi-0.0.10/oops_wsgi/django.py 0000644 0001750 0001750 00000003407 11712137446 017322 0 ustar robertc robertc # Copyright (c) 2011 Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
# GNU Lesser General Public License version 3 (see the file LICENSE).
"""Django glue for OOPS integration.
To use:
* Use OOPSWSGIHandler rather than than WSGIHandler.
* Create an oops wrapper with oops_wsgi.make_app(..., oops_on_status=['500'])
This is not needed if you have https://code.djangoproject.com/ticket/16674
fixed in your Django.
"""
from __future__ import absolute_import
from django.core.handlers import wsgi
__all__ = [
'OOPSWSGIHandler',
]
class OOPSWSGIHandler(wsgi.WSGIHandler):
def handle_uncaught_exception(self, request, resolver, exc_info):
if 'oops.context' in request.environ:
# We are running under python-oops-wsgi - inject the exception into
# its context. This will provide the exception to the handler, and
# if you use oops_on_status=['500'] OOPS reports will be created
# when Django has suffered a failure.
request.environ['oops.context']['exc_info'] = exc_info
# Now perform the default django uncaught exception behaviour.
return super(OOPSWSGIHandler, self).handle_uncaught_exception(
request, resolver, exc_info)
oops_wsgi-0.0.10/oops_wsgi/__init__.py 0000644 0001750 0001750 00000012043 11712140406 017601 0 ustar robertc robertc #
# Copyright (c) 2011, Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
# GNU Lesser General Public License version 3 (see the file LICENSE).
"""oops <-> wsgi integration.
oops_wsgi provides integration with an oops.Config, permitting errors in your
web application to be gathered centrally, with tracebacks and other diagnostic
information.
Typically, something like this:
* Setup your configuration::
>>> from oops import Config
>>> config = Config()
Note that you will probably want at least one publisher, or your reports will
be discarded.
* Add in wsgi specific hooks to the config::
>>> oops_wsgi.install_hooks(config)
This is a convenience function - you are welcome to pick and choose the creation
or filter hooks you want from oops_wsgi.hooks.
* Create your wsgi app as normal, and then wrap it::
>>> app = oops_wsgi.make_app(app, config)
If any exception bubbles up through this middleware, an oops will be logged. If
the body of the request had not started, then a custom page is shown that
shows the OOPS id, and the exception is swallowed. Exceptions that indicate
normal situations like end-of-file on a socket do not trigger OOPSes. If the
OOPS is filtered, or no publishers are configured, then the exception will
propogate up the stack - the oops middleware cannot do anything useful in these
cases. (For instance, if you have a custom 404 middleware above the oops
middleware in the wsgi stack, and filter 404 exceptions so they do not create
reports, then if the oops middleware did anything other than propogate the
exception, your custom 404 middleware would not work.
If the body had started, then there is no way to communicate the OOPS id to the
client and the exception will propogate up the wsgi app stack.
You can customise the error page if you supply a helper that accepts (environ,
report) and returns HTML to be sent to the client.
>>> def myerror_html(environ, report):
... return 'OOPS! %s
' % report['id']
>>> app = oops_wsgi.make_app(app, config, error_render=myerror_html)
Or you can supply a string template to be formatted with the report.
>>> json_template='{"oopsid" : "%(id)s"}'
>>> app = oops_wsgi.make_app(app, config, error_template=json_template)
If the wrapped app errors by sending exc_info to start_response, that will be
used to create an OOPS report, and the id added to the headers under the
X-Oops-Id header. This is also present when an OOPS is triggered by catching an
exception in the wrapped app (as long as the body hasn't started).
You can request that reports be created when a given status code is used (e.g.
to gather stats on the number of 404's occuring without doing log processing).
>>> app = oops_wsgi.make_app(app, config, oops_on_status=['404'])
The oops middleware injects two variables into the WSGI environ to make it easy
for cooperating code to report additional data.
The `oops.report` variable is a dict which is copied into the report. See the
`oops` package documentation for documentation on what should be present in an
oops report. This requires the update_report hook to be installed (which
`install_hooks` will do for you).
The `oops.context` variable is a dict used for generating the report - keys and
values added to that can be used in the `config.on_create` hooks to populate
custom data without needing to resort to global variables.
If a timeline is present in the WSGI environ (as 'timeline.timeline') it is
automatically captured to the oops context when generating an OOPS. See the
oops-timeline module for hooks to use this.
`pydoc oops_wsgi.make_app` describes the entire capabilities of the
middleware.
"""
# same format as sys.version_info: "A tuple containing the five components of
# the version number: major, minor, micro, releaselevel, and serial. All
# values except releaselevel are integers; the release level is 'alpha',
# 'beta', 'candidate', or 'final'. The version_info value corresponding to the
# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a
# releaselevel of 'dev' for unreleased under-development code.
#
# If the releaselevel is 'alpha' then the major/minor/micro components are not
# established at this point, and setup.py will use a version of next-$(revno).
# If the releaselevel is 'final', then the tarball will be major.minor.micro.
# Otherwise it is major.minor.micro~$(revno).
__version__ = (0, 0, 10, 'beta', 0)
__all__ = [
'install_hooks',
'make_app'
]
from oops_wsgi.middleware import make_app
from oops_wsgi.hooks import install_hooks
oops_wsgi-0.0.10/oops_wsgi/middleware.py 0000644 0001750 0001750 00000025572 11712137665 020207 0 ustar robertc robertc # Copyright (c) 2010, 2011, Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
# GNU Lesser General Public License version 3 (see the file LICENSE).
"""WSGI middleware to integrate with an oops.Config."""
__metaclass__ = type
import socket
import sys
import time
from paste.request import construct_url
__all__ = [
'default_map_environ',
'generator_tracker',
'make_app',
]
default_error_template='''
Oops! - %(id)s
Oops!
Something broke while generating the page.
Please try again in a few minutes, and if the problem persists file
a bug or contact customer support. Please quote OOPS-ID
%(id)s
'''
default_map_environ = {
# Map timeline objects into the oops context as 'timeline'
'timeline.timeline': 'timeline',
}
class SoftRequestTimeout(Exception):
"""Soft request timeout expired"""
def make_app(app, config, template=default_error_template,
content_type='text/html', error_render=None, oops_on_status=None,
map_environ=None, tracker=None, soft_start_timeout=None):
"""Construct a middleware around app that will forward errors via config.
Any errors encountered by the app will be forwarded to config and an error
page shown.
If the body of a reply has already started the error will be forwarded to
config and also re-raised.
If there are no publishers, or an error is filtered, the error will be
re-raised rather than an error page shown. This permits containing
middleware to show custom errors (for 404's, for instance), perhaps even
for just some occurences of the issue.
:param app: A WSGI app.
:param config: An oops.Config.
:param template: Optional string template to use when reporting the oops to
the client. If not supplied a default template is used (unless an
error_render function has been supplied).
:param content_type: The content type for error pages. Defaults to
text/html.
:param error_render: Optional custom renderer for presenting error reports
to clients. Should be a callable taking the report as its only
parameter.
:param oops_on_status: Optional list of HTTP status codes that should
generate OOPSes. OOPSes triggered by sniffing these codes will not
interfere with the response being sent. For instance, if you do
not expect any 404's from your application, you might set
oops_on_status=['404'].
:param map_environ: A dictionary of environment keys to look for, and if
present map into the OOPS context when generating an OOPS. The value of
the key determines the name given in the OOPS context. If None is passed
the default_map_environ is used. Pass {} in to entirely disable mapping.
:param tracker: A factory function to create a tracker. Trackers are used
to allow variations on the WSGI environment to still use oops_wsgi.
See generator_tracker for the reference tracker used in regular WSGI
environments. generator_tracker is used by default or when
tracker=None.
:param soft_start_timeout: A duration in milliseconds for the creation of
reports on slow requests. If this is set and the duration between
calling into the app and start_response being called is greater than
the timeout value, then an OOPS will be created and the OOPS id added
to the response HTTP headers as normal. A backtrace leading into the
middleware is generated (this can be informative as start_response is
a callback) and the exception type is set to SoftRequestTimeout.
:return: A WSGI app.
"""
def oops_middleware(environ, start_response):
"""OOPS inserting middleware.
This has the following WSGI properties:
* start_response is buffered until either write() is called, or the
wrapped app starts yielding content.
* Exceptions that are ignored by the oops config get re-raised.
* socket errors and GeneratorExit errors are passed through without
* being forward to the oops system.
"""
environ['oops.report'] = {}
environ['oops.context'] = {}
if soft_start_timeout:
start_time = time.time()
state = {}
def make_context(exc_info=None):
context = dict(url=construct_url(environ), wsgi_environ=environ)
context.update(environ.get('oops.context', {}))
mapper = map_environ
if mapper is None:
mapper = default_map_environ
for environ_key, context_key in mapper.items():
if environ_key in environ:
context[context_key] = environ[environ_key]
if exc_info is not None:
context['exc_info'] = exc_info
return context
def oops_write(bytes):
write = state.get('write')
if write is None:
status, headers = state.pop('response')
# Signal that we have called start_response
state['write'] = start_response(status, headers)
write = state['write']
write(bytes)
def oops_start_response(status, headers, exc_info=None):
if exc_info is not None:
# The app is explicitly signalling an error (rather than
# returning a page describing the error). Capture that and then
# forward to the containing element untouched except for the
# addition of the X-Oops-Id header. We don't touch the body
# because the application is handling the error and generating
# the body itself. We may in future provide an option to
# replace the body in this situation.
report = config.create(make_context(exc_info=exc_info))
ids = config.publish(report)
try:
if ids:
headers = list(headers)
headers.append(('X-Oops-Id', str(report['id'])))
state['write'] = start_response(status, headers, exc_info)
return state['write']
finally:
del exc_info
else:
do_oops = False
if oops_on_status:
for sniff_status in oops_on_status:
if status.startswith(sniff_status):
do_oops = True
if (soft_start_timeout and
(time.time()-start_time)*1000 > soft_start_timeout):
try:
raise SoftRequestTimeout(
"Start_response over timeout %s."
% soft_start_timeout)
except SoftRequestTimeout:
exc_info = sys.exc_info()
do_oops = True
if do_oops:
report = config.create(make_context(exc_info=exc_info))
report['HTTP_STATUS'] = status.split(' ')[0]
config.publish(report)
state['response'] = (status, headers)
return oops_write
try:
def ensure_start_response():
if 'write' not in state:
status, headers = state.pop('response')
# Signal that we have called start_response
state['write'] = start_response(status, headers)
def on_exception(exc_info):
report = config.create(make_context(exc_info=exc_info))
ids = config.publish(report)
if not ids or 'write' in state:
# No OOPS generated, no oops publisher, or we have already
# transmitted the wrapped apps headers - either way we can't
# replace the content with a clean error, so let the wsgi
# server figure it out.
raise
headers = [('Content-Type', content_type)]
headers.append(('X-Oops-Id', str(report['id'])))
start_response(
'500 Internal Server Error', headers, exc_info)
del exc_info
if error_render is not None:
return error_render(report)
else:
return template % report
if tracker is None:
tracker_factory = generator_tracker
else:
tracker_factory = tracker
return tracker_factory(
ensure_start_response, ensure_start_response, on_exception,
app(environ, oops_start_response))
except socket.error:
raise
except Exception:
exc_info = sys.exc_info()
return [on_exception(exc_info)]
return oops_middleware
def generator_tracker(on_first_bytes, on_finish, on_error, app_body):
"""A wrapper for generators that calls the OOPS hooks as needed.
:param on_first_bytes: Called as on_first_bytes() when the first bytes from
the app body are available but before they are yielded.
:param on_finish: Called as on_finish() when the app body is fully
consumed.
:param on_error: Called as on_error(sys.exc_info()) if a handleable error
has occured while consuming the generator. Errors like GeneratorExit
are not handleable.
:param app_body: The iterable body for the WSGI app. This may be a simple
list or a generator - it is merely known to meet the iterator protocol.
"""
try:
called_first = False
for bytes in app_body:
if not called_first:
called_first = True
on_first_bytes()
yield bytes
on_finish()
except socket.error:
# start_response, which iteration can trigger a call into, may raise
# socket.error when writing if the client has disconnected: thats not
# an OOPS condition. This does potentially mask socket.error issues in
# the appserver code, so we may want to change this to callback to
# determine if start_response has been called upstream, and if so, to
# still generate an OOPS.
raise
except GeneratorExit:
# Python 2.4
raise
except Exception:
exc_info = sys.exc_info()
yield on_error(exc_info)
oops_wsgi-0.0.10/setup.py 0000755 0001750 0001750 00000003301 11712140376 015177 0 ustar robertc robertc #!/usr/bin/env python
#
# Copyright (c) 2011, Canonical Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 only.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
# GNU Lesser General Public License version 3 (see the file LICENSE).
from distutils.core import setup
import os.path
description = file(
os.path.join(os.path.dirname(__file__), 'README'), 'rb').read()
setup(name="oops_wsgi",
version="0.0.10",
description=\
"OOPS wsgi middleware.",
long_description=description,
maintainer="Launchpad Developers",
maintainer_email="launchpad-dev@lists.launchpad.net",
url="https://launchpad.net/python-oops-wsgi",
packages=['oops_wsgi'],
package_dir = {'':'.'},
classifiers = [
'Development Status :: 2 - Pre-Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Operating System :: OS Independent',
'Programming Language :: Python',
],
install_requires = [
'oops',
'paste',
],
extras_require = dict(
test=[
'testtools',
]
),
)