add django_paste command for paste-compatible projects.

This commit is contained in:
Benoit Chesneau 2010-01-23 14:25:41 +01:00
parent 73e8f4c103
commit b0f1a0fe06
45 changed files with 1120 additions and 5 deletions

View File

@ -8,10 +8,14 @@ This is a port of Unicorn (http://unicorn.bogomips.org/) in Python.
Installation
------------
::
Install from sources :
$ python setup.py install
Or from Pypi :
$ easy_install -U gunicorn
Usage
-----
@ -37,6 +41,20 @@ For django projects use the `gunicorn_django` command::
$ cd yourdjangoproject
$ gunicorn_django --workers=2
For paste-compatible projects (like Pylons) use the `gunicorn_paste` command:
$ cd your pasteproject
$ gunicorn_paste --workers=2 development.ini
**WARNING**
If you get the following error :
AssertionError: The EvalException middleware is not usable in a multi-process environment
you have to set the debug option n your paste configuration file to False.
Kernel Parameters
-----------------

61
bin/gunicorn_paste Normal file
View File

@ -0,0 +1,61 @@
#! /usr/bin/env python
# -*- coding: utf-8 -
#
# 2009 (c) Benoit Chesneau <benoitc@e-engura.com>
# 2009 (c) Paul J. Davis <paul.joseph.davis@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
import os
import pkg_resources
import re
import sys
from paste.deploy import loadapp
from gunicorn.main import main
__usage__ = "%prog [OPTIONS] APP_MODULE"
_scheme_re = re.compile(r'^[a-z][a-z]+:', re.I)
def get_app(parser, opts, args):
if len(args) != 1:
parser.error("No applicantion name specified.")
config_file = os.path.abspath(os.path.normpath(
os.path.join(os.getcwd(), args[0])))
if not os.path.exists(config_file):
parser.error("Config file not found.")
config_url = 'config:%s' % config_file
relative_to = os.path.dirname(config_file)
# load module in sys path
sys.path.insert(0, relative_to)
# add to eggs
pkg_resources.working_set.add_entry(relative_to)
app = loadapp(config_url, relative_to=relative_to)
return app
main(__usage__, get_app)

View File

@ -0,0 +1,3 @@
include pylonstest/config/deployment.ini_tmpl
recursive-include pylonstest/public *
recursive-include pylonstest/templates *

View File

@ -0,0 +1,19 @@
This file is for you to describe the pylonstest application. Typically
you would include information such as the information below:
Installation and Setup
======================
Install ``pylonstest`` using easy_install::
easy_install pylonstest
Make a config file as follows::
paster make-config pylonstest config.ini
Tweak the config file as appropriate and then setup the application::
paster setup-app config.ini
Then you are ready to go.

View File

@ -0,0 +1,72 @@
#
# pylonstest - Pylons development environment configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
debug = false
# Uncomment and replace with the address which should receive any error reports
#email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 5000
[app:main]
use = egg:pylonstest
full_stack = true
static_files = true
cache_dir = %(here)s/data
beaker.session.key = pylonstest
beaker.session.secret = somesecret
# If you'd like to fine-tune the individual locations of the cache data dirs
# for the Cache data, or the Session saves, un-comment the desired settings
# here:
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
#set debug = false
# Logging configuration
[loggers]
keys = root, routes, pylonstest
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_routes]
level = INFO
handlers =
qualname = routes.middleware
# "level = DEBUG" logs the route matched and routing variables.
[logger_pylonstest]
level = DEBUG
handlers =
qualname = pylonstest
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

View File

@ -0,0 +1,19 @@
pylonstest
++++++++++
This is the main index page of your documentation. It should be written in
`reStructuredText format <http://docutils.sourceforge.net/rst.html>`_.
You can generate your documentation in HTML format by running this command::
setup.py pudge
For this to work you will need to download and install `buildutils`_,
`pudge`_, and `pygments`_. The ``pudge`` command is disabled by
default; to ativate it in your project, run::
setup.py addcommand -p buildutils.pudge_command
.. _buildutils: http://pypi.python.org/pypi/buildutils
.. _pudge: http://pudge.lesscode.org/
.. _pygments: http://pygments.org/

View File

@ -0,0 +1,276 @@
#!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:])

View File

@ -0,0 +1,10 @@
Metadata-Version: 1.0
Name: pylonstest
Version: 0.1dev
Summary: UNKNOWN
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN

View File

@ -0,0 +1,35 @@
MANIFEST.in
README.txt
setup.cfg
setup.py
pylonstest/__init__.py
pylonstest/websetup.py
pylonstest.egg-info/PKG-INFO
pylonstest.egg-info/SOURCES.txt
pylonstest.egg-info/dependency_links.txt
pylonstest.egg-info/entry_points.txt
pylonstest.egg-info/not-zip-safe
pylonstest.egg-info/paster_plugins.txt
pylonstest.egg-info/requires.txt
pylonstest.egg-info/top_level.txt
pylonstest/config/__init__.py
pylonstest/config/deployment.ini_tmpl
pylonstest/config/environment.py
pylonstest/config/middleware.py
pylonstest/config/routing.py
pylonstest/controllers/__init__.py
pylonstest/controllers/error.py
pylonstest/controllers/hello.py
pylonstest/lib/__init__.py
pylonstest/lib/app_globals.py
pylonstest/lib/base.py
pylonstest/lib/helpers.py
pylonstest/model/__init__.py
pylonstest/public/bg.png
pylonstest/public/favicon.ico
pylonstest/public/index.html
pylonstest/public/pylons-logo.gif
pylonstest/tests/__init__.py
pylonstest/tests/test_models.py
pylonstest/tests/functional/__init__.py
pylonstest/tests/functional/test_hello.py

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,7 @@
[paste.app_factory]
main = pylonstest.config.middleware:make_app
[paste.app_install]
main = pylons.util:PylonsInstaller

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,2 @@
PasteScript
Pylons

View File

@ -0,0 +1 @@
Pylons>=0.9.7

View File

@ -0,0 +1 @@
pylonstest

View File

@ -0,0 +1,60 @@
#
# pylonstest - Pylons configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
debug = true
email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 5000
[app:main]
use = egg:pylonstest
full_stack = true
static_files = true
cache_dir = %(here)s/data
beaker.session.key = pylonstest
beaker.session.secret = ${app_instance_secret}
app_instance_uuid = ${app_instance_uuid}
# If you'd like to fine-tune the individual locations of the cache data dirs
# for the Cache data, or the Session saves, un-comment the desired settings
# here:
#beaker.cache.data_dir = %(here)s/data/cache
#beaker.session.data_dir = %(here)s/data/sessions
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
set debug = false
# Logging configuration
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s

View File

@ -0,0 +1,39 @@
"""Pylons environment configuration"""
import os
from mako.lookup import TemplateLookup
from pylons import config
from pylons.error import handle_mako_error
import pylonstest.lib.app_globals as app_globals
import pylonstest.lib.helpers
from pylonstest.config.routing import make_map
def load_environment(global_conf, app_conf):
"""Configure the Pylons environment via the ``pylons.config``
object
"""
# Pylons paths
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
paths = dict(root=root,
controllers=os.path.join(root, 'controllers'),
static_files=os.path.join(root, 'public'),
templates=[os.path.join(root, 'templates')])
# Initialize config with the basic options
config.init_app(global_conf, app_conf, package='pylonstest', paths=paths)
config['routes.map'] = make_map()
config['pylons.app_globals'] = app_globals.Globals()
config['pylons.h'] = pylonstest.lib.helpers
# Create the Mako TemplateLookup, with the default auto-escaping
config['pylons.app_globals'].mako_lookup = TemplateLookup(
directories=paths['templates'],
error_handler=handle_mako_error,
module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
input_encoding='utf-8', default_filters=['escape'],
imports=['from webhelpers.html import escape'])
# CONFIGURATION OPTIONS HERE (note: all config options will override
# any Pylons config options)

View File

@ -0,0 +1,69 @@
"""Pylons middleware initialization"""
from beaker.middleware import CacheMiddleware, SessionMiddleware
from paste.cascade import Cascade
from paste.registry import RegistryManager
from paste.urlparser import StaticURLParser
from paste.deploy.converters import asbool
from pylons import config
from pylons.middleware import ErrorHandler, StatusCodeRedirect
from pylons.wsgiapp import PylonsApp
from routes.middleware import RoutesMiddleware
from pylonstest.config.environment import load_environment
def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
"""Create a Pylons WSGI application and return it
``global_conf``
The inherited configuration for this application. Normally from
the [DEFAULT] section of the Paste ini file.
``full_stack``
Whether this application provides a full WSGI stack (by default,
meaning it handles its own exceptions and errors). Disable
full_stack when this application is "managed" by another WSGI
middleware.
``static_files``
Whether this application serves its own static files; disable
when another web server is responsible for serving them.
``app_conf``
The application's local configuration. Normally specified in
the [app:<name>] section of the Paste ini file (where <name>
defaults to main).
"""
# Configure the Pylons environment
load_environment(global_conf, app_conf)
# The Pylons WSGI app
app = PylonsApp()
# Routing/Session/Cache Middleware
app = RoutesMiddleware(app, config['routes.map'])
app = SessionMiddleware(app, config)
app = CacheMiddleware(app, config)
# CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares)
if asbool(full_stack):
# Handle Python exceptions
app = ErrorHandler(app, global_conf, **config['pylons.errorware'])
# Display error documents for 401, 403, 404 status codes (and
# 500 when debug is disabled)
if asbool(config['debug']):
app = StatusCodeRedirect(app)
else:
app = StatusCodeRedirect(app, [400, 401, 403, 404, 500])
# Establish the Registry for this application
app = RegistryManager(app)
if asbool(static_files):
# Serve static files
static_app = StaticURLParser(config['pylons.paths']['static_files'])
app = Cascade([static_app, app])
return app

View File

@ -0,0 +1,26 @@
"""Routes configuration
The more specific and detailed routes should be defined first so they
may take precedent over the more generic routes. For more information
refer to the routes manual at http://routes.groovie.org/docs/
"""
from pylons import config
from routes import Mapper
def make_map():
"""Create, configure and return the routes Mapper"""
map = Mapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])
map.minimization = False
# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
map.connect('/error/{action}', controller='error')
map.connect('/error/{action}/{id}', controller='error')
# CUSTOM ROUTES HERE
map.connect('/{controller}/{action}')
map.connect('/{controller}/{action}/{id}')
return map

View File

@ -0,0 +1,46 @@
import cgi
from paste.urlparser import PkgResourcesParser
from pylons import request
from pylons.controllers.util import forward
from pylons.middleware import error_document_template
from webhelpers.html.builder import literal
from pylonstest.lib.base import BaseController
class ErrorController(BaseController):
"""Generates error documents as and when they are required.
The ErrorDocuments middleware forwards to ErrorController when error
related status codes are returned from the application.
This behaviour can be altered by changing the parameters to the
ErrorDocuments middleware in your config/middleware.py file.
"""
def document(self):
"""Render the error document"""
resp = request.environ.get('pylons.original_response')
content = literal(resp.body) or cgi.escape(request.GET.get('message', ''))
page = error_document_template % \
dict(prefix=request.environ.get('SCRIPT_NAME', ''),
code=cgi.escape(request.GET.get('code', str(resp.status_int))),
message=content)
return page
def img(self, id):
"""Serve Pylons' stock images"""
return self._serve_file('/'.join(['media/img', id]))
def style(self, id):
"""Serve Pylons' stock stylesheets"""
return self._serve_file('/'.join(['media/style', id]))
def _serve_file(self, path):
"""Call Paste's FileApp (a WSGI application) to serve the file
at the specified path
"""
request.environ['PATH_INFO'] = '/%s' % path
return forward(PkgResourcesParser('pylons', 'pylons'))

View File

@ -0,0 +1,16 @@
import logging
from pylons import request, response, session, tmpl_context as c
from pylons.controllers.util import abort, redirect_to
from pylonstest.lib.base import BaseController, render
log = logging.getLogger(__name__)
class HelloController(BaseController):
def index(self):
# Return a rendered template
#return render('/hello.mako')
# or, return a response
return 'Hello World'

View File

@ -0,0 +1,15 @@
"""The application's Globals object"""
class Globals(object):
"""Globals acts as a container for objects available throughout the
life of the application
"""
def __init__(self):
"""One instance of Globals is created during application
initialization and is available during requests via the
'app_globals' variable
"""

View File

@ -0,0 +1,15 @@
"""The base Controller API
Provides the BaseController class for subclassing.
"""
from pylons.controllers import WSGIController
from pylons.templating import render_mako as render
class BaseController(WSGIController):
def __call__(self, environ, start_response):
"""Invoke the Controller"""
# WSGIController.__call__ dispatches to the Controller method
# the request is routed to. This routing information is
# available in environ['pylons.routes_dict']
return WSGIController.__call__(self, environ, start_response)

View File

@ -0,0 +1,7 @@
"""Helper functions
Consists of functions to typically be used within templates, but also
available to Controllers. This module is available to templates as 'h'.
"""
# Import helpers as desired, or define your own, ie:
#from webhelpers.html.tags import checkbox, password

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Welcome to Pylons!</title>
<style type="text/css">
body {
font-family: arial, helvetica, sans-serif;
background-color: #ffc900;
background-image: url(bg.png);
background-repeat: repeat-x;
width:100%;
height:100%;
margin:0;
max-height: 100%;
padding:0;
border:none;
line-height:1.4;
}
#container {
color:white;
background-color:#111;
position: absolute;
left: 50%;
width: 500px;
margin-left: -300px;
padding:50px;
height:100%;
}
#footer {
margin: 120px 0 0 0;
padding: 3px;
text-align:center;
font-size:small;
background-color:#222;
letter-spacing: 1px;
}
h1 {
text-align:center;
font-size:xx-large;
font-weight:normal;
margin: 0 0 20px 0;
border:none;
padding:0;
letter-spacing: 5px;
}
h2 {
font-size:xx-large;
font-weight:normal;
margin: 0 0 20px 0;
border:none;
padding:0;
}
hr {
margin-bottom:30px;
border: 1px solid #222;
background-color: #222;
padding: 2px;
}
#logo {
background-image: url(signum8b_spk.png);
background-repeat: no-repeat;
height: 0;
overflow: hidden;
padding-top: 99px;
width: 239px;
}
#left {
float:left;
width:250px;
margin:0 50px 0 0;
border:none;
padding:0 0 0 10px;
}
#right {
margin:0 0 0 330px;
border:none;
padding:0;
}
ul {
list-style:none;
margin:0;
border:none;
padding:0;
}
a:visited {
color:white;
text-decoration:none;
}
a:link {
color:white;
text-decoration:none;
}</style>
</head>
<body>
<div id="container">
<h1>Welcome to <img src="pylons-logo.gif" alt="Logo displaying the word Pylons"
style="vertical-align:-15px; width: 250px;"/>
</h1>
<hr/>
<div id="left">
<h2>Let's begin!</h2>
<p>If you haven't used Pylons before, start with the <a href="http://pylonshq.com/docs/en/0.9.7/gettingstarted/"
style="text-decoration:underline;">beginners' tutorial</a>.</p>
</div>
<div id="right">
<h2>Help</h2>
<ul>
<li>
<a href="http://pylonshq.com/docs/en/0.9.7/">Official documentation</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/display/pylonsfaq/Home">FAQ</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/dashboard.action">Wiki</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/display/pylonscommunity/Home#Home-JointheMailingLists">Mailing list</a>
</li>
<li>
<a href="http://wiki.pylonshq.com/display/pylonscommunity/Home#Home-IRC">IRC</a>
</li>
<li>
<a href="http://pylonshq.com/project/pylonshq/roadmap">Bug tracker</a>
</li>
</ul>
</div>
<div id="footer">
<a href="http://www.pylonshq.com" style="color: #ccc; text-decoration:none;"
>www.pylonshq.com</a>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,36 @@
"""Pylons application test package
This package assumes the Pylons environment is already loaded, such as
when this script is imported from the `nosetests --with-pylons=test.ini`
command.
This module initializes the application via ``websetup`` (`paster
setup-app`) and provides the base testing objects.
"""
from unittest import TestCase
from paste.deploy import loadapp
from paste.script.appinstall import SetupCommand
from pylons import config, url
from routes.util import URLGenerator
from webtest import TestApp
import pylons.test
__all__ = ['environ', 'url', 'TestController']
# Invoke websetup with the current config file
SetupCommand('setup-app').run([config['__file__']])
environ = {}
class TestController(TestCase):
def __init__(self, *args, **kwargs):
if pylons.test.pylonsapp:
wsgiapp = pylons.test.pylonsapp
else:
wsgiapp = loadapp('config:%s' % config['__file__'])
self.app = TestApp(wsgiapp)
url._push_object(URLGenerator(config['routes.map'], environ))
TestCase.__init__(self, *args, **kwargs)

View File

@ -0,0 +1,7 @@
from pylonstest.tests import *
class TestHelloController(TestController):
def test_index(self):
response = self.app.get(url(controller='hello', action='index'))
# Test response...

View File

@ -0,0 +1,10 @@
"""Setup the pylonstest application"""
import logging
from pylonstest.config.environment import load_environment
log = logging.getLogger(__name__)
def setup_app(command, conf, vars):
"""Place any commands to setup pylonstest here"""
load_environment(conf.global_conf, conf.local_conf)

View File

@ -0,0 +1,31 @@
[egg_info]
tag_build = dev
tag_svn_revision = true
[easy_install]
find_links = http://www.pylonshq.com/download/
[nosetests]
with-pylons = test.ini
# Babel configuration
[compile_catalog]
domain = pylonstest
directory = pylonstest/i18n
statistics = true
[extract_messages]
add_comments = TRANSLATORS:
output_file = pylonstest/i18n/pylonstest.pot
width = 80
[init_catalog]
domain = pylonstest
input_file = pylonstest/i18n/pylonstest.pot
output_dir = pylonstest/i18n
[update_catalog]
domain = pylonstest
input_file = pylonstest/i18n/pylonstest.pot
output_dir = pylonstest/i18n
previous = true

View File

@ -0,0 +1,36 @@
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='pylonstest',
version='0.1',
description='',
author='',
author_email='',
url='',
install_requires=[
"Pylons>=0.9.7",
],
setup_requires=["PasteScript>=1.6.3"],
packages=find_packages(exclude=['ez_setup']),
include_package_data=True,
test_suite='nose.collector',
package_data={'pylonstest': ['i18n/*/LC_MESSAGES/*.mo']},
#message_extractors={'pylonstest': [
# ('**.py', 'python', None),
# ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
# ('public/**', 'ignore', None)]},
zip_safe=False,
paster_plugins=['PasteScript', 'Pylons'],
entry_points="""
[paste.app_factory]
main = pylonstest.config.middleware:make_app
[paste.app_install]
main = pylons.util:PylonsInstaller
""",
)

View File

@ -0,0 +1,21 @@
#
# pylonstest - Pylons testing environment configuration
#
# The %(here)s variable will be replaced with the parent directory of this file
#
[DEFAULT]
debug = true
# Uncomment and replace with the address which should receive any error reports
#email_to = you@yourdomain.com
smtp_server = localhost
error_email_from = paste@localhost
[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 5000
[app:main]
use = config:development.ini
# Add additional test specific configuration options as necessary.

13
examples/testing.py Normal file
View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -
def app(environ, start_response):
"""Simplest possible application object"""
data = 'Hello, World!\n'
status = '200 OK'
response_headers = [
('Content-type','text/plain'),
('Content-Length', len(data))
]
start_response(status, response_headers)
return [data]

View File

@ -112,7 +112,13 @@ class HttpRequest(object):
return environ
def start_response(self, status, response_headers):
def start_response(self, status, response_headers, exc_info=None):
if exc_info:
exc_info = None
elif self.start_response_called:
raise AssertionError("Response headers already set!")
self.response_status = status
for name, value in response_headers:
name = normalize_name(name)

View File

@ -11,7 +11,6 @@ read or restart etc ... It's based on TeeInput from unicorn.
"""
import os
import StringIO
import tempfile

View File

@ -9,7 +9,7 @@ from setuptools import setup, find_packages
setup(
name = 'gunicorn',
version = '0.2',
version = '0.3',
description = 'WSGI HTTP Server for UNIX',
long_description = file(
@ -38,7 +38,7 @@ setup(
packages = find_packages(),
include_package_data = True,
scripts = ['bin/gunicorn', 'bin/gunicorn_django'],
scripts = ['bin/gunicorn', 'bin/gunicorn_django', 'bin/gunicorn_paste'],
test_suite = 'nose.collector',