Add support for a `gunicorn_pecan` console script.

This commit is contained in:
Ryan Petrello
2013-01-09 22:00:50 -05:00
parent 890f86d980
commit 0bd4be184a
4 changed files with 140 additions and 22 deletions

View File

@@ -184,13 +184,4 @@ Pecan's default project::
$ pip install gunicorn
$ pecan create simpleapp && cd simpleapp
$ python setup.py develop
Next, let's create a new file in the project root::
# wsgi.py
from pecan.deploy import deploy
application = deploy('config.py')
...and then run it with::
$ gunicorn wsgi
$ gunicorn_pecan config.py

View File

@@ -132,3 +132,49 @@ class ServeCommand(BaseCommand):
print(' $ pip install watchdog')
else:
self._serve(app, conf)
def gunicorn_run():
"""
The ``gunicorn_pecan`` command for launching ``pecan`` applications
"""
try:
from gunicorn.app.wsgiapp import WSGIApplication
except ImportError as exc:
args = exc.args
arg0 = args[0] if args else ''
arg0 += ' (are you sure `gunicorn` is installed?)'
exc.args = (arg0,) + args[1:]
raise
class PecanApplication(WSGIApplication):
def init(self, parser, opts, args):
if len(args) != 1:
parser.error("No configuration file was specified.")
self.cfgfname = os.path.normpath(
os.path.join(os.getcwd(), args[0])
)
self.cfgfname = os.path.abspath(self.cfgfname)
if not os.path.exists(self.cfgfname):
parser.error("Config file not found: %s" % self.cfgfname)
from pecan.configuration import _runtime_conf, set_config
set_config(self.cfgfname, overwrite=True)
# If available, use the host and port from the pecan config file
cfg = {}
if _runtime_conf.get('server'):
server = _runtime_conf['server']
if hasattr(server, 'host') and hasattr(server, 'port'):
cfg['bind'] = '%s:%s' % (
server.host, server.port
)
return cfg
def load(self):
from pecan.deploy import deploy
return deploy(self.cfgfname)
PecanApplication("%(prog)s [OPTIONS] config.py").run()

View File

@@ -182,13 +182,13 @@ class TestTemplateBuilds(unittest.TestCase):
def setUp(self):
# Make a temp install location and record the cwd
self.install()
self.install_scaffolded_package()
def tearDown(self):
shutil.rmtree(self.install_dir)
os.chdir(self.cwd)
def install(self):
def create_virtualenv(self):
# Create a new virtualenv in the temp install location
import virtualenv
virtualenv.create_environment(
@@ -198,6 +198,8 @@ class TestTemplateBuilds(unittest.TestCase):
# chdir into the pecan source
os.chdir(pkg_resources.get_distribution('pecan').location)
def install_scaffolded_package(self):
self.create_virtualenv()
py_exe = os.path.join(self.install_dir, 'bin', 'python')
pecan_exe = os.path.join(self.install_dir, 'bin', 'pecan')
@@ -219,6 +221,21 @@ class TestTemplateBuilds(unittest.TestCase):
'develop'
])
def install_dependency(self, name):
pip_exe = os.path.join(self.install_dir, 'bin', 'pip')
proc = subprocess.Popen([
pip_exe,
'install',
name
])
proc.wait()
return os.path.join(
self.install_dir,
'bin',
name
)
def poll(self, proc):
limit = 30
for i in range(limit):
@@ -228,7 +245,7 @@ class TestTemplateBuilds(unittest.TestCase):
if proc.returncode is None:
break
elif i == limit: # pragma: no cover
raise RuntimeError("pecan serve config.py didn't start.")
raise RuntimeError("Server process didn't start.")
time.sleep(.1)
@unittest.skipUnless(has_internet(), 'Internet connectivity unavailable.')
@@ -330,16 +347,9 @@ class TestTemplateBuilds(unittest.TestCase):
)
def test_project_passes_pep8(self):
# Install pep8
pip_exe = os.path.join(self.install_dir, 'bin', 'pip')
proc = subprocess.Popen([
pip_exe,
'install',
'pep8'
])
proc.wait()
pep8_exe = self.install_dependency('pep8')
# Run pep8 on setup.py and the project
pep8_exe = os.path.join(self.install_dir, 'bin', 'pep8')
proc = subprocess.Popen([
pep8_exe,
'setup.py',
@@ -353,3 +363,72 @@ class TestTemplateBuilds(unittest.TestCase):
# No output == good
output = proc.stdout.read()
assert output == ''
class TestGunicornServeCommand(TestTemplateBuilds):
def create_virtualenv(self):
super(TestGunicornServeCommand, self).create_virtualenv()
# Install gunicorn
self.gunicorn_exe = self.install_dependency('gunicorn')
def install_dependency(self, name):
return super(
TestGunicornServeCommand,
self
).install_dependency(name).replace(
'gunicorn', 'gunicorn_pecan'
)
def poll_gunicorn(self, proc, port):
try:
self.poll(proc)
retries = 30
while True:
retries -= 1
if retries < 0: # pragma: nocover
raise RuntimeError(
"The gunicorn server has not replied within 3 seconds."
)
try:
# ...and that it's serving (valid) content...
resp = urllib2.urlopen('http://localhost:%d/' % port)
assert resp.getcode() == 200
assert 'This is a sample Pecan project.' in resp.read()
except urllib2.URLError:
pass
else:
break
time.sleep(.1)
finally:
proc.terminate()
@unittest.skipUnless(has_internet(), 'Internet connectivity unavailable.')
@unittest.skipUnless(
getattr(pecan, '__run_all_tests__', False) is True,
'Skipping (slow). To run, `$ python setup.py test --functional.`'
)
def test_serve_from_config(self):
# Start the server
proc = subprocess.Popen([
self.gunicorn_exe,
'config.py'
])
self.poll_gunicorn(proc, 8080)
@unittest.skipUnless(has_internet(), 'Internet connectivity unavailable.')
@unittest.skipUnless(
getattr(pecan, '__run_all_tests__', False) is True,
'Skipping (slow). To run, `$ python setup.py test --functional.`'
)
def test_serve_with_custom_bind(self):
# Start the server
proc = subprocess.Popen([
self.gunicorn_exe,
'--bind=0.0.0.0:9191',
'config.py'
])
self.poll_gunicorn(proc, 9191)

View File

@@ -31,7 +31,8 @@ tests_require = requirements + [
'virtualenv',
'Genshi',
'Kajiki',
'Jinja'
'Jinja',
'gunicorn'
]
if sys.version_info < (2, 7):
tests_require += ['unittest2']
@@ -102,5 +103,6 @@ setup(
base = pecan.scaffolds:BaseScaffold
[console_scripts]
pecan = pecan.commands:CommandRunner.handle_command_line
gunicorn_pecan = pecan.commands.serve:gunicorn_run
"""
)