diff options
| author | Chris McDonough <chrism@plope.com> | 2012-11-20 22:50:05 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2012-11-20 22:50:05 -0500 |
| commit | 1dc4a9066be9d90a317127781b0ac0046df85cd9 (patch) | |
| tree | 71da0a57bfdb591d2acad62b108b8d3ba902ab0a | |
| parent | b5e4443dd28f3d4ed6e767e06d658838575d159e (diff) | |
| parent | 6ba0fc746b15262a018cd271efc14089a71d8297 (diff) | |
| download | pyramid-1dc4a9066be9d90a317127781b0ac0046df85cd9.tar.gz pyramid-1dc4a9066be9d90a317127781b0ac0046df85cd9.tar.bz2 pyramid-1dc4a9066be9d90a317127781b0ac0046df85cd9.zip | |
Merge branch 'sontek-fix_commands'
| -rw-r--r-- | CHANGES.txt | 5 | ||||
| -rw-r--r-- | docs/api/paster.rst | 2 | ||||
| -rw-r--r-- | pyramid/paster.py | 22 | ||||
| -rw-r--r-- | pyramid/scripts/__init__.py | 1 | ||||
| -rw-r--r-- | pyramid/scripts/common.py | 15 | ||||
| -rw-r--r-- | pyramid/scripts/prequest.py | 5 | ||||
| -rw-r--r-- | pyramid/scripts/proutes.py | 5 | ||||
| -rw-r--r-- | pyramid/scripts/pserve.py | 47 | ||||
| -rw-r--r-- | pyramid/scripts/pshell.py | 4 | ||||
| -rw-r--r-- | pyramid/scripts/ptweens.py | 3 | ||||
| -rw-r--r-- | pyramid/scripts/pviews.py | 7 | ||||
| -rw-r--r-- | pyramid/tests/test_paster.py | 35 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_common.py | 13 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_prequest.py | 4 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_proutes.py | 22 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_pserve.py | 43 |
16 files changed, 183 insertions, 50 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 51266d15f..5344cb7d1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,11 @@ Features class. A similar microoptimization was done to ``pyramid.request.Request.is_response``. +- Make it possible to use variable arguments on ``p*`` commands (``pserve``, + ``pshell``, ``pviews``, etc) in the form ``a=1 b=2`` so you can fill in + values in parameterized ``.ini`` file, e.g. ``pshell etc/development.ini + http_port=8080``. See https://github.com/Pylons/pyramid/pull/714 + Bug Fixes --------- diff --git a/docs/api/paster.rst b/docs/api/paster.rst index 3f7a1c364..bde128e05 100644 --- a/docs/api/paster.rst +++ b/docs/api/paster.rst @@ -7,7 +7,7 @@ .. autofunction:: bootstrap - .. autofunction:: get_app(config_uri, name=None) + .. autofunction:: get_app(config_uri, name=None, options=None) .. autofunction:: get_appsettings(config_uri, name=None) diff --git a/pyramid/paster.py b/pyramid/paster.py index b0e4d7933..ce07d1fe0 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -9,17 +9,27 @@ from pyramid.compat import configparser from logging.config import fileConfig from pyramid.scripting import prepare -def get_app(config_uri, name=None, loadapp=loadapp): +def get_app(config_uri, name=None, options=None, loadapp=loadapp): """ Return the WSGI application named ``name`` in the PasteDeploy config file specified by ``config_uri``. + ``options``, if passed, should be a dictionary used as variable assignments + like ``{'http_port': 8080}``. This is useful if e.g. ``%(http_port)s`` is + used in the config file. + If the ``name`` is None, this will attempt to parse the name from the ``config_uri`` string expecting the format ``inifile#name``. If no name is found, the name will default to "main".""" path, section = _getpathsec(config_uri, name) config_name = 'config:%s' % path here_dir = os.getcwd() - app = loadapp(config_name, name=section, relative_to=here_dir) + if options: + kw = {'global_conf': options} + else: + kw = {} + + app = loadapp(config_name, name=section, relative_to=here_dir, **kw) + return app def get_appsettings(config_uri, name=None, appconfig=appconfig): @@ -63,7 +73,7 @@ def _getpathsec(config_uri, name): section = name return path, section -def bootstrap(config_uri, request=None): +def bootstrap(config_uri, request=None, options=None): """ Load a WSGI application from the PasteDeploy config file specified by ``config_uri``. The environment will be configured as if it is currently serving ``request``, leaving a natural environment in place @@ -103,10 +113,14 @@ def bootstrap(config_uri, request=None): for you if none is provided. You can mutate the request's ``environ`` later to setup a specific host/port/scheme/etc. + ``options`` Is passed to get_app for use as variable assignments like + {'http_port': 8080} and then use %(http_port)s in the + config file. + See :ref:`writing_a_script` for more information about how to use this function. """ - app = get_app(config_uri) + app = get_app(config_uri, options=options) env = prepare(request) env['app'] = app return env diff --git a/pyramid/scripts/__init__.py b/pyramid/scripts/__init__.py index ed88d78b4..5bb534f79 100644 --- a/pyramid/scripts/__init__.py +++ b/pyramid/scripts/__init__.py @@ -1,2 +1 @@ # package - diff --git a/pyramid/scripts/common.py b/pyramid/scripts/common.py index dfff26449..cbc172e9b 100644 --- a/pyramid/scripts/common.py +++ b/pyramid/scripts/common.py @@ -2,6 +2,21 @@ import os from pyramid.compat import configparser from logging.config import fileConfig +def parse_vars(args): + """ + Given variables like ``['a=b', 'c=d']`` turns it into ``{'a': + 'b', 'c': 'd'}`` + """ + result = {} + for arg in args: + if '=' not in arg: + raise ValueError( + 'Variable assignment %r invalid (no "=")' + % arg) + name, value = arg.split('=', 1) + result[name] = value + return result + def logging_file_config(config_file, fileConfig=fileConfig, configparser=configparser): """ diff --git a/pyramid/scripts/prequest.py b/pyramid/scripts/prequest.py index 13406372a..da6b8cc14 100644 --- a/pyramid/scripts/prequest.py +++ b/pyramid/scripts/prequest.py @@ -5,6 +5,7 @@ import textwrap from pyramid.compat import url_unquote from pyramid.request import Request from pyramid.paster import get_app +from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PRequestCommand(argv, quiet) @@ -101,7 +102,9 @@ class PRequestCommand(object): name, value = item.split(':', 1) headers[name] = value.strip() - app = self.get_app(app_spec, self.options.app_name) + app = self.get_app(app_spec, self.options.app_name, + options=parse_vars(self.args[2:])) + request_method = (self.options.method or 'GET').upper() environ = { diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py index f64107d2b..49e19deca 100644 --- a/pyramid/scripts/proutes.py +++ b/pyramid/scripts/proutes.py @@ -3,6 +3,7 @@ import sys import textwrap from pyramid.paster import bootstrap +from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PRoutesCommand(argv, quiet) @@ -47,12 +48,14 @@ class PRoutesCommand(object): if not self.args: self.out('requires a config file argument') return 2 + from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IView from zope.interface import Interface config_uri = self.args[0] - env = self.bootstrap[0](config_uri) + + env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:])) registry = env['registry'] mapper = self._get_mapper(registry) if mapper is not None: diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index 9fbf0729a..6c7e1d654 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -22,12 +22,15 @@ import threading import time import traceback -from paste.deploy import loadapp, loadserver +from paste.deploy import loadserver +from paste.deploy import loadapp from pyramid.compat import WIN from pyramid.paster import setup_logging +from pyramid.scripts.common import parse_vars + MAXFD = 1024 if WIN and not hasattr(os, 'kill'): # pragma: no cover @@ -160,6 +163,15 @@ class PServeCommand(object): if not self.quiet: print(msg) + def get_options(self): + if (len(self.args) > 1 + and self.args[1] in self.possible_subcommands): + restvars = self.args[2:] + else: + restvars = self.args[1:] + + return parse_vars(restvars) + def run(self): # pragma: no cover if self.options.stop_daemon: return self.stop_daemon() @@ -176,13 +188,12 @@ class PServeCommand(object): self.out('You must give a config file') return 2 app_spec = self.args[0] + if (len(self.args) > 1 and self.args[1] in self.possible_subcommands): cmd = self.args[1] - restvars = self.args[2:] else: cmd = None - restvars = self.args[1:] if self.options.reload: if os.environ.get(self._reloader_environ_key): @@ -218,7 +229,9 @@ class PServeCommand(object): self.options.daemon = True app_name = self.options.app_name - vars = self.parse_vars(restvars) + + vars = self.get_options() + if not self._scheme_re.search(app_spec): app_spec = 'config:' + app_spec server_name = self.options.server_name @@ -286,8 +299,9 @@ class PServeCommand(object): server = self.loadserver(server_spec, name=server_name, relative_to=base, global_conf=vars) - app = self.loadapp(app_spec, name=app_name, - relative_to=base, global_conf=vars) + + app = self.loadapp(app_spec, name=app_name, relative_to=base, + global_conf=vars) if self.verbose > 0: if hasattr(os, 'getpid'): @@ -310,27 +324,12 @@ class PServeCommand(object): serve() - def loadserver(self, server_spec, name, relative_to, **kw):# pragma:no cover - return loadserver( - server_spec, name=name, relative_to=relative_to, **kw) - def loadapp(self, app_spec, name, relative_to, **kw): # pragma: no cover return loadapp(app_spec, name=name, relative_to=relative_to, **kw) - def parse_vars(self, args): - """ - Given variables like ``['a=b', 'c=d']`` turns it into ``{'a': - 'b', 'c': 'd'}`` - """ - result = {} - for arg in args: - if '=' not in arg: - raise ValueError( - 'Variable assignment %r invalid (no "=")' - % arg) - name, value = arg.split('=', 1) - result[name] = value - return result + def loadserver(self, server_spec, name, relative_to, **kw):# pragma:no cover + return loadserver( + server_spec, name=name, relative_to=relative_to, **kw) def quote_first_command_arg(self, arg): # pragma: no cover """ diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 7a21eaf98..f74402928 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -9,6 +9,8 @@ from pyramid.paster import bootstrap from pyramid.paster import setup_logging +from pyramid.scripts.common import parse_vars + def main(argv=sys.argv, quiet=False): command = PShellCommand(argv, quiet) return command.run() @@ -87,7 +89,7 @@ class PShellCommand(object): self.pshell_file_config(config_file) # bootstrap the environ - env = self.bootstrap[0](config_uri) + env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:])) # remove the closer from the env closer = env.pop('closer') diff --git a/pyramid/scripts/ptweens.py b/pyramid/scripts/ptweens.py index d3e17db58..5fe2fa120 100644 --- a/pyramid/scripts/ptweens.py +++ b/pyramid/scripts/ptweens.py @@ -7,6 +7,7 @@ from pyramid.interfaces import ITweens from pyramid.tweens import MAIN from pyramid.tweens import INGRESS from pyramid.paster import bootstrap +from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PTweensCommand(argv, quiet) @@ -62,7 +63,7 @@ class PTweensCommand(object): self.out('Requires a config file argument') return 2 config_uri = self.args[0] - env = self.bootstrap[0](config_uri) + env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:])) registry = env['registry'] tweens = self._get_tweens(registry) if tweens is not None: diff --git a/pyramid/scripts/pviews.py b/pyramid/scripts/pviews.py index 60aecb9bb..081c13e9d 100644 --- a/pyramid/scripts/pviews.py +++ b/pyramid/scripts/pviews.py @@ -4,6 +4,7 @@ import textwrap from pyramid.interfaces import IMultiView from pyramid.paster import bootstrap +from pyramid.scripts.common import parse_vars def main(argv=sys.argv, quiet=False): command = PViewsCommand(argv, quiet) @@ -230,10 +231,12 @@ class PViewsCommand(object): if len(self.args) < 2: self.out('Command requires a config file arg and a url arg') return 2 - config_uri, url = self.args + config_uri = self.args[0] + url = self.args[1] + if not url.startswith('/'): url = '/%s' % url - env = self.bootstrap[0](config_uri) + env = self.bootstrap[0](config_uri, options=parse_vars(self.args[2:])) registry = env['registry'] view = self._find_view(url, registry) self.out('') diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py index d94b46a9f..b72e0e6b6 100644 --- a/pyramid/tests/test_paster.py +++ b/pyramid/tests/test_paster.py @@ -2,14 +2,16 @@ import os import unittest class Test_get_app(unittest.TestCase): - def _callFUT(self, config_file, section_name, loadapp): + def _callFUT(self, config_file, section_name, options=None, loadapp=None): from pyramid.paster import get_app - return get_app(config_file, section_name, loadapp) + return get_app( + config_file, section_name, options=options, loadapp=loadapp + ) def test_it(self): app = DummyApp() loadapp = DummyLoadWSGI(app) - result = self._callFUT('/foo/bar/myapp.ini', 'myapp', loadapp) + result = self._callFUT('/foo/bar/myapp.ini', 'myapp', loadapp=loadapp) self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(loadapp.section_name, 'myapp') self.assertEqual(loadapp.relative_to, os.getcwd()) @@ -18,7 +20,9 @@ class Test_get_app(unittest.TestCase): def test_it_with_hash(self): app = DummyApp() loadapp = DummyLoadWSGI(app) - result = self._callFUT('/foo/bar/myapp.ini#myapp', None, loadapp) + result = self._callFUT( + '/foo/bar/myapp.ini#myapp', None, loadapp=loadapp + ) self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(loadapp.section_name, 'myapp') self.assertEqual(loadapp.relative_to, os.getcwd()) @@ -27,12 +31,30 @@ class Test_get_app(unittest.TestCase): def test_it_with_hash_and_name_override(self): app = DummyApp() loadapp = DummyLoadWSGI(app) - result = self._callFUT('/foo/bar/myapp.ini#myapp', 'yourapp', loadapp) + result = self._callFUT( + '/foo/bar/myapp.ini#myapp', 'yourapp', loadapp=loadapp + ) self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') self.assertEqual(loadapp.section_name, 'yourapp') self.assertEqual(loadapp.relative_to, os.getcwd()) self.assertEqual(result, app) + def test_it_with_options(self): + app = DummyApp() + loadapp = DummyLoadWSGI(app) + options = {'a':1} + result = self._callFUT( + '/foo/bar/myapp.ini#myapp', + 'yourapp', + loadapp=loadapp, + options=options, + ) + self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini') + self.assertEqual(loadapp.section_name, 'yourapp') + self.assertEqual(loadapp.relative_to, os.getcwd()) + self.assertEqual(loadapp.kw, {'global_conf':options}) + self.assertEqual(result, app) + class Test_get_appsettings(unittest.TestCase): def _callFUT(self, config_file, section_name, appconfig): from pyramid.paster import get_appsettings @@ -132,10 +154,11 @@ class DummyLoadWSGI: def __init__(self, result): self.result = result - def __call__(self, config_name, name=None, relative_to=None): + def __call__(self, config_name, name=None, relative_to=None, **kw): self.config_name = config_name self.section_name = name self.relative_to = relative_to + self.kw = kw return self.result class DummyApp: diff --git a/pyramid/tests/test_scripts/test_common.py b/pyramid/tests/test_scripts/test_common.py index c3c792ca4..13ab0ae6a 100644 --- a/pyramid/tests/test_scripts/test_common.py +++ b/pyramid/tests/test_scripts/test_common.py @@ -17,6 +17,19 @@ class Test_logging_file_config(unittest.TestCase): def fileConfig(self, config_file, dict): return config_file, dict +class TestParseVars(unittest.TestCase): + def test_parse_vars_good(self): + from pyramid.scripts.common import parse_vars + vars = ['a=1', 'b=2'] + result = parse_vars(vars) + self.assertEqual(result, {'a': '1', 'b': '2'}) + + def test_parse_vars_bad(self): + from pyramid.scripts.common import parse_vars + vars = ['a'] + self.assertRaises(ValueError, parse_vars, vars) + + class DummyConfigParser(object): def read(self, x): pass diff --git a/pyramid/tests/test_scripts/test_prequest.py b/pyramid/tests/test_scripts/test_prequest.py index cf7af4218..91d2b322a 100644 --- a/pyramid/tests/test_scripts/test_prequest.py +++ b/pyramid/tests/test_scripts/test_prequest.py @@ -13,9 +13,11 @@ class TestPRequestCommand(unittest.TestCase): cmd.out = self.out return cmd - def get_app(self, spec, app_name=None): + def get_app(self, spec, app_name=None, options=None): self._spec = spec self._app_name = app_name + self._options = options or {} + def helloworld(environ, start_request): self._environ = environ self._path_info = environ['PATH_INFO'] diff --git a/pyramid/tests/test_scripts/test_proutes.py b/pyramid/tests/test_scripts/test_proutes.py index fad8c1895..25a3cd2e3 100644 --- a/pyramid/tests/test_scripts/test_proutes.py +++ b/pyramid/tests/test_scripts/test_proutes.py @@ -12,6 +12,28 @@ class TestPRoutesCommand(unittest.TestCase): cmd.args = ('/foo/bar/myapp.ini#myapp',) return cmd + def test_good_args(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (dummy.DummyBootstrap(),) + cmd.args = ('/foo/bar/myapp.ini#myapp', 'a=1') + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + cmd._get_mapper = lambda *arg: mapper + L = [] + cmd.out = lambda msg: L.append(msg) + cmd.run() + self.assertTrue('<unknown>' in ''.join(L)) + + def test_bad_args(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (dummy.DummyBootstrap(),) + cmd.args = ('/foo/bar/myapp.ini#myapp', 'a') + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + cmd._get_mapper = lambda *arg: mapper + + self.assertRaises(ValueError, cmd.run) + def test_no_routes(self): command = self._makeOne() mapper = dummy.DummyMapper() diff --git a/pyramid/tests/test_scripts/test_pserve.py b/pyramid/tests/test_scripts/test_pserve.py index d7b252d92..6e4b0f17d 100644 --- a/pyramid/tests/test_scripts/test_pserve.py +++ b/pyramid/tests/test_scripts/test_pserve.py @@ -22,6 +22,12 @@ class TestPServeCommand(unittest.TestCase): def out(self, msg): self.out_.write(msg) + def _get_server(*args, **kwargs): + def server(app): + return '' + + return server + def _getTargetClass(self): from pyramid.scripts.pserve import PServeCommand return PServeCommand @@ -193,16 +199,39 @@ class TestPServeCommand(unittest.TestCase): msg = 'PID in %s is not valid (deleting)' % fn self.assertEqual(self.out_.getvalue(), msg) - def test_parse_vars_good(self): - vars = ['a=1', 'b=2'] - inst = self._makeOne('development.ini') - result = inst.parse_vars(vars) + def test_get_options_with_command(self): + inst = self._makeOne() + inst.args = ['foo', 'stop', 'a=1', 'b=2'] + result = inst.get_options() self.assertEqual(result, {'a': '1', 'b': '2'}) + def test_get_options_no_command(self): + inst = self._makeOne() + inst.args = ['foo', 'a=1', 'b=2'] + result = inst.get_options() + self.assertEqual(result, {'a': '1', 'b': '2'}) + + def test_parse_vars_good(self): + from pyramid.tests.test_scripts.dummy import DummyApp + + inst = self._makeOne('development.ini', 'a=1', 'b=2') + inst.loadserver = self._get_server + + + app = DummyApp() + + def get_app(*args, **kwargs): + app.global_conf = kwargs.get('global_conf', None) + + inst.loadapp = get_app + inst.run() + + self.assertEqual(app.global_conf, {'a': '1', 'b': '2'}) + def test_parse_vars_bad(self): - vars = ['a'] - inst = self._makeOne('development.ini') - self.assertRaises(ValueError, inst.parse_vars, vars) + inst = self._makeOne('development.ini', 'a') + inst.loadserver = self._get_server + self.assertRaises(ValueError, inst.run) class Test_read_pidfile(unittest.TestCase): def _callFUT(self, filename): |
