From bb69a069791074dd8557da60a857a701d370a5c4 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 29 Sep 2011 03:04:03 -0400 Subject: experimental use of glue --- pyramid/paster.py | 22 ++++++---------------- pyramid/scaffolds/__init__.py | 24 +++++++----------------- setup.py | 12 +++--------- 3 files changed, 16 insertions(+), 42 deletions(-) diff --git a/pyramid/paster.py b/pyramid/paster.py index c9c356a92..1c06ca79b 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -3,18 +3,8 @@ import sys from code import interact import zope.deprecation - -try: - from paste.deploy import loadapp -except ImportError: # pragma: no cover - def loadapp(*arg, **kw): - raise NotImplementedError - -try: - from paste.script.command import Command -except ImportError: # pragma: no cover - class Command: - pass +from paste.deploy import loadapp +from glue.command import Command from pyramid.interfaces import IMultiView from pyramid.interfaces import ITweens @@ -121,7 +111,7 @@ class PShellCommand(PCommand): Example:: - $ paster pshell myapp.ini#main + $ glue pshell myapp.ini#main .. note:: If you do not point the loader directly at the section of the ini file containing your :app:`Pyramid` application, the @@ -294,7 +284,7 @@ class PRoutesCommand(PCommand): Example:: - $ paster proutes myapp.ini#main + $ glue proutes myapp.ini#main """ summary = "Print all URL dispatch routes related to a Pyramid application" @@ -358,7 +348,7 @@ class PViewsCommand(PCommand): Example:: - $ paster proutes myapp.ini#main url + $ glue proutes myapp.ini#main url """ summary = "Print all views in an application that might match a URL" @@ -599,7 +589,7 @@ class PTweensCommand(PCommand): Example:: - $ paster ptweens myapp.ini#main + $ glue ptweens myapp.ini#main """ summary = "Print all tweens related to a Pyramid application" diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py index 673f22e21..8661038bf 100644 --- a/pyramid/scaffolds/__init__.py +++ b/pyramid/scaffolds/__init__.py @@ -1,22 +1,16 @@ +import binascii import os -from pyramid.compat import print_ +from pyramid.compat import ( + print_, + native_ + ) -try: - from paste.script.templates import Template -except ImportError: # pragma: no cover - class Template: - pass - -try: - from paste.util.template import paste_script_template_renderer -except ImportError: # pragma: no cover - def paste_script_template_renderer(): - pass +from glue.template import Template class PyramidTemplate(Template): def pre(self, command, output_dir, vars): - vars['random_string'] = os.urandom(20).encode('hex') + vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' @@ -34,20 +28,16 @@ class PyramidTemplate(Template): class StarterProjectTemplate(PyramidTemplate): _template_dir = 'starter' summary = 'pyramid starter project' - template_renderer = staticmethod(paste_script_template_renderer) class ZODBProjectTemplate(PyramidTemplate): _template_dir = 'zodb' summary = 'pyramid ZODB starter project' - template_renderer = staticmethod(paste_script_template_renderer) class RoutesAlchemyProjectTemplate(PyramidTemplate): _template_dir = 'routesalchemy' summary = 'pyramid SQLAlchemy project using url dispatch (no traversal)' - template_renderer = staticmethod(paste_script_template_renderer) class AlchemyProjectTemplate(PyramidTemplate): _template_dir = 'alchemy' summary = 'pyramid SQLAlchemy project using traversal' - template_renderer = staticmethod(paste_script_template_renderer) diff --git a/setup.py b/setup.py index 3334a01d6..af0094856 100644 --- a/setup.py +++ b/setup.py @@ -39,15 +39,9 @@ install_requires=[ 'zope.deprecation >= 3.5.0', # py3 compat 'venusian >= 1.0a1', # ``onerror`` 'translationstring >= 0.4', # py3 compat + 'glue', ] -if not PY3: - install_requires.extend([ - 'Paste > 1.7', # temp version pin to prevent PyPi install failure :-( - 'PasteDeploy', - 'PasteScript >= 1.7.4', # "here" in logging fileConfig - ]) - tests_require = install_requires + [ 'WebTest >= 1.3.1', # py3 compat 'virtualenv', @@ -90,12 +84,12 @@ setup(name='pyramid', tests_require = tests_require, test_suite="pyramid.tests", entry_points = """\ - [paste.paster_create_template] + [glue.create_template] pyramid_starter=pyramid.scaffolds:StarterProjectTemplate pyramid_zodb=pyramid.scaffolds:ZODBProjectTemplate pyramid_routesalchemy=pyramid.scaffolds:RoutesAlchemyProjectTemplate pyramid_alchemy=pyramid.scaffolds:AlchemyProjectTemplate - [paste.paster_command] + [glue.command] pshell=pyramid.paster:PShellCommand proutes=pyramid.paster:PRoutesCommand pviews=pyramid.paster:PViewsCommand -- cgit v1.2.3 From 24b5c1f0276fd38754d609915a7236ba95c4e69d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 29 Sep 2011 03:51:05 -0400 Subject: glue it up --- pyramid/request.py | 2 +- pyramid/scaffolds/alchemy/+package+/models.py | 2 +- pyramid/scaffolds/alchemy/development.ini_tmpl | 2 +- pyramid/scaffolds/alchemy/production.ini_tmpl | 2 +- pyramid/scaffolds/routesalchemy/+package+/models.py | 2 +- pyramid/scaffolds/routesalchemy/+package+/views.py_tmpl | 2 +- pyramid/scaffolds/routesalchemy/development.ini_tmpl | 2 +- pyramid/scaffolds/routesalchemy/production.ini_tmpl | 2 +- pyramid/scaffolds/starter/development.ini_tmpl | 2 +- pyramid/scaffolds/starter/production.ini_tmpl | 2 +- pyramid/scaffolds/zodb/development.ini_tmpl | 2 +- pyramid/scaffolds/zodb/production.ini_tmpl | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pyramid/request.py b/pyramid/request.py index eae83da6f..d98175feb 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -328,7 +328,7 @@ class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin, :class:`pyramid.exceptions.ConfigurationError` will be raised""" factory = self.registry.queryUtility(ISessionFactory) if factory is None: - raise ConfigurationError( + raise AttributeError( 'No session factory registered ' '(see the Sessions chapter of the Pyramid documentation)') return factory(self) diff --git a/pyramid/scaffolds/alchemy/+package+/models.py b/pyramid/scaffolds/alchemy/+package+/models.py index 4a93ecf8a..2685da5bb 100755 --- a/pyramid/scaffolds/alchemy/+package+/models.py +++ b/pyramid/scaffolds/alchemy/+package+/models.py @@ -64,7 +64,7 @@ def root_factory(request): def populate(): session = DBSession() - model = MyModel(name=u'test name', value=55) + model = MyModel(name='test name', value=55) session.add(model) session.flush() transaction.commit() diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl index 05e7ac296..b7c06aff3 100644 --- a/pyramid/scaffolds/alchemy/development.ini_tmpl +++ b/pyramid/scaffolds/alchemy/development.ini_tmpl @@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl index fad5baa30..dc17e8338 100644 --- a/pyramid/scaffolds/alchemy/production.ini_tmpl +++ b/pyramid/scaffolds/alchemy/production.ini_tmpl @@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/routesalchemy/+package+/models.py b/pyramid/scaffolds/routesalchemy/+package+/models.py index cffb8f5ef..bef483d3a 100644 --- a/pyramid/scaffolds/routesalchemy/+package+/models.py +++ b/pyramid/scaffolds/routesalchemy/+package+/models.py @@ -27,7 +27,7 @@ class MyModel(Base): def populate(): session = DBSession() - model = MyModel(name=u'root', value=55) + model = MyModel(name='root', value=55) session.add(model) session.flush() transaction.commit() diff --git a/pyramid/scaffolds/routesalchemy/+package+/views.py_tmpl b/pyramid/scaffolds/routesalchemy/+package+/views.py_tmpl index 86cc02e41..45532b47b 100644 --- a/pyramid/scaffolds/routesalchemy/+package+/views.py_tmpl +++ b/pyramid/scaffolds/routesalchemy/+package+/views.py_tmpl @@ -3,5 +3,5 @@ from {{package}}.models import MyModel def my_view(request): dbsession = DBSession() - root = dbsession.query(MyModel).filter(MyModel.name==u'root').first() + root = dbsession.query(MyModel).filter(MyModel.name=='root').first() return {'root':root, 'project':'{{project}}'} diff --git a/pyramid/scaffolds/routesalchemy/development.ini_tmpl b/pyramid/scaffolds/routesalchemy/development.ini_tmpl index 05e7ac296..b7c06aff3 100644 --- a/pyramid/scaffolds/routesalchemy/development.ini_tmpl +++ b/pyramid/scaffolds/routesalchemy/development.ini_tmpl @@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/routesalchemy/production.ini_tmpl b/pyramid/scaffolds/routesalchemy/production.ini_tmpl index fad5baa30..dc17e8338 100644 --- a/pyramid/scaffolds/routesalchemy/production.ini_tmpl +++ b/pyramid/scaffolds/routesalchemy/production.ini_tmpl @@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl index 2d2f6e354..c47fcec22 100644 --- a/pyramid/scaffolds/starter/development.ini_tmpl +++ b/pyramid/scaffolds/starter/development.ini_tmpl @@ -10,7 +10,7 @@ pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl index b04f37ab3..0634072c0 100644 --- a/pyramid/scaffolds/starter/production.ini_tmpl +++ b/pyramid/scaffolds/starter/production.ini_tmpl @@ -9,7 +9,7 @@ pyramid.debug_templates = false pyramid.default_locale_name = en [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl index 29da8ee38..661e042a2 100644 --- a/pyramid/scaffolds/zodb/development.ini_tmpl +++ b/pyramid/scaffolds/zodb/development.ini_tmpl @@ -14,7 +14,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl index 0275a33a7..2378f9751 100644 --- a/pyramid/scaffolds/zodb/production.ini_tmpl +++ b/pyramid/scaffolds/zodb/production.ini_tmpl @@ -13,7 +13,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:glue#wsgiref host = 0.0.0.0 port = 6543 -- cgit v1.2.3 From a30a39ae06b26f57c0c393bbfd3e0cc0b540df14 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 05:06:49 -0400 Subject: add a pcreate command --- pyramid/scripts/__init__.py | 2 + pyramid/scripts/pcreate.py | 98 +++++++++++++++++++++++++++++++++++++++++++++ setup.py | 11 ++--- 3 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 pyramid/scripts/__init__.py create mode 100644 pyramid/scripts/pcreate.py diff --git a/pyramid/scripts/__init__.py b/pyramid/scripts/__init__.py new file mode 100644 index 000000000..ed88d78b4 --- /dev/null +++ b/pyramid/scripts/__init__.py @@ -0,0 +1,2 @@ +# package + diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py new file mode 100644 index 000000000..bece38e68 --- /dev/null +++ b/pyramid/scripts/pcreate.py @@ -0,0 +1,98 @@ +import optparse +import os +import pkg_resources +import re +import sys + +_bad_chars_re = re.compile('[^a-zA-Z0-9_]') + +def main(argv=sys.argv): + command = CreateCommand(argv) + return command.run() + +class CreateCommand(object): + verbose = True + interactive = False + simulate = False + usage = "usage: %prog [options] project" + parser = optparse.OptionParser(usage) + parser.add_option('-s', '--scaffold', + dest='scaffold_name', + action='append', + help=("Add a scaffold to the create process " + "(multiple -s args accepted)")) + parser.add_option('-l', '--list', + dest='list', + action='store_true', + help="List all available scaffold names") + parser.add_option('--simulate', + dest='simulate', + action='store_true', + help='Simulate but do no work') + parser.add_option('--overwrite', + dest='overwrite', + action='store_true', + help='Always overwrite') + + def __init__(self, argv): + self.options, self.args = self.parser.parse_args(argv[1:]) + self.scaffolds = all_scaffolds() + self.available_scaffoldnames = [x.name for x in self.scaffolds] + + def run(self): + if self.options.list: + return self.show_scaffolds() + if not self.options.scaffold_name: + print('You must provide at least one scaffold name') + return + if not self.args: + print('You must provide a project name') + return + diff = set(self.options.scaffold_name).difference( + self.available_scaffoldnames) + if diff: + print('Unavailable scaffolds: %s' % list(diff)) + self.render_scaffolds() + + def render_scaffolds(self): + options = self.options + args = self.args + dist_name = args[0].lstrip(os.path.sep) + output_dir = os.path.normpath(os.path.join(os.getcwd(), dist_name)) + pkg_name = _bad_chars_re.sub('', dist_name.lower()) + safe_name = pkg_resources.safe_name(dist_name) + egg_name = pkg_resources.to_filename(safe_name), + vars = { + 'project': dist_name, + 'package': pkg_name, + 'egg': egg_name, + } + for scaffold_name in options.scaffold_name: + for scaffold in self.scaffolds: + if scaffold.name == scaffold_name: + scaffold.run(self, output_dir, vars) + + def show_scaffolds(self): + scaffolds = list(self.scaffolds) + max_name = max([len(t.name) for t in scaffolds]) + scaffolds.sort(key=lambda x: x.name) + print('Available scaffolds:') + for scaffold in scaffolds: + print(' %s:%s %s' % ( + scaffold.name, + ' '*(max_name-len(scaffold.name)), scaffold.summary)) + + +def all_scaffolds(): + scaffolds = [] + eps = list(pkg_resources.iter_entry_points('pyramid.scaffold')) + for entry in eps: + try: + scaffold_class = entry.load() + scaffold = scaffold_class(entry.name) + scaffolds.append(scaffold) + except Exception as e: + print('Warning: could not load entry point %s (%s: %s)' % ( + entry.name, e.__class__.__name__, e)) + return scaffolds + diff --git a/setup.py b/setup.py index af0094856..d029c1a61 100644 --- a/setup.py +++ b/setup.py @@ -84,11 +84,11 @@ setup(name='pyramid', tests_require = tests_require, test_suite="pyramid.tests", entry_points = """\ - [glue.create_template] - pyramid_starter=pyramid.scaffolds:StarterProjectTemplate - pyramid_zodb=pyramid.scaffolds:ZODBProjectTemplate - pyramid_routesalchemy=pyramid.scaffolds:RoutesAlchemyProjectTemplate - pyramid_alchemy=pyramid.scaffolds:AlchemyProjectTemplate + [pyramid.scaffold] + starter=pyramid.scaffolds:StarterProjectTemplate + zodb=pyramid.scaffolds:ZODBProjectTemplate + routesalchemy=pyramid.scaffolds:RoutesAlchemyProjectTemplate + alchemy=pyramid.scaffolds:AlchemyProjectTemplate [glue.command] pshell=pyramid.paster:PShellCommand proutes=pyramid.paster:PRoutesCommand @@ -96,6 +96,7 @@ setup(name='pyramid', ptweens=pyramid.paster:PTweensCommand [console_scripts] bfg2pyramid = pyramid.fixers.fix_bfg_imports:main + pcreate = pyramid.scripts.pcreate:main """ ) -- cgit v1.2.3 From 0e0cb766f9b8c49ce53123fe9d13d0184196e9b1 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 08:11:52 -0400 Subject: add pserve command and move template code out of glue; add a wsgiref paste.server_runner entry point --- pyramid/compat.py | 10 + pyramid/scaffolds/__init__.py | 2 +- pyramid/scaffolds/copydir.py | 294 +++++++++++++++++ pyramid/scaffolds/template.py | 123 +++++++ pyramid/scaffolds/tests.py | 9 +- pyramid/scripts/pserve.py | 724 ++++++++++++++++++++++++++++++++++++++++++ setup.py | 3 + 7 files changed, 1159 insertions(+), 6 deletions(-) create mode 100644 pyramid/scaffolds/copydir.py create mode 100644 pyramid/scaffolds/template.py create mode 100644 pyramid/scripts/pserve.py diff --git a/pyramid/compat.py b/pyramid/compat.py index e686be27d..552a90b4b 100644 --- a/pyramid/compat.py +++ b/pyramid/compat.py @@ -224,3 +224,13 @@ if PY3: # pragma: no cover from html import escape else: from cgi import escape + +try: + input_ = raw_input +except NameError: + input_ = input + +try: # pragma: no cover + import configparser +except ImportError: + import ConfigParser as configparser diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py index 8661038bf..93a5db12a 100644 --- a/pyramid/scaffolds/__init__.py +++ b/pyramid/scaffolds/__init__.py @@ -6,7 +6,7 @@ from pyramid.compat import ( native_ ) -from glue.template import Template +from pyramid.scaffolds.template import Template class PyramidTemplate(Template): def pre(self, command, output_dir, vars): diff --git a/pyramid/scaffolds/copydir.py b/pyramid/scaffolds/copydir.py new file mode 100644 index 000000000..5728fce5f --- /dev/null +++ b/pyramid/scaffolds/copydir.py @@ -0,0 +1,294 @@ +# (c) 2005 Ian Bicking and contributors; written for Paste +# (http://pythonpaste.org) Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license.php + +import os +import sys +import pkg_resources +import cgi +import urllib + +from pyramid.compat import ( + input_, + native_ + ) + +fsenc = sys.getfilesystemencoding() + +class SkipTemplate(Exception): + """ + Raised to indicate that the template should not be copied over. + Raise this exception during the substitution of your template + """ + +def copy_dir(source, dest, vars, verbosity, simulate, indent=0, + sub_vars=True, interactive=False, overwrite=True, + template_renderer=None): + """ + Copies the ``source`` directory to the ``dest`` directory. + + ``vars``: A dictionary of variables to use in any substitutions. + + ``verbosity``: Higher numbers will show more about what is happening. + + ``simulate``: If true, then don't actually *do* anything. + + ``indent``: Indent any messages by this amount. + + ``sub_vars``: If true, variables in ``_tmpl`` files and ``+var+`` + in filenames will be substituted. + + ``overwrite``: If false, then don't every overwrite anything. + + ``interactive``: If you are overwriting a file and interactive is + true, then ask before overwriting. + + ``template_renderer``: This is a function for rendering templates (if you + don't want to use string.Template). It should have the signature + ``template_renderer(content_as_string, vars_as_dict, + filename=filename)``. + """ + # This allows you to use a leading +dot+ in filenames which would + # otherwise be skipped because leading dots make the file hidden: + vars.setdefault('dot', '.') + vars.setdefault('plus', '+') + use_pkg_resources = isinstance(source, tuple) + if use_pkg_resources: + names = sorted(pkg_resources.resource_listdir(source[0], source[1])) + else: + names = sorted(os.listdir(source)) + pad = ' '*(indent*2) + if not os.path.exists(dest): + if verbosity >= 1: + print('%sCreating %s/' % (pad, dest)) + if not simulate: + makedirs(dest, verbosity=verbosity, pad=pad) + elif verbosity >= 2: + print('%sDirectory %s exists' % (pad, dest)) + for name in names: + if use_pkg_resources: + full = '/'.join([source[1], name]) + else: + full = os.path.join(source, name) + reason = should_skip_file(name) + if reason: + if verbosity >= 2: + reason = pad + reason % {'filename': full} + print(reason) + continue + if sub_vars: + dest_full = os.path.join(dest, substitute_filename(name, vars)) + sub_file = False + if dest_full.endswith('_tmpl'): + dest_full = dest_full[:-5] + sub_file = sub_vars + if use_pkg_resources and pkg_resources.resource_isdir(source[0], full): + if verbosity: + print('%sRecursing into %s' % (pad, os.path.basename(full))) + copy_dir((source[0], full), dest_full, vars, verbosity, simulate, + indent=indent+1, + sub_vars=sub_vars, interactive=interactive, + template_renderer=template_renderer) + continue + elif not use_pkg_resources and os.path.isdir(full): + if verbosity: + print('%sRecursing into %s' % (pad, os.path.basename(full))) + copy_dir(full, dest_full, vars, verbosity, simulate, + indent=indent+1, + sub_vars=sub_vars, interactive=interactive, + template_renderer=template_renderer) + continue + elif use_pkg_resources: + content = pkg_resources.resource_string(source[0], full) + else: + f = open(full, 'rb') + content = f.read() + f.close() + if sub_file: + try: + content = substitute_content( + content, vars, filename=full, + template_renderer=template_renderer + ) + except SkipTemplate: + continue + if content is None: + continue + already_exists = os.path.exists(dest_full) + if already_exists: + f = open(dest_full, 'rb') + old_content = f.read() + f.close() + if old_content == content: + if verbosity: + print('%s%s already exists (same content)' % + (pad, dest_full)) + continue + if interactive: + if not query_interactive( + native_(full, fsenc), native_(dest_full, fsenc), + native_(content, fsenc), native_(old_content, fsenc), + simulate=simulate): + continue + elif not overwrite: + continue + if verbosity and use_pkg_resources: + print('%sCopying %s to %s' % (pad, full, dest_full)) + elif verbosity: + print( + '%sCopying %s to %s' % (pad, os.path.basename(full), dest_full)) + if not simulate: + f = open(dest_full, 'wb') + f.write(content) + f.close() + +def should_skip_file(name): + """ + Checks if a file should be skipped based on its name. + + If it should be skipped, returns the reason, otherwise returns + None. + """ + if name.startswith('.'): + return 'Skipping hidden file %(filename)s' + if name.endswith('~') or name.endswith('.bak'): + return 'Skipping backup file %(filename)s' + if name.endswith('.pyc') or name.endswith('.pyo'): + return 'Skipping %s file %(filename)s' % os.path.splitext(name)[1] + if name.endswith('$py.class'): + return 'Skipping $py.class file %(filename)s' + if name in ('CVS', '_darcs'): + return 'Skipping version control directory %(filename)s' + return None + +# Overridden on user's request: +all_answer = None + +def query_interactive(src_fn, dest_fn, src_content, dest_content, + simulate): + global all_answer + from difflib import unified_diff, context_diff + u_diff = list(unified_diff( + dest_content.splitlines(), + src_content.splitlines(), + dest_fn, src_fn)) + c_diff = list(context_diff( + dest_content.splitlines(), + src_content.splitlines(), + dest_fn, src_fn)) + added = len([l for l in u_diff if l.startswith('+') + and not l.startswith('+++')]) + removed = len([l for l in u_diff if l.startswith('-') + and not l.startswith('---')]) + if added > removed: + msg = '; %i lines added' % (added-removed) + elif removed > added: + msg = '; %i lines removed' % (removed-added) + else: + msg = '' + print('Replace %i bytes with %i bytes (%i/%i lines changed%s)' % ( + len(dest_content), len(src_content), + removed, len(dest_content.splitlines()), msg)) + prompt = 'Overwrite %s [y/n/d/B/?] ' % dest_fn + while 1: + if all_answer is None: + response = input_(prompt).strip().lower() + else: + response = all_answer + if not response or response[0] == 'b': + import shutil + new_dest_fn = dest_fn + '.bak' + n = 0 + while os.path.exists(new_dest_fn): + n += 1 + new_dest_fn = dest_fn + '.bak' + str(n) + print('Backing up %s to %s' % (dest_fn, new_dest_fn)) + if not simulate: + shutil.copyfile(dest_fn, new_dest_fn) + return True + elif response.startswith('all '): + rest = response[4:].strip() + if not rest or rest[0] not in ('y', 'n', 'b'): + print(query_usage) + continue + response = all_answer = rest[0] + if response[0] == 'y': + return True + elif response[0] == 'n': + return False + elif response == 'dc': + print('\n'.join(c_diff)) + elif response[0] == 'd': + print('\n'.join(u_diff)) + else: + print(query_usage) + +query_usage = """\ +Responses: + Y(es): Overwrite the file with the new content. + N(o): Do not overwrite the file. + D(iff): Show a unified diff of the proposed changes (dc=context diff) + B(ackup): Save the current file contents to a .bak file + (and overwrite) + Type "all Y/N/B" to use Y/N/B for answer to all future questions +""" + +def makedirs(dir, verbosity, pad): + parent = os.path.dirname(os.path.abspath(dir)) + if not os.path.exists(parent): + makedirs(parent, verbosity, pad) + os.mkdir(dir) + +def substitute_filename(fn, vars): + for var, value in vars.items(): + fn = fn.replace('+%s+' % var, str(value)) + return fn + +def substitute_content(content, vars, filename='', + template_renderer=None): + v = standard_vars.copy() + v.update(vars) + return template_renderer(content, v, filename=filename) + +def html_quote(s): + if s is None: + return '' + return cgi.escape(str(s), 1) + +def url_quote(s): + if s is None: + return '' + return urllib.quote(str(s)) + +def test(conf, true_cond, false_cond=None): + if conf: + return true_cond + else: + return false_cond + +def skip_template(condition=True, *args): + """ + Raise SkipTemplate, which causes copydir to skip the template + being processed. If you pass in a condition, only raise if that + condition is true (allows you to use this with string.Template) + + If you pass any additional arguments, they will be used to + instantiate SkipTemplate (generally use like + ``skip_template(license=='GPL', 'Skipping file; not using GPL')``) + """ + if condition: + raise SkipTemplate(*args) + +standard_vars = { + 'nothing': None, + 'html_quote': html_quote, + 'url_quote': url_quote, + 'empty': '""', + 'test': test, + 'repr': repr, + 'str': str, + 'bool': bool, + 'SkipTemplate': SkipTemplate, + 'skip_template': skip_template, + } + diff --git a/pyramid/scaffolds/template.py b/pyramid/scaffolds/template.py new file mode 100644 index 000000000..f9af8010a --- /dev/null +++ b/pyramid/scaffolds/template.py @@ -0,0 +1,123 @@ +# (c) 2005 Ian Bicking and contributors; written for Paste +# (http://pythonpaste.org) Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license.php + +import re +import sys +import os + +from pyramid.compat import ( + native_, + bytes_, + ) + +from pyramid.scaffolds import copydir + +fsenc = sys.getfilesystemencoding() + +class Template(object): + + def __init__(self, name): + self.name = name + + def template_renderer(self, content, vars, filename=None): + content = native_(content, fsenc) + try: + return bytes_( + substitute_double_braces(content, TypeMapper(vars)), fsenc) + except Exception as e: + _add_except(e, ' in file %s' % filename) + raise + + def module_dir(self): + """Returns the module directory of this template.""" + mod = sys.modules[self.__class__.__module__] + return os.path.dirname(mod.__file__) + + def template_dir(self): + assert self._template_dir is not None, ( + "Template %r didn't set _template_dir" % self) + if isinstance( self._template_dir, tuple): + return self._template_dir + else: + return os.path.join(self.module_dir(), self._template_dir) + + def run(self, command, output_dir, vars): + self.pre(command, output_dir, vars) + self.write_files(command, output_dir, vars) + self.post(command, output_dir, vars) + + def pre(self, command, output_dir, vars): + """ + Called before template is applied. + """ + pass + + def post(self, command, output_dir, vars): + """ + Called after template is applied. + """ + pass + + def write_files(self, command, output_dir, vars): + template_dir = self.template_dir() + if not os.path.exists(output_dir): + print("Creating directory %s" % output_dir) + if not command.simulate: + # Don't let copydir create this top-level directory, + # since copydir will svn add it sometimes: + os.makedirs(output_dir) + copydir.copy_dir(template_dir, output_dir, + vars, + verbosity=command.verbose, + simulate=command.options.simulate, + interactive=command.interactive, + overwrite=command.options.overwrite, + indent=1, + template_renderer=self.template_renderer) + + +class TypeMapper(dict): + + def __getitem__(self, item): + options = item.split('|') + for op in options[:-1]: + try: + value = eval_with_catch(op, dict(self.items())) + break + except (NameError, KeyError): + pass + else: + value = eval(options[-1], dict(self.items())) + if value is None: + return '' + else: + return str(value) + +def eval_with_catch(expr, vars): + try: + return eval(expr, vars) + except Exception as e: + _add_except(e, 'in expression %r' % expr) + raise + +double_brace_pattern = re.compile(r'{{(?P.*?)}}') + +def substitute_double_braces(content, values): + def double_bracerepl(match): + value = match.group('braced').strip() + return values[value] + return double_brace_pattern.sub(double_bracerepl, content) + +def _add_except(exc, info): + if not hasattr(exc, 'args') or exc.args is None: + return + args = list(exc.args) + if args: + args[0] += ' ' + info + else: + args = [info] + exc.args = tuple(args) + return + + diff --git a/pyramid/scaffolds/tests.py b/pyramid/scaffolds/tests.py index 7eb838e2f..9b8b975a3 100644 --- a/pyramid/scaffolds/tests.py +++ b/pyramid/scaffolds/tests.py @@ -34,8 +34,7 @@ class TemplateTest(object): [os.path.join(self.directory, 'bin', 'python'), 'setup.py', 'develop']) os.chdir(self.directory) - subprocess.check_call(['bin/paster', 'create', '-t', tmpl_name, - 'Dingle']) + subprocess.check_call(['bin/pcreate', '-s', tmpl_name, 'Dingle']) os.chdir('Dingle') py = os.path.join(self.directory, 'bin', 'python') subprocess.check_call([py, 'setup.py', 'install']) @@ -79,10 +78,10 @@ if __name__ == '__main__': # pragma: no cover raise ValueError(returncode) subprocess.check_call = check_call - templates = ['pyramid_starter', 'pyramid_alchemy', 'pyramid_routesalchemy',] + templates = ['starter', 'alchemy', 'routesalchemy',] - if sys.version_info >= (2, 5): - templates.append('pyramid_zodb') + if sys.version_info >= (2, 5) and sys.version_info < (3, 0): + templates.append('zodb') for name in templates: test = TemplateTest() diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py new file mode 100644 index 000000000..f3414d2cf --- /dev/null +++ b/pyramid/scripts/pserve.py @@ -0,0 +1,724 @@ +# (c) 2005 Ian Bicking and contributors; written for Paste +# (http://pythonpaste.org) Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license.php @@: This should be moved +# to paste.deploy For discussion of daemonizing: +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 Code taken +# also from QP: http://www.mems-exchange.org/software/qp/ From lib/site.py + +import re +import optparse +import os +import errno +import sys +import time +import subprocess +import threading +import atexit +import logging +from logging.config import fileConfig + +from pyramid.compat import configparser + +from paste.deploy import loadapp, loadserver + +MAXFD = 1024 + +jython = sys.platform.startswith('java') + +def main(argv=sys.argv): + command = ServeCommand(argv) + return command.run() + +class DaemonizeException(Exception): + pass + +class ServeCommand(object): + + min_args = 0 + usage = 'CONFIG_FILE [start|stop|restart|status] [var=value]' + takes_config_file = 1 + summary = "Serve the described application" + description = """\ + This command serves a web application that uses a paste.deploy + configuration file for the server and application. + + If start/stop/restart is given, then --daemon is implied, and it will + start (normal operation), stop (--stop-daemon), or do both. + + You can also include variable assignments like 'http_port=8080' + and then use %(http_port)s in your config files. + """ + verbose = 1 + + # used by subclasses that configure apps and servers differently + requires_config_file = True + + parser = optparse.OptionParser() + parser.add_option( + '-n', '--app-name', + dest='app_name', + metavar='NAME', + help="Load the named application (default main)") + parser.add_option( + '-s', '--server', + dest='server', + metavar='SERVER_TYPE', + help="Use the named server.") + parser.add_option( + '--server-name', + dest='server_name', + metavar='SECTION_NAME', + help="Use the named server as defined in the configuration file (default: main)") + if hasattr(os, 'fork'): + parser.add_option( + '--daemon', + dest="daemon", + action="store_true", + help="Run in daemon (background) mode") + parser.add_option( + '--pid-file', + dest='pid_file', + metavar='FILENAME', + help="Save PID to file (default to pyramid.pid if running in daemon mode)") + parser.add_option( + '--log-file', + dest='log_file', + metavar='LOG_FILE', + help="Save output to the given log file (redirects stdout)") + parser.add_option( + '--reload', + dest='reload', + action='store_true', + help="Use auto-restart file monitor") + parser.add_option( + '--reload-interval', + dest='reload_interval', + default=1, + help="Seconds between checking files (low number can cause significant CPU usage)") + parser.add_option( + '--monitor-restart', + dest='monitor_restart', + action='store_true', + help="Auto-restart server if it dies") + parser.add_option( + '--status', + action='store_true', + dest='show_status', + help="Show the status of the (presumably daemonized) server") + + if hasattr(os, 'setuid'): + # I don't think these are available on Windows + parser.add_option( + '--user', + dest='set_user', + metavar="USERNAME", + help="Set the user (usually only possible when run as root)") + parser.add_option( + '--group', + dest='set_group', + metavar="GROUP", + help="Set the group (usually only possible when run as root)") + + parser.add_option( + '--stop-daemon', + dest='stop_daemon', + action='store_true', + help='Stop a daemonized server (given a PID file, or default pyramid.pid file)') + + if jython: + parser.add_option( + '--disable-jython-reloader', + action='store_true', + dest='disable_jython_reloader', + help="Disable the Jython reloader") + + _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) + + default_verbosity = 1 + + _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN' + _monitor_environ_key = 'PASTE_MONITOR_SHOULD_RUN' + + possible_subcommands = ('start', 'stop', 'restart', 'status') + + def __init__(self, argv): + self.options, self.args = self.parser.parse_args(argv[1:]) + + def run(self): + if self.options.stop_daemon: + return self.stop_daemon() + + if not hasattr(self.options, 'set_user'): + # Windows case: + self.options.set_user = self.options.set_group = None + # @@: Is this the right stage to set the user at? + self.change_user_group( + self.options.set_user, self.options.set_group) + + if self.requires_config_file: + if not self.args: + raise ValueError('You must give a config file') + 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:] + else: + app_spec = "" + if (self.args + and self.args[0] in self.possible_subcommands): + cmd = self.args[0] + restvars = self.args[1:] + else: + cmd = None + restvars = self.args[:] + + jython_monitor = False + if self.options.reload: + if jython and not self.options.disable_jython_reloader: + # JythonMonitor raises the special SystemRestart + # exception that'll cause the Jython interpreter to + # reload in the existing Java process (avoiding + # subprocess startup time) + try: + from paste.reloader import JythonMonitor + except ImportError: + pass + else: + jython_monitor = JythonMonitor(poll_interval=int( + self.options.reload_interval)) + if self.requires_config_file: + jython_monitor.watch_file(self.args[0]) + + if not jython_monitor: + if os.environ.get(self._reloader_environ_key): + from paste import reloader + if self.verbose > 1: + print('Running reloading file monitor') + reloader.install(int(self.options.reload_interval)) + if self.requires_config_file: + reloader.watch_file(self.args[0]) + else: + return self.restart_with_reloader() + + if cmd not in (None, 'start', 'stop', 'restart', 'status'): + raise ValueError( + 'Error: must give start|stop|restart (not %s)' % cmd) + + if cmd == 'status' or self.options.show_status: + return self.show_status() + + if cmd == 'restart' or cmd == 'stop': + result = self.stop_daemon() + if result: + if cmd == 'restart': + print("Could not stop daemon; aborting") + else: + print("Could not stop daemon") + return result + if cmd == 'stop': + return result + self.options.daemon = True + + if cmd == 'start': + self.options.daemon = True + + app_name = self.options.app_name + vars = self.parse_vars(restvars) + if not self._scheme_re.search(app_spec): + app_spec = 'config:' + app_spec + server_name = self.options.server_name + if self.options.server: + server_spec = 'egg:pyramid' + assert server_name is None + server_name = self.options.server + else: + server_spec = app_spec + base = os.getcwd() + + if getattr(self.options, 'daemon', False): + if not self.options.pid_file: + self.options.pid_file = 'pyramid.pid' + if not self.options.log_file: + self.options.log_file = 'pyramid.log' + + # Ensure the log file is writeable + if self.options.log_file: + try: + writeable_log_file = open(self.options.log_file, 'a') + except IOError as ioe: + msg = 'Error: Unable to write to log file: %s' % ioe + raise ValueError(msg) + writeable_log_file.close() + + # Ensure the pid file is writeable + if self.options.pid_file: + try: + writeable_pid_file = open(self.options.pid_file, 'a') + except IOError as ioe: + msg = 'Error: Unable to write to pid file: %s' % ioe + raise ValueError(msg) + writeable_pid_file.close() + + if getattr(self.options, 'daemon', False): + try: + self.daemonize() + except DaemonizeException as ex: + if self.verbose > 0: + print(str(ex)) + return + + if (self.options.monitor_restart + and not os.environ.get(self._monitor_environ_key)): + return self.restart_with_monitor() + + if self.options.pid_file: + self.record_pid(self.options.pid_file) + + if self.options.log_file: + stdout_log = LazyWriter(self.options.log_file, 'a') + sys.stdout = stdout_log + sys.stderr = stdout_log + logging.basicConfig(stream=stdout_log) + + log_fn = app_spec + if log_fn.startswith('config:'): + log_fn = app_spec[len('config:'):] + elif log_fn.startswith('egg:'): + log_fn = None + if log_fn: + log_fn = os.path.join(base, log_fn) + self.logging_file_config(log_fn) + + 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) + + if self.verbose > 0: + if hasattr(os, 'getpid'): + msg = 'Starting server in PID %i.' % os.getpid() + else: + msg = 'Starting server.' + print(msg) + + def serve(): + try: + server(app) + except (SystemExit, KeyboardInterrupt) as e: + if self.verbose > 1: + raise + if str(e): + msg = ' '+str(e) + else: + msg = '' + print('Exiting%s (-v to see traceback)' % msg) + + if jython_monitor: + # JythonMonitor has to be ran from the main thread + threading.Thread(target=serve).start() + print('Starting Jython file monitor') + jython_monitor.periodic_reload() + else: + serve() + + def loadserver(self, server_spec, name, relative_to, **kw): + return loadserver( + server_spec, name=name, + relative_to=relative_to, **kw) + + def loadapp(self, app_spec, name, relative_to, **kw): + 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 logging_file_config(self, config_file): + """ + Setup logging via the logging module's fileConfig function with the + specified ``config_file``, if applicable. + + ConfigParser defaults are specified for the special ``__file__`` + and ``here`` variables, similar to PasteDeploy config loading. + """ + parser = configparser.ConfigParser() + parser.read([config_file]) + if parser.has_section('loggers'): + config_file = os.path.abspath(config_file) + fileConfig(config_file, dict(__file__=config_file, + here=os.path.dirname(config_file))) + + def quote_first_command_arg(self, arg): + """ + There's a bug in Windows when running an executable that's + located inside a path with a space in it. This method handles + that case, or on non-Windows systems or an executable with no + spaces, it just leaves well enough alone. + """ + if (sys.platform != 'win32' or ' ' not in arg): + # Problem does not apply: + return arg + try: + import win32api + except ImportError: + raise ValueError( + "The executable %r contains a space, and in order to " + "handle this issue you must have the win32api module " + "installed" % arg) + arg = win32api.GetShortPathName(arg) + return arg + + def daemonize(self): + pid = live_pidfile(self.options.pid_file) + if pid: + raise DaemonizeException( + "Daemon is already running (PID: %s from PID file %s)" + % (pid, self.options.pid_file)) + + if self.verbose > 0: + print('Entering daemon mode') + pid = os.fork() + if pid: + # The forked process also has a handle on resources, so we + # *don't* want proper termination of the process, we just + # want to exit quick (which os._exit() does) + os._exit(0) + # Make this the session leader + os.setsid() + # Fork again for good measure! + pid = os.fork() + if pid: + os._exit(0) + + # @@: Should we set the umask and cwd now? + + import resource # Resource usage information. + maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] + if (maxfd == resource.RLIM_INFINITY): + maxfd = MAXFD + # Iterate through and close all file descriptors. + for fd in range(0, maxfd): + try: + os.close(fd) + except OSError: # ERROR, fd wasn't open to begin with (ignored) + pass + + if (hasattr(os, "devnull")): + REDIRECT_TO = os.devnull + else: + REDIRECT_TO = "/dev/null" + os.open(REDIRECT_TO, os.O_RDWR) # standard input (0) + # Duplicate standard input to standard output and standard error. + os.dup2(0, 1) # standard output (1) + os.dup2(0, 2) # standard error (2) + + def record_pid(self, pid_file): + pid = os.getpid() + if self.verbose > 1: + print('Writing PID %s to %s' % (pid, pid_file)) + f = open(pid_file, 'w') + f.write(str(pid)) + f.close() + atexit.register(_remove_pid_file, pid, pid_file, self.verbose) + + def stop_daemon(self): + pid_file = self.options.pid_file or 'pyramid.pid' + if not os.path.exists(pid_file): + print('No PID file exists in %s' % pid_file) + return 1 + pid = read_pidfile(pid_file) + if not pid: + print("Not a valid PID file in %s" % pid_file) + return 1 + pid = live_pidfile(pid_file) + if not pid: + print("PID in %s is not valid (deleting)" % pid_file) + try: + os.unlink(pid_file) + except (OSError, IOError) as e: + print("Could not delete: %s" % e) + return 2 + return 1 + for j in range(10): + if not live_pidfile(pid_file): + break + import signal + os.kill(pid, signal.SIGTERM) + time.sleep(1) + else: + print("failed to kill web process %s" % pid) + return 3 + if os.path.exists(pid_file): + os.unlink(pid_file) + return 0 + + def show_status(self): + pid_file = self.options.pid_file or 'pyramid.pid' + if not os.path.exists(pid_file): + print('No PID file %s' % pid_file) + return 1 + pid = read_pidfile(pid_file) + if not pid: + print('No PID in file %s' % pid_file) + return 1 + pid = live_pidfile(pid_file) + if not pid: + print('PID %s in %s is not running' % (pid, pid_file)) + return 1 + print('Server running in PID %s' % pid) + return 0 + + def restart_with_reloader(self): + self.restart_with_monitor(reloader=True) + + def restart_with_monitor(self, reloader=False): + if self.verbose > 0: + if reloader: + print('Starting subprocess with file monitor') + else: + print('Starting subprocess with monitor parent') + while 1: + args = [self.quote_first_command_arg(sys.executable)] + sys.argv + new_environ = os.environ.copy() + if reloader: + new_environ[self._reloader_environ_key] = 'true' + else: + new_environ[self._monitor_environ_key] = 'true' + proc = None + try: + try: + _turn_sigterm_into_systemexit() + proc = subprocess.Popen(args, env=new_environ) + exit_code = proc.wait() + proc = None + except KeyboardInterrupt: + print('^C caught in monitor process') + if self.verbose > 1: + raise + return 1 + finally: + if (proc is not None + and hasattr(os, 'kill')): + import signal + try: + os.kill(proc.pid, signal.SIGTERM) + except (OSError, IOError): + pass + + if reloader: + # Reloader always exits with code 3; but if we are + # a monitor, any exit code will restart + if exit_code != 3: + return exit_code + if self.verbose > 0: + print('-'*20, 'Restarting', '-'*20) + + def change_user_group(self, user, group): + if not user and not group: + return + import pwd, grp + uid = gid = None + if group: + try: + gid = int(group) + group = grp.getgrgid(gid).gr_name + except ValueError: + import grp + try: + entry = grp.getgrnam(group) + except KeyError: + raise ValueError( + "Bad group: %r; no such group exists" % group) + gid = entry.gr_gid + try: + uid = int(user) + user = pwd.getpwuid(uid).pw_name + except ValueError: + try: + entry = pwd.getpwnam(user) + except KeyError: + raise ValueError( + "Bad username: %r; no such user exists" % user) + if not gid: + gid = entry.pw_gid + uid = entry.pw_uid + if self.verbose > 0: + print('Changing user to %s:%s (%s:%s)' % ( + user, group or '(unknown)', uid, gid)) + if gid: + os.setgid(gid) + if uid: + os.setuid(uid) + +class LazyWriter(object): + + """ + File-like object that opens a file lazily when it is first written + to. + """ + + def __init__(self, filename, mode='w'): + self.filename = filename + self.fileobj = None + self.lock = threading.Lock() + self.mode = mode + + def open(self): + if self.fileobj is None: + self.lock.acquire() + try: + if self.fileobj is None: + self.fileobj = open(self.filename, self.mode) + finally: + self.lock.release() + return self.fileobj + + def write(self, text): + fileobj = self.open() + fileobj.write(text) + fileobj.flush() + + def writelines(self, text): + fileobj = self.open() + fileobj.writelines(text) + fileobj.flush() + + def flush(self): + self.open().flush() + +def live_pidfile(pidfile): + """(pidfile:str) -> int | None + Returns an int found in the named file, if there is one, + and if there is a running process with that process id. + Return None if no such process exists. + """ + pid = read_pidfile(pidfile) + if pid: + try: + os.kill(int(pid), 0) + return pid + except OSError as e: + if e.errno == errno.EPERM: + return pid + return None + +def read_pidfile(filename): + if os.path.exists(filename): + try: + f = open(filename) + content = f.read() + f.close() + return int(content.strip()) + except (ValueError, IOError): + return None + else: + return None + +def _remove_pid_file(written_pid, filename, verbosity): + current_pid = os.getpid() + if written_pid != current_pid: + # A forked process must be exiting, not the process that + # wrote the PID file + return + if not os.path.exists(filename): + return + f = open(filename) + content = f.read().strip() + f.close() + try: + pid_in_file = int(content) + except ValueError: + pass + else: + if pid_in_file != current_pid: + print("PID file %s contains %s, not expected PID %s" % ( + filename, pid_in_file, current_pid)) + return + if verbosity > 0: + print("Removing PID file %s" % filename) + try: + os.unlink(filename) + return + except OSError as e: + # Record, but don't give traceback + print("Cannot remove PID file: %s" % e) + # well, at least lets not leave the invalid PID around... + try: + f = open(filename, 'w') + f.write('') + f.close() + except OSError as e: + print('Stale PID left in file: %s (%e)' % (filename, e)) + else: + print('Stale PID removed') + + +def ensure_port_cleanup(bound_addresses, maxtries=30, sleeptime=2): + """ + This makes sure any open ports are closed. + + Does this by connecting to them until they give connection + refused. Servers should call like:: + + import paste.script + ensure_port_cleanup([80, 443]) + """ + atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries, + sleeptime=sleeptime) + +def _cleanup_ports(bound_addresses, maxtries=30, sleeptime=2): + # Wait for the server to bind to the port. + import socket + import errno + for bound_address in bound_addresses: + for attempt in range(maxtries): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect(bound_address) + except socket.error as e: + if e.args[0] != errno.ECONNREFUSED: + raise + break + else: + time.sleep(sleeptime) + else: + raise SystemExit('Timeout waiting for port.') + sock.close() + +def _turn_sigterm_into_systemexit(): + """ + Attempts to turn a SIGTERM exception into a SystemExit exception. + """ + try: + import signal + except ImportError: + return + def handle_term(signo, frame): + raise SystemExit + signal.signal(signal.SIGTERM, handle_term) + +# For paste.deploy server instantiation (egg:pyramid#wsgiref) +def wsgiref_server_runner(wsgi_app, global_conf, **kw): + from wsgiref.simple_server import make_server + host = kw.get('host', '0.0.0.0') + port = int(kw.get('port', 8080)) + server = make_server(host, port, wsgi_app) + print('Starting HTTP server on http://%s:%s' % (host, port)) + server.serve_forever() diff --git a/setup.py b/setup.py index d029c1a61..1602f9aa7 100644 --- a/setup.py +++ b/setup.py @@ -97,6 +97,9 @@ setup(name='pyramid', [console_scripts] bfg2pyramid = pyramid.fixers.fix_bfg_imports:main pcreate = pyramid.scripts.pcreate:main + pserve = pyramid.scripts.pserve:main + [paste.server_runner] + wsgiref = pyramid.scripts.pserve:wsgiref_server_runner """ ) -- cgit v1.2.3 From 33796033414d4460381531e713622a21401972a6 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 09:05:00 -0400 Subject: turn all paster commands into scripts --- pyramid/paster.py | 562 ----------- pyramid/scaffolds/alchemy/development.ini_tmpl | 2 +- pyramid/scaffolds/alchemy/production.ini_tmpl | 2 +- .../scaffolds/routesalchemy/development.ini_tmpl | 2 +- .../scaffolds/routesalchemy/production.ini_tmpl | 2 +- pyramid/scaffolds/starter/development.ini_tmpl | 2 +- pyramid/scaffolds/starter/production.ini_tmpl | 2 +- pyramid/scaffolds/zodb/development.ini_tmpl | 2 +- pyramid/scaffolds/zodb/production.ini_tmpl | 2 +- pyramid/scripts/proutes.py | 75 ++ pyramid/scripts/pshell.py | 200 ++++ pyramid/scripts/ptweens.py | 87 ++ pyramid/scripts/pviews.py | 256 +++++ pyramid/tests/test_paster.py | 1011 ------------------- pyramid/tests/test_scripts.py | 1037 ++++++++++++++++++++ setup.py | 11 +- 16 files changed, 1668 insertions(+), 1587 deletions(-) create mode 100644 pyramid/scripts/proutes.py create mode 100644 pyramid/scripts/pshell.py create mode 100644 pyramid/scripts/ptweens.py create mode 100644 pyramid/scripts/pviews.py create mode 100644 pyramid/tests/test_scripts.py diff --git a/pyramid/paster.py b/pyramid/paster.py index 1c06ca79b..3fec6c556 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -1,20 +1,9 @@ import os -import sys -from code import interact import zope.deprecation from paste.deploy import loadapp -from glue.command import Command -from pyramid.interfaces import IMultiView -from pyramid.interfaces import ITweens - -from pyramid.compat import print_ -from pyramid.compat import configparser from pyramid.scripting import prepare -from pyramid.util import DottedNameResolver -from pyramid.tweens import MAIN -from pyramid.tweens import INGRESS from pyramid.scaffolds import PyramidTemplate # bw compat PyramidTemplate = PyramidTemplate # pyflakes @@ -90,554 +79,3 @@ def bootstrap(config_uri, request=None): env['app'] = app return env -class PCommand(Command): - bootstrap = (bootstrap,) # testing - verbose = 3 - - def __init__(self, *arg, **kw): - # needs to be in constructor to support Jython (used to be at class - # scope as ``usage = '\n' + __doc__``. - self.usage = '\n' + self.__doc__ - Command.__init__(self, *arg, **kw) - -class PShellCommand(PCommand): - """Open an interactive shell with a :app:`Pyramid` app loaded. - - This command accepts one positional argument: - - ``config_uri`` -- specifies the PasteDeploy config file to use for the - interactive shell. The format is ``inifile#name``. If the name is left - off, ``main`` will be assumed. - - Example:: - - $ glue pshell myapp.ini#main - - .. note:: If you do not point the loader directly at the section of the - ini file containing your :app:`Pyramid` application, the - command will attempt to find the app for you. If you are - loading a pipeline that contains more than one :app:`Pyramid` - application within it, the loader will use the last one. - - """ - summary = "Open an interactive shell with a Pyramid application loaded" - - min_args = 1 - max_args = 1 - - parser = Command.standard_parser(simulate=True) - parser.add_option('-d', '--disable-ipython', - action='store_true', - dest='disable_ipython', - help="Don't use IPython even if it is available") - parser.add_option('--setup', - dest='setup', - help=("A callable that will be passed the environment " - "before it is made available to the shell. This " - "option will override the 'setup' key in the " - "[pshell] ini section.")) - - ConfigParser = configparser.ConfigParser # testing - - loaded_objects = {} - object_help = {} - setup = None - - def pshell_file_config(self, filename): - config = self.ConfigParser() - config.read(filename) - try: - items = config.items('pshell') - except configparser.NoSectionError: - return - - resolver = DottedNameResolver(None) - self.loaded_objects = {} - self.object_help = {} - self.setup = None - for k, v in items: - if k == 'setup': - self.setup = v - else: - self.loaded_objects[k] = resolver.maybe_resolve(v) - self.object_help[k] = v - - def command(self, shell=None): - config_uri = self.args[0] - config_file = config_uri.split('#', 1)[0] - self.logging_file_config(config_file) - self.pshell_file_config(config_file) - - # bootstrap the environ - env = self.bootstrap[0](config_uri) - - # remove the closer from the env - closer = env.pop('closer') - - # setup help text for default environment - env_help = dict(env) - env_help['app'] = 'The WSGI application.' - env_help['root'] = 'Root of the default resource tree.' - env_help['registry'] = 'Active Pyramid registry.' - env_help['request'] = 'Active request object.' - env_help['root_factory'] = ( - 'Default root factory used to create `root`.') - - # override use_script with command-line options - if self.options.setup: - self.setup = self.options.setup - - if self.setup: - # store the env before muddling it with the script - orig_env = env.copy() - - # call the setup callable - resolver = DottedNameResolver(None) - setup = resolver.maybe_resolve(self.setup) - setup(env) - - # remove any objects from default help that were overidden - for k, v in env.iteritems(): - if k not in orig_env or env[k] != orig_env[k]: - env_help[k] = v - - # load the pshell section of the ini file - env.update(self.loaded_objects) - - # eliminate duplicates from env, allowing custom vars to override - for k in self.loaded_objects: - if k in env_help: - del env_help[k] - - # generate help text - help = '' - if env_help: - help += 'Environment:' - for var in sorted(env_help.keys()): - help += '\n %-12s %s' % (var, env_help[var]) - - if self.object_help: - help += '\n\nCustom Variables:' - for var in sorted(self.object_help.keys()): - help += '\n %-12s %s' % (var, self.object_help[var]) - - if shell is None and not self.options.disable_ipython: - shell = self.make_ipython_v0_11_shell() - if shell is None: - shell = self.make_ipython_v0_10_shell() - - if shell is None: - shell = self.make_default_shell() - - try: - shell(env, help) - finally: - closer() - - def make_default_shell(self, interact=interact): - def shell(env, help): - cprt = 'Type "help" for more information.' - banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt) - banner += '\n\n' + help + '\n' - interact(banner, local=env) - return shell - - def make_ipython_v0_11_shell(self, IPShellFactory=None): - if IPShellFactory is None: # pragma: no cover - try: - from IPython.frontend.terminal.embed import ( - InteractiveShellEmbed) - IPShellFactory = InteractiveShellEmbed - except ImportError: - return None - def shell(env, help): - IPShell = IPShellFactory(banner2=help + '\n', user_ns=env) - IPShell() - return shell - - def make_ipython_v0_10_shell(self, IPShellFactory=None): - if IPShellFactory is None: # pragma: no cover - try: - from IPython.Shell import IPShellEmbed - IPShellFactory = IPShellEmbed - except ImportError: - return None - def shell(env, help): - IPShell = IPShellFactory(argv=[], user_ns=env) - IPShell.set_banner(IPShell.IP.BANNER + '\n' + help + '\n') - IPShell() - return shell - -BFGShellCommand = PShellCommand # b/w compat forever - -class PRoutesCommand(PCommand): - """Print all URL dispatch routes used by a Pyramid application in the - order in which they are evaluated. Each route includes the name of the - route, the pattern of the route, and the view callable which will be - invoked when the route is matched. - - This command accepts one positional argument: - - ``config_uri`` -- specifies the PasteDeploy config file to use for the - interactive shell. The format is ``inifile#name``. If the name is left - off, ``main`` will be assumed. - - Example:: - - $ glue proutes myapp.ini#main - - """ - summary = "Print all URL dispatch routes related to a Pyramid application" - min_args = 1 - max_args = 1 - stdout = sys.stdout - - parser = Command.standard_parser(simulate=True) - - def _get_mapper(self, registry): - from pyramid.config import Configurator - config = Configurator(registry = registry) - return config.get_routes_mapper() - - def out(self, msg): # pragma: no cover - print_(msg) - - def command(self): - 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) - registry = env['registry'] - mapper = self._get_mapper(registry) - if mapper is not None: - routes = mapper.get_routes() - fmt = '%-15s %-30s %-25s' - if not routes: - return - self.out(fmt % ('Name', 'Pattern', 'View')) - self.out( - fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View'))) - for route in routes: - request_iface = registry.queryUtility(IRouteRequest, - name=route.name) - view_callable = None - if (request_iface is None) or (route.factory is not None): - self.out(fmt % (route.name, route.pattern, '')) - else: - view_callable = registry.adapters.lookup( - (IViewClassifier, request_iface, Interface), - IView, name='', default=None) - self.out(fmt % (route.name, route.pattern, view_callable)) - - -class PViewsCommand(PCommand): - """Print, for a given URL, the views that might match. Underneath each - potentially matching route, list the predicates required. Underneath - each route+predicate set, print each view that might match and its - predicates. - - This command accepts two positional arguments: - - ``config_uri`` -- specifies the PasteDeploy config file to use for the - interactive shell. The format is ``inifile#name``. If the name is left - off, ``main`` will be assumed. - - ``url`` -- specifies the URL that will be used to find matching views. - - Example:: - - $ glue proutes myapp.ini#main url - - """ - summary = "Print all views in an application that might match a URL" - min_args = 2 - max_args = 2 - stdout = sys.stdout - - parser = Command.standard_parser(simulate=True) - - def out(self, msg): # pragma: no cover - print_(msg) - - def _find_multi_routes(self, mapper, request): - infos = [] - path = request.environ['PATH_INFO'] - # find all routes that match path, regardless of predicates - for route in mapper.get_routes(): - match = route.match(path) - if match is not None: - info = {'match':match, 'route':route} - infos.append(info) - return infos - - def _find_view(self, url, registry): - """ - Accept ``url`` and ``registry``; create a :term:`request` and - find a :app:`Pyramid` view based on introspection of :term:`view - configuration` within the application registry; return the view. - """ - from zope.interface import providedBy - from zope.interface import implementer - from pyramid.interfaces import IRequest - from pyramid.interfaces import IRootFactory - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IRequestFactory - from pyramid.interfaces import IRoutesMapper - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import ITraverser - from pyramid.request import Request - from pyramid.traversal import DefaultRootFactory - from pyramid.traversal import ResourceTreeTraverser - - q = registry.queryUtility - root_factory = q(IRootFactory, default=DefaultRootFactory) - routes_mapper = q(IRoutesMapper) - request_factory = q(IRequestFactory, default=Request) - - adapters = registry.adapters - request = None - - @implementer(IMultiView) - class RoutesMultiView(object): - - def __init__(self, infos, context_iface, root_factory, request): - self.views = [] - for info in infos: - match, route = info['match'], info['route'] - if route is not None: - request_iface = registry.queryUtility( - IRouteRequest, - name=route.name, - default=IRequest) - view = adapters.lookup( - (IViewClassifier, request_iface, context_iface), - IView, name='', default=None) - if view is None: - continue - view.__request_attrs__ = {} - view.__request_attrs__['matchdict'] = match - view.__request_attrs__['matched_route'] = route - root_factory = route.factory or root_factory - root = root_factory(request) - traverser = adapters.queryAdapter(root, ITraverser) - if traverser is None: - traverser = ResourceTreeTraverser(root) - tdict = traverser(request) - view.__request_attrs__.update(tdict) - if not hasattr(view, '__view_attr__'): - view.__view_attr__ = '' - self.views.append((None, view, None)) - - - # create the request - environ = { - 'wsgi.url_scheme':'http', - 'SERVER_NAME':'localhost', - 'SERVER_PORT':'8080', - 'REQUEST_METHOD':'GET', - 'PATH_INFO':url, - } - request = request_factory(environ) - context = None - routes_multiview = None - attrs = request.__dict__ - attrs['registry'] = registry - request_iface = IRequest - - # find the root object - if routes_mapper is not None: - infos = self._find_multi_routes(routes_mapper, request) - if len(infos) == 1: - info = infos[0] - match, route = info['match'], info['route'] - if route is not None: - attrs['matchdict'] = match - attrs['matched_route'] = route - request.environ['bfg.routes.matchdict'] = match - request_iface = registry.queryUtility( - IRouteRequest, - name=route.name, - default=IRequest) - root_factory = route.factory or root_factory - if len(infos) > 1: - routes_multiview = infos - - root = root_factory(request) - attrs['root'] = root - - # find a context - traverser = adapters.queryAdapter(root, ITraverser) - if traverser is None: - traverser = ResourceTreeTraverser(root) - tdict = traverser(request) - context, view_name, subpath, traversed, vroot, vroot_path =( - tdict['context'], tdict['view_name'], tdict['subpath'], - tdict['traversed'], tdict['virtual_root'], - tdict['virtual_root_path']) - attrs.update(tdict) - - # find a view callable - context_iface = providedBy(context) - if routes_multiview is None: - view = adapters.lookup( - (IViewClassifier, request_iface, context_iface), - IView, name=view_name, default=None) - else: - view = RoutesMultiView(infos, context_iface, root_factory, request) - - # routes are not registered with a view name - if view is None: - view = adapters.lookup( - (IViewClassifier, request_iface, context_iface), - IView, name='', default=None) - # we don't want a multiview here - if IMultiView.providedBy(view): - view = None - - if view is not None: - view.__request_attrs__ = attrs - - return view - - def output_route_attrs(self, attrs, indent): - route = attrs['matched_route'] - self.out("%sroute name: %s" % (indent, route.name)) - self.out("%sroute pattern: %s" % (indent, route.pattern)) - self.out("%sroute path: %s" % (indent, route.path)) - self.out("%ssubpath: %s" % (indent, '/'.join(attrs['subpath']))) - predicates = ', '.join([p.__text__ for p in route.predicates]) - if predicates != '': - self.out("%sroute predicates (%s)" % (indent, predicates)) - - def output_view_info(self, view_wrapper, level=1): - indent = " " * level - name = getattr(view_wrapper, '__name__', '') - module = getattr(view_wrapper, '__module__', '') - attr = getattr(view_wrapper, '__view_attr__', None) - request_attrs = getattr(view_wrapper, '__request_attrs__', {}) - if attr is not None: - view_callable = "%s.%s.%s" % (module, name, attr) - else: - attr = view_wrapper.__class__.__name__ - if attr == 'function': - attr = name - view_callable = "%s.%s" % (module, attr) - self.out('') - if 'matched_route' in request_attrs: - self.out("%sRoute:" % indent) - self.out("%s------" % indent) - self.output_route_attrs(request_attrs, indent) - permission = getattr(view_wrapper, '__permission__', None) - if not IMultiView.providedBy(view_wrapper): - # single view for this route, so repeat call without route data - del request_attrs['matched_route'] - self.output_view_info(view_wrapper, level+1) - else: - self.out("%sView:" % indent) - self.out("%s-----" % indent) - self.out("%s%s" % (indent, view_callable)) - permission = getattr(view_wrapper, '__permission__', None) - if permission is not None: - self.out("%srequired permission = %s" % (indent, permission)) - predicates = getattr(view_wrapper, '__predicates__', None) - if predicates is not None: - predicate_text = ', '.join([p.__text__ for p in predicates]) - self.out("%sview predicates (%s)" % (indent, predicate_text)) - - def command(self): - config_uri, url = self.args - if not url.startswith('/'): - url = '/%s' % url - env = self.bootstrap[0](config_uri) - registry = env['registry'] - view = self._find_view(url, registry) - self.out('') - self.out("URL = %s" % url) - self.out('') - if view is not None: - self.out(" context: %s" % view.__request_attrs__['context']) - self.out(" view name: %s" % view.__request_attrs__['view_name']) - if IMultiView.providedBy(view): - for dummy, view_wrapper, dummy in view.views: - self.output_view_info(view_wrapper) - if IMultiView.providedBy(view_wrapper): - for dummy, mv_view_wrapper, dummy in view_wrapper.views: - self.output_view_info(mv_view_wrapper, level=2) - else: - if view is not None: - self.output_view_info(view) - else: - self.out(" Not found.") - self.out('') - - -class PTweensCommand(PCommand): - """Print all implicit and explicit :term:`tween` objects used by a - Pyramid application. The handler output includes whether the system is - using an explicit tweens ordering (will be true when the - ``pyramid.tweens`` setting is used) or an implicit tweens ordering (will - be true when the ``pyramid.tweens`` setting is *not* used). - - This command accepts one positional argument: - - ``config_uri`` -- specifies the PasteDeploy config file to use for the - interactive shell. The format is ``inifile#name``. If the name is left - off, ``main`` will be assumed. - - Example:: - - $ glue ptweens myapp.ini#main - - """ - summary = "Print all tweens related to a Pyramid application" - min_args = 1 - max_args = 1 - stdout = sys.stdout - - parser = Command.standard_parser(simulate=True) - - def _get_tweens(self, registry): - from pyramid.config import Configurator - config = Configurator(registry = registry) - return config.registry.queryUtility(ITweens) - - def out(self, msg): # pragma: no cover - print_(msg) - - def show_chain(self, chain): - fmt = '%-10s %-65s' - self.out(fmt % ('Position', 'Name')) - self.out(fmt % ('-'*len('Position'), '-'*len('Name'))) - self.out(fmt % ('-', INGRESS)) - for pos, (name, _) in enumerate(chain): - self.out(fmt % (pos, name)) - self.out(fmt % ('-', MAIN)) - - def command(self): - config_uri = self.args[0] - env = self.bootstrap[0](config_uri) - registry = env['registry'] - tweens = self._get_tweens(registry) - if tweens is not None: - explicit = tweens.explicit - if explicit: - self.out('"pyramid.tweens" config value set ' - '(explicitly ordered tweens used)') - self.out('') - self.out('Explicit Tween Chain (used)') - self.out('') - self.show_chain(tweens.explicit) - self.out('') - self.out('Implicit Tween Chain (not used)') - self.out('') - self.show_chain(tweens.implicit()) - else: - self.out('"pyramid.tweens" config value NOT set ' - '(implicitly ordered tweens used)') - self.out('') - self.out('Implicit Tween Chain') - self.out('') - self.show_chain(tweens.implicit()) diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl index b7c06aff3..d804a0b0e 100644 --- a/pyramid/scaffolds/alchemy/development.ini_tmpl +++ b/pyramid/scaffolds/alchemy/development.ini_tmpl @@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl index dc17e8338..7350ce25f 100644 --- a/pyramid/scaffolds/alchemy/production.ini_tmpl +++ b/pyramid/scaffolds/alchemy/production.ini_tmpl @@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/routesalchemy/development.ini_tmpl b/pyramid/scaffolds/routesalchemy/development.ini_tmpl index b7c06aff3..d804a0b0e 100644 --- a/pyramid/scaffolds/routesalchemy/development.ini_tmpl +++ b/pyramid/scaffolds/routesalchemy/development.ini_tmpl @@ -13,7 +13,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/routesalchemy/production.ini_tmpl b/pyramid/scaffolds/routesalchemy/production.ini_tmpl index dc17e8338..7350ce25f 100644 --- a/pyramid/scaffolds/routesalchemy/production.ini_tmpl +++ b/pyramid/scaffolds/routesalchemy/production.ini_tmpl @@ -12,7 +12,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl index c47fcec22..4d3a80286 100644 --- a/pyramid/scaffolds/starter/development.ini_tmpl +++ b/pyramid/scaffolds/starter/development.ini_tmpl @@ -10,7 +10,7 @@ pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl index 0634072c0..931cfa510 100644 --- a/pyramid/scaffolds/starter/production.ini_tmpl +++ b/pyramid/scaffolds/starter/production.ini_tmpl @@ -9,7 +9,7 @@ pyramid.debug_templates = false pyramid.default_locale_name = en [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl index 661e042a2..eb2b0d3de 100644 --- a/pyramid/scaffolds/zodb/development.ini_tmpl +++ b/pyramid/scaffolds/zodb/development.ini_tmpl @@ -14,7 +14,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl index 2378f9751..7c91ea0df 100644 --- a/pyramid/scaffolds/zodb/production.ini_tmpl +++ b/pyramid/scaffolds/zodb/production.ini_tmpl @@ -13,7 +13,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:glue#wsgiref +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py new file mode 100644 index 000000000..0dab9acda --- /dev/null +++ b/pyramid/scripts/proutes.py @@ -0,0 +1,75 @@ +import optparse +import sys + +from pyramid.compat import print_ +from pyramid.paster import bootstrap + +def main(argv=sys.argv): + command = PRoutesCommand(argv) + command.run() + +class PRoutesCommand(object): + """Print all URL dispatch routes used by a Pyramid application in the + order in which they are evaluated. Each route includes the name of the + route, the pattern of the route, and the view callable which will be + invoked when the route is matched. + + This command accepts one positional argument: + + ``config_uri`` -- specifies the PasteDeploy config file to use for the + interactive shell. The format is ``inifile#name``. If the name is left + off, ``main`` will be assumed. + + Example:: + + $ proutes myapp.ini#main + + """ + bootstrap = (bootstrap,) + summary = "Print all URL dispatch routes related to a Pyramid application" + min_args = 1 + max_args = 1 + stdout = sys.stdout + + parser = optparse.OptionParser() + + def __init__(self, argv): + self.options, self.args = self.parser.parse_args(argv[1:]) + + def _get_mapper(self, registry): + from pyramid.config import Configurator + config = Configurator(registry = registry) + return config.get_routes_mapper() + + def out(self, msg): # pragma: no cover + print_(msg) + + def run(self): + 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) + registry = env['registry'] + mapper = self._get_mapper(registry) + if mapper is not None: + routes = mapper.get_routes() + fmt = '%-15s %-30s %-25s' + if not routes: + return + self.out(fmt % ('Name', 'Pattern', 'View')) + self.out( + fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View'))) + for route in routes: + request_iface = registry.queryUtility(IRouteRequest, + name=route.name) + view_callable = None + if (request_iface is None) or (route.factory is not None): + self.out(fmt % (route.name, route.pattern, '')) + else: + view_callable = registry.adapters.lookup( + (IViewClassifier, request_iface, Interface), + IView, name='', default=None) + self.out(fmt % (route.name, route.pattern, view_callable)) + diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py new file mode 100644 index 000000000..85c7e16ea --- /dev/null +++ b/pyramid/scripts/pshell.py @@ -0,0 +1,200 @@ +from code import interact +import optparse +import os +import sys +from logging.config import fileConfig + +from pyramid.compat import configparser +from pyramid.util import DottedNameResolver +from pyramid.paster import bootstrap + +def main(argv=sys.argv): + command = PShellCommand(argv) + return command.run() + +class PShellCommand(object): + """Open an interactive shell with a :app:`Pyramid` app loaded. + + This command accepts one positional argument: + + ``config_uri`` -- specifies the PasteDeploy config file to use for the + interactive shell. The format is ``inifile#name``. If the name is left + off, ``main`` will be assumed. + + Example:: + + $ pshell myapp.ini#main + + .. note:: If you do not point the loader directly at the section of the + ini file containing your :app:`Pyramid` application, the + command will attempt to find the app for you. If you are + loading a pipeline that contains more than one :app:`Pyramid` + application within it, the loader will use the last one. + + """ + bootstrap = (bootstrap,) # for testing + summary = "Open an interactive shell with a Pyramid application loaded" + + min_args = 1 + max_args = 1 + + parser = optparse.OptionParser() + parser.add_option('-d', '--disable-ipython', + action='store_true', + dest='disable_ipython', + help="Don't use IPython even if it is available") + parser.add_option('--setup', + dest='setup', + help=("A callable that will be passed the environment " + "before it is made available to the shell. This " + "option will override the 'setup' key in the " + "[pshell] ini section.")) + + ConfigParser = configparser.ConfigParser # testing + + loaded_objects = {} + object_help = {} + setup = None + + def __init__(self, argv): + self.options, self.args = self.parser.parse_args(argv[1:]) + + def pshell_file_config(self, filename): + config = self.ConfigParser() + config.read(filename) + try: + items = config.items('pshell') + except configparser.NoSectionError: + return + + resolver = DottedNameResolver(None) + self.loaded_objects = {} + self.object_help = {} + self.setup = None + for k, v in items: + if k == 'setup': + self.setup = v + else: + self.loaded_objects[k] = resolver.maybe_resolve(v) + self.object_help[k] = v + + def run(self, shell=None): + config_uri = self.args[0] + config_file = config_uri.split('#', 1)[0] + self.logging_file_config(config_file) + self.pshell_file_config(config_file) + + # bootstrap the environ + env = self.bootstrap[0](config_uri) + + # remove the closer from the env + closer = env.pop('closer') + + # setup help text for default environment + env_help = dict(env) + env_help['app'] = 'The WSGI application.' + env_help['root'] = 'Root of the default resource tree.' + env_help['registry'] = 'Active Pyramid registry.' + env_help['request'] = 'Active request object.' + env_help['root_factory'] = ( + 'Default root factory used to create `root`.') + + # override use_script with command-line options + if self.options.setup: + self.setup = self.options.setup + + if self.setup: + # store the env before muddling it with the script + orig_env = env.copy() + + # call the setup callable + resolver = DottedNameResolver(None) + setup = resolver.maybe_resolve(self.setup) + setup(env) + + # remove any objects from default help that were overidden + for k, v in env.iteritems(): + if k not in orig_env or env[k] != orig_env[k]: + env_help[k] = v + + # load the pshell section of the ini file + env.update(self.loaded_objects) + + # eliminate duplicates from env, allowing custom vars to override + for k in self.loaded_objects: + if k in env_help: + del env_help[k] + + # generate help text + help = '' + if env_help: + help += 'Environment:' + for var in sorted(env_help.keys()): + help += '\n %-12s %s' % (var, env_help[var]) + + if self.object_help: + help += '\n\nCustom Variables:' + for var in sorted(self.object_help.keys()): + help += '\n %-12s %s' % (var, self.object_help[var]) + + if shell is None and not self.options.disable_ipython: + shell = self.make_ipython_v0_11_shell() + if shell is None: + shell = self.make_ipython_v0_10_shell() + + if shell is None: + shell = self.make_default_shell() + + try: + shell(env, help) + finally: + closer() + + def make_default_shell(self, interact=interact): + def shell(env, help): + cprt = 'Type "help" for more information.' + banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt) + banner += '\n\n' + help + '\n' + interact(banner, local=env) + return shell + + def make_ipython_v0_11_shell(self, IPShellFactory=None): + if IPShellFactory is None: # pragma: no cover + try: + from IPython.frontend.terminal.embed import ( + InteractiveShellEmbed) + IPShellFactory = InteractiveShellEmbed + except ImportError: + return None + def shell(env, help): + IPShell = IPShellFactory(banner2=help + '\n', user_ns=env) + IPShell() + return shell + + def make_ipython_v0_10_shell(self, IPShellFactory=None): + if IPShellFactory is None: # pragma: no cover + try: + from IPython.Shell import IPShellEmbed + IPShellFactory = IPShellEmbed + except ImportError: + return None + def shell(env, help): + IPShell = IPShellFactory(argv=[], user_ns=env) + IPShell.set_banner(IPShell.IP.BANNER + '\n' + help + '\n') + IPShell() + return shell + + def logging_file_config(self, config_file): + """ + Setup logging via the logging module's fileConfig function with the + specified ``config_file``, if applicable. + + ConfigParser defaults are specified for the special ``__file__`` + and ``here`` variables, similar to PasteDeploy config loading. + """ + parser = configparser.ConfigParser() + parser.read([config_file]) + if parser.has_section('loggers'): + config_file = os.path.abspath(config_file) + fileConfig(config_file, dict(__file__=config_file, + here=os.path.dirname(config_file))) diff --git a/pyramid/scripts/ptweens.py b/pyramid/scripts/ptweens.py new file mode 100644 index 000000000..56d8b422b --- /dev/null +++ b/pyramid/scripts/ptweens.py @@ -0,0 +1,87 @@ +import optparse +import sys + +from pyramid.interfaces import ITweens + +from pyramid.tweens import MAIN +from pyramid.tweens import INGRESS +from pyramid.paster import bootstrap + +from pyramid.compat import print_ + +def main(argv=sys.argv): + command = PTweensCommand(argv) + command.run() + +class PTweensCommand(object): + """Print all implicit and explicit :term:`tween` objects used by a + Pyramid application. The handler output includes whether the system is + using an explicit tweens ordering (will be true when the + ``pyramid.tweens`` setting is used) or an implicit tweens ordering (will + be true when the ``pyramid.tweens`` setting is *not* used). + + This command accepts one positional argument: + + ``config_uri`` -- specifies the PasteDeploy config file to use for the + interactive shell. The format is ``inifile#name``. If the name is left + off, ``main`` will be assumed. + + Example:: + + $ ptweens myapp.ini#main + + """ + summary = "Print all tweens related to a Pyramid application" + min_args = 1 + max_args = 1 + stdout = sys.stdout + + parser = optparse.OptionParser() + + bootstrap = (bootstrap,) # testing + + def __init__(self, argv): + self.options, self.args = self.parser.parse_args(argv[1:]) + + def _get_tweens(self, registry): + from pyramid.config import Configurator + config = Configurator(registry = registry) + return config.registry.queryUtility(ITweens) + + def out(self, msg): # pragma: no cover + print_(msg) + + def show_chain(self, chain): + fmt = '%-10s %-65s' + self.out(fmt % ('Position', 'Name')) + self.out(fmt % ('-'*len('Position'), '-'*len('Name'))) + self.out(fmt % ('-', INGRESS)) + for pos, (name, _) in enumerate(chain): + self.out(fmt % (pos, name)) + self.out(fmt % ('-', MAIN)) + + def run(self): + config_uri = self.args[0] + env = self.bootstrap[0](config_uri) + registry = env['registry'] + tweens = self._get_tweens(registry) + if tweens is not None: + explicit = tweens.explicit + if explicit: + self.out('"pyramid.tweens" config value set ' + '(explicitly ordered tweens used)') + self.out('') + self.out('Explicit Tween Chain (used)') + self.out('') + self.show_chain(tweens.explicit) + self.out('') + self.out('Implicit Tween Chain (not used)') + self.out('') + self.show_chain(tweens.implicit()) + else: + self.out('"pyramid.tweens" config value NOT set ' + '(implicitly ordered tweens used)') + self.out('') + self.out('Implicit Tween Chain') + self.out('') + self.show_chain(tweens.implicit()) diff --git a/pyramid/scripts/pviews.py b/pyramid/scripts/pviews.py new file mode 100644 index 000000000..69baaee87 --- /dev/null +++ b/pyramid/scripts/pviews.py @@ -0,0 +1,256 @@ +import optparse +import sys + +from pyramid.compat import print_ +from pyramid.interfaces import IMultiView +from pyramid.paster import bootstrap + +def main(argv=sys.argv): + command = PViewsCommand(argv) + command.run() + +class PViewsCommand(object): + """Print, for a given URL, the views that might match. Underneath each + potentially matching route, list the predicates required. Underneath + each route+predicate set, print each view that might match and its + predicates. + + This command accepts two positional arguments: + + ``config_uri`` -- specifies the PasteDeploy config file to use for the + interactive shell. The format is ``inifile#name``. If the name is left + off, ``main`` will be assumed. + + ``url`` -- specifies the URL that will be used to find matching views. + + Example:: + + $ proutes myapp.ini#main url + + """ + summary = "Print all views in an application that might match a URL" + min_args = 2 + max_args = 2 + stdout = sys.stdout + + parser = optparse.OptionParser() + + bootstrap = (bootstrap,) # testing + + def __init__(self, argv): + self.options, self.args = self.parser.parse_args(argv[1:]) + + def out(self, msg): # pragma: no cover + print_(msg) + + def _find_multi_routes(self, mapper, request): + infos = [] + path = request.environ['PATH_INFO'] + # find all routes that match path, regardless of predicates + for route in mapper.get_routes(): + match = route.match(path) + if match is not None: + info = {'match':match, 'route':route} + infos.append(info) + return infos + + def _find_view(self, url, registry): + """ + Accept ``url`` and ``registry``; create a :term:`request` and + find a :app:`Pyramid` view based on introspection of :term:`view + configuration` within the application registry; return the view. + """ + from zope.interface import providedBy + from zope.interface import implementer + from pyramid.interfaces import IRequest + from pyramid.interfaces import IRootFactory + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IRequestFactory + from pyramid.interfaces import IRoutesMapper + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import ITraverser + from pyramid.request import Request + from pyramid.traversal import DefaultRootFactory + from pyramid.traversal import ResourceTreeTraverser + + q = registry.queryUtility + root_factory = q(IRootFactory, default=DefaultRootFactory) + routes_mapper = q(IRoutesMapper) + request_factory = q(IRequestFactory, default=Request) + + adapters = registry.adapters + request = None + + @implementer(IMultiView) + class RoutesMultiView(object): + + def __init__(self, infos, context_iface, root_factory, request): + self.views = [] + for info in infos: + match, route = info['match'], info['route'] + if route is not None: + request_iface = registry.queryUtility( + IRouteRequest, + name=route.name, + default=IRequest) + view = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name='', default=None) + if view is None: + continue + view.__request_attrs__ = {} + view.__request_attrs__['matchdict'] = match + view.__request_attrs__['matched_route'] = route + root_factory = route.factory or root_factory + root = root_factory(request) + traverser = adapters.queryAdapter(root, ITraverser) + if traverser is None: + traverser = ResourceTreeTraverser(root) + tdict = traverser(request) + view.__request_attrs__.update(tdict) + if not hasattr(view, '__view_attr__'): + view.__view_attr__ = '' + self.views.append((None, view, None)) + + + # create the request + environ = { + 'wsgi.url_scheme':'http', + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'8080', + 'REQUEST_METHOD':'GET', + 'PATH_INFO':url, + } + request = request_factory(environ) + context = None + routes_multiview = None + attrs = request.__dict__ + attrs['registry'] = registry + request_iface = IRequest + + # find the root object + if routes_mapper is not None: + infos = self._find_multi_routes(routes_mapper, request) + if len(infos) == 1: + info = infos[0] + match, route = info['match'], info['route'] + if route is not None: + attrs['matchdict'] = match + attrs['matched_route'] = route + request.environ['bfg.routes.matchdict'] = match + request_iface = registry.queryUtility( + IRouteRequest, + name=route.name, + default=IRequest) + root_factory = route.factory or root_factory + if len(infos) > 1: + routes_multiview = infos + + root = root_factory(request) + attrs['root'] = root + + # find a context + traverser = adapters.queryAdapter(root, ITraverser) + if traverser is None: + traverser = ResourceTreeTraverser(root) + tdict = traverser(request) + context, view_name, subpath, traversed, vroot, vroot_path =( + tdict['context'], tdict['view_name'], tdict['subpath'], + tdict['traversed'], tdict['virtual_root'], + tdict['virtual_root_path']) + attrs.update(tdict) + + # find a view callable + context_iface = providedBy(context) + if routes_multiview is None: + view = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name=view_name, default=None) + else: + view = RoutesMultiView(infos, context_iface, root_factory, request) + + # routes are not registered with a view name + if view is None: + view = adapters.lookup( + (IViewClassifier, request_iface, context_iface), + IView, name='', default=None) + # we don't want a multiview here + if IMultiView.providedBy(view): + view = None + + if view is not None: + view.__request_attrs__ = attrs + + return view + + def output_route_attrs(self, attrs, indent): + route = attrs['matched_route'] + self.out("%sroute name: %s" % (indent, route.name)) + self.out("%sroute pattern: %s" % (indent, route.pattern)) + self.out("%sroute path: %s" % (indent, route.path)) + self.out("%ssubpath: %s" % (indent, '/'.join(attrs['subpath']))) + predicates = ', '.join([p.__text__ for p in route.predicates]) + if predicates != '': + self.out("%sroute predicates (%s)" % (indent, predicates)) + + def output_view_info(self, view_wrapper, level=1): + indent = " " * level + name = getattr(view_wrapper, '__name__', '') + module = getattr(view_wrapper, '__module__', '') + attr = getattr(view_wrapper, '__view_attr__', None) + request_attrs = getattr(view_wrapper, '__request_attrs__', {}) + if attr is not None: + view_callable = "%s.%s.%s" % (module, name, attr) + else: + attr = view_wrapper.__class__.__name__ + if attr == 'function': + attr = name + view_callable = "%s.%s" % (module, attr) + self.out('') + if 'matched_route' in request_attrs: + self.out("%sRoute:" % indent) + self.out("%s------" % indent) + self.output_route_attrs(request_attrs, indent) + permission = getattr(view_wrapper, '__permission__', None) + if not IMultiView.providedBy(view_wrapper): + # single view for this route, so repeat call without route data + del request_attrs['matched_route'] + self.output_view_info(view_wrapper, level+1) + else: + self.out("%sView:" % indent) + self.out("%s-----" % indent) + self.out("%s%s" % (indent, view_callable)) + permission = getattr(view_wrapper, '__permission__', None) + if permission is not None: + self.out("%srequired permission = %s" % (indent, permission)) + predicates = getattr(view_wrapper, '__predicates__', None) + if predicates is not None: + predicate_text = ', '.join([p.__text__ for p in predicates]) + self.out("%sview predicates (%s)" % (indent, predicate_text)) + + def run(self): + config_uri, url = self.args + if not url.startswith('/'): + url = '/%s' % url + env = self.bootstrap[0](config_uri) + registry = env['registry'] + view = self._find_view(url, registry) + self.out('') + self.out("URL = %s" % url) + self.out('') + if view is not None: + self.out(" context: %s" % view.__request_attrs__['context']) + self.out(" view name: %s" % view.__request_attrs__['view_name']) + if IMultiView.providedBy(view): + for dummy, view_wrapper, dummy in view.views: + self.output_view_info(view_wrapper) + if IMultiView.providedBy(view_wrapper): + for dummy, mv_view_wrapper, dummy in view_wrapper.views: + self.output_view_info(mv_view_wrapper, level=2) + else: + if view is not None: + self.output_view_info(view) + else: + self.out(" Not found.") + self.out('') diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py index a697b5691..0182ea08b 100644 --- a/pyramid/tests/test_paster.py +++ b/pyramid/tests/test_paster.py @@ -1,834 +1,5 @@ import unittest -from pyramid.testing import skip_on -@skip_on('py3') -class TestPShellCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.paster import PShellCommand - return PShellCommand - - def _makeOne(self, patch_bootstrap=True, patch_config=True, - patch_args=True, patch_options=True): - cmd = self._getTargetClass()('pshell') - if patch_bootstrap: - self.bootstrap = DummyBootstrap() - cmd.bootstrap = (self.bootstrap,) - if patch_config: - self.config_factory = DummyConfigParserFactory() - cmd.ConfigParser = self.config_factory - if patch_args: - self.args = ('/foo/bar/myapp.ini#myapp',) - cmd.args = self.args - if patch_options: - class Options(object): pass - self.options = Options() - self.options.disable_ipython = True - self.options.setup = None - cmd.options = self.options - return cmd - - def test_make_default_shell(self): - command = self._makeOne() - interact = DummyInteractor() - shell = command.make_default_shell(interact) - shell({'foo': 'bar'}, 'a help message') - self.assertEqual(interact.local, {'foo': 'bar'}) - self.assertTrue('a help message' in interact.banner) - - def test_make_ipython_v0_11_shell(self): - command = self._makeOne() - ipshell_factory = DummyIPShellFactory() - shell = command.make_ipython_v0_11_shell(ipshell_factory) - shell({'foo': 'bar'}, 'a help message') - self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) - self.assertTrue('a help message' in ipshell_factory.kw['banner2']) - self.assertTrue(ipshell_factory.shell.called) - - def test_make_ipython_v0_10_shell(self): - command = self._makeOne() - ipshell_factory = DummyIPShellFactory() - shell = command.make_ipython_v0_10_shell(ipshell_factory) - shell({'foo': 'bar'}, 'a help message') - self.assertEqual(ipshell_factory.kw['argv'], []) - self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) - self.assertTrue('a help message' in ipshell_factory.shell.banner) - self.assertTrue(ipshell_factory.shell.called) - - def test_command_loads_default_shell(self): - command = self._makeOne() - shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: None - command.make_ipython_v0_10_shell = lambda: None - command.make_default_shell = lambda: shell - command.command() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_default_shell_with_ipython_disabled(self): - command = self._makeOne() - shell = DummyShell() - bad_shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: bad_shell - command.make_ipython_v0_10_shell = lambda: bad_shell - command.make_default_shell = lambda: shell - command.options.disable_ipython = True - command.command() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertEqual(bad_shell.env, {}) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_ipython_v0_11(self): - command = self._makeOne() - shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: shell - command.make_ipython_v0_10_shell = lambda: None - command.make_default_shell = lambda: None - command.options.disable_ipython = False - command.command() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_ipython_v0_10(self): - command = self._makeOne() - shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: None - command.make_ipython_v0_10_shell = lambda: shell - command.make_default_shell = lambda: None - command.options.disable_ipython = False - command.command() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_custom_items(self): - command = self._makeOne() - model = Dummy() - self.config_factory.items = [('m', model)] - shell = DummyShell() - command.command(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'm':model, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_setup(self): - command = self._makeOne() - def setup(env): - env['a'] = 1 - env['root'] = 'root override' - self.config_factory.items = [('setup', setup)] - shell = DummyShell() - command.command(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':'root override', - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'a':1, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_check_variable_override_order(self): - command = self._makeOne() - model = Dummy() - def setup(env): - env['a'] = 1 - env['m'] = 'model override' - env['root'] = 'root override' - self.config_factory.items = [('setup', setup), ('m', model)] - shell = DummyShell() - command.command(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':'root override', - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'a':1, 'm':model, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_setup_from_options(self): - command = self._makeOne() - def setup(env): - env['a'] = 1 - env['root'] = 'root override' - model = Dummy() - self.config_factory.items = [('setup', 'abc'), - ('m', model)] - command.options.setup = setup - shell = DummyShell() - command.command(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':'root override', - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'a':1, 'm':model, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_custom_section_override(self): - command = self._makeOne() - dummy = Dummy() - self.config_factory.items = [('app', dummy), ('root', dummy), - ('registry', dummy), ('request', dummy)] - shell = DummyShell() - command.command(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':dummy, 'root':dummy, 'registry':dummy, 'request':dummy, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - -@skip_on('py3') -class TestPRoutesCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.paster import PRoutesCommand - return PRoutesCommand - - def _makeOne(self): - cmd = self._getTargetClass()('proutes') - cmd.bootstrap = (DummyBootstrap(),) - cmd.args = ('/foo/bar/myapp.ini#myapp',) - return cmd - - def test_no_routes(self): - command = self._makeOne() - mapper = DummyMapper() - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L, []) - - def test_no_mapper(self): - command = self._makeOne() - command._get_mapper = lambda *arg:None - L = [] - command.out = L.append - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L, []) - - def test_single_route_no_route_registered(self): - command = self._makeOne() - route = DummyRoute('a', '/a') - mapper = DummyMapper(route) - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - result = command.command() - self.assertEqual(result, None) - self.assertEqual(len(L), 3) - self.assertEqual(L[-1].split(), ['a', '/a', '']) - - def test_single_route_no_views_registered(self): - from zope.interface import Interface - from pyramid.registry import Registry - from pyramid.interfaces import IRouteRequest - registry = Registry() - def view():pass - class IMyRoute(Interface): - pass - registry.registerUtility(IMyRoute, IRouteRequest, name='a') - command = self._makeOne() - route = DummyRoute('a', '/a') - mapper = DummyMapper(route) - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - command.bootstrap = (DummyBootstrap(registry=registry),) - result = command.command() - self.assertEqual(result, None) - self.assertEqual(len(L), 3) - self.assertEqual(L[-1].split()[:3], ['a', '/a', 'None']) - - def test_single_route_one_view_registered(self): - from zope.interface import Interface - from pyramid.registry import Registry - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - registry = Registry() - def view():pass - class IMyRoute(Interface): - pass - registry.registerAdapter(view, - (IViewClassifier, IMyRoute, Interface), - IView, '') - registry.registerUtility(IMyRoute, IRouteRequest, name='a') - command = self._makeOne() - route = DummyRoute('a', '/a') - mapper = DummyMapper(route) - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - command.bootstrap = (DummyBootstrap(registry=registry),) - result = command.command() - self.assertEqual(result, None) - self.assertEqual(len(L), 3) - self.assertEqual(L[-1].split()[:4], ['a', '/a', '']) - - def test__get_mapper(self): - from pyramid.registry import Registry - from pyramid.urldispatch import RoutesMapper - command = self._makeOne() - registry = Registry() - result = command._get_mapper(registry) - self.assertEqual(result.__class__, RoutesMapper) - -@skip_on('py3') -class TestPViewsCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.paster import PViewsCommand - return PViewsCommand - - def _makeOne(self, registry=None): - cmd = self._getTargetClass()('pviews') - cmd.bootstrap = (DummyBootstrap(registry=registry),) - cmd.args = ('/foo/bar/myapp.ini#myapp',) - return cmd - - def _register_mapper(self, registry, routes): - from pyramid.interfaces import IRoutesMapper - mapper = DummyMapper(*routes) - registry.registerUtility(mapper, IRoutesMapper) - - def test__find_view_no_match(self): - from pyramid.registry import Registry - registry = Registry() - self._register_mapper(registry, []) - command = self._makeOne(registry) - result = command._find_view('/a', registry) - self.assertEqual(result, None) - - def test__find_view_no_match_multiview_registered(self): - from zope.interface import implementer - from zope.interface import providedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - from pyramid.traversal import DefaultRootFactory - from pyramid.registry import Registry - registry = Registry() - @implementer(IMultiView) - class View1(object): - pass - request = DummyRequest({'PATH_INFO':'/a'}) - root = DefaultRootFactory(request) - root_iface = providedBy(root) - registry.registerAdapter(View1(), - (IViewClassifier, IRequest, root_iface), - IMultiView) - self._register_mapper(registry, []) - command = self._makeOne(registry=registry) - result = command._find_view('/x', registry) - self.assertEqual(result, None) - - def test__find_view_traversal(self): - from zope.interface import providedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - from pyramid.traversal import DefaultRootFactory - from pyramid.registry import Registry - registry = Registry() - def view1(): pass - request = DummyRequest({'PATH_INFO':'/a'}) - root = DefaultRootFactory(request) - root_iface = providedBy(root) - registry.registerAdapter(view1, - (IViewClassifier, IRequest, root_iface), - IView, name='a') - self._register_mapper(registry, []) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertEqual(result, view1) - - def test__find_view_traversal_multiview(self): - from zope.interface import implementer - from zope.interface import providedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - from pyramid.traversal import DefaultRootFactory - from pyramid.registry import Registry - registry = Registry() - @implementer(IMultiView) - class View1(object): - pass - request = DummyRequest({'PATH_INFO':'/a'}) - root = DefaultRootFactory(request) - root_iface = providedBy(root) - view = View1() - registry.registerAdapter(view, - (IViewClassifier, IRequest, root_iface), - IMultiView, name='a') - self._register_mapper(registry, []) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertEqual(result, view) - - def test__find_view_route_no_multiview(self): - from zope.interface import Interface - from zope.interface import implementer - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - from pyramid.registry import Registry - registry = Registry() - def view():pass - class IMyRoot(Interface): - pass - class IMyRoute(Interface): - pass - registry.registerAdapter(view, - (IViewClassifier, IMyRoute, IMyRoot), - IView, '') - registry.registerUtility(IMyRoute, IRouteRequest, name='a') - @implementer(IMyRoot) - class Factory(object): - def __init__(self, request): - pass - routes = [DummyRoute('a', '/a', factory=Factory, matchdict={}), - DummyRoute('b', '/b', factory=Factory)] - self._register_mapper(registry, routes) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertEqual(result, view) - - def test__find_view_route_multiview_no_view_registered(self): - from zope.interface import Interface - from zope.interface import implementer - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IRootFactory - from pyramid.registry import Registry - registry = Registry() - def view1():pass - def view2():pass - class IMyRoot(Interface): - pass - class IMyRoute1(Interface): - pass - class IMyRoute2(Interface): - pass - registry.registerUtility(IMyRoute1, IRouteRequest, name='a') - registry.registerUtility(IMyRoute2, IRouteRequest, name='b') - @implementer(IMyRoot) - class Factory(object): - def __init__(self, request): - pass - registry.registerUtility(Factory, IRootFactory) - routes = [DummyRoute('a', '/a', matchdict={}), - DummyRoute('b', '/a', matchdict={})] - self._register_mapper(registry, routes) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertTrue(IMultiView.providedBy(result)) - - def test__find_view_route_multiview(self): - from zope.interface import Interface - from zope.interface import implementer - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IRootFactory - from pyramid.registry import Registry - registry = Registry() - def view1():pass - def view2():pass - class IMyRoot(Interface): - pass - class IMyRoute1(Interface): - pass - class IMyRoute2(Interface): - pass - registry.registerAdapter(view1, - (IViewClassifier, IMyRoute1, IMyRoot), - IView, '') - registry.registerAdapter(view2, - (IViewClassifier, IMyRoute2, IMyRoot), - IView, '') - registry.registerUtility(IMyRoute1, IRouteRequest, name='a') - registry.registerUtility(IMyRoute2, IRouteRequest, name='b') - @implementer(IMyRoot) - class Factory(object): - def __init__(self, request): - pass - registry.registerUtility(Factory, IRootFactory) - routes = [DummyRoute('a', '/a', matchdict={}), - DummyRoute('b', '/a', matchdict={})] - self._register_mapper(registry, routes) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertTrue(IMultiView.providedBy(result)) - self.assertEqual(len(result.views), 2) - self.assertTrue((None, view1, None) in result.views) - self.assertTrue((None, view2, None) in result.views) - - def test__find_multi_routes_all_match(self): - command = self._makeOne() - def factory(request): pass - routes = [DummyRoute('a', '/a', factory=factory, matchdict={}), - DummyRoute('b', '/a', factory=factory, matchdict={})] - mapper = DummyMapper(*routes) - request = DummyRequest({'PATH_INFO':'/a'}) - result = command._find_multi_routes(mapper, request) - self.assertEqual(result, [{'match':{}, 'route':routes[0]}, - {'match':{}, 'route':routes[1]}]) - - def test__find_multi_routes_some_match(self): - command = self._makeOne() - def factory(request): pass - routes = [DummyRoute('a', '/a', factory=factory), - DummyRoute('b', '/a', factory=factory, matchdict={})] - mapper = DummyMapper(*routes) - request = DummyRequest({'PATH_INFO':'/a'}) - result = command._find_multi_routes(mapper, request) - self.assertEqual(result, [{'match':{}, 'route':routes[1]}]) - - def test__find_multi_routes_none_match(self): - command = self._makeOne() - def factory(request): pass - routes = [DummyRoute('a', '/a', factory=factory), - DummyRoute('b', '/a', factory=factory)] - mapper = DummyMapper(*routes) - request = DummyRequest({'PATH_INFO':'/a'}) - result = command._find_multi_routes(mapper, request) - self.assertEqual(result, []) - - def test_views_command_not_found(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - command._find_view = lambda arg1, arg2: None - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' Not found.') - - def test_views_command_not_found_url_starts_without_slash(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - command._find_view = lambda arg1, arg2: None - command.args = ('/foo/bar/myapp.ini#myapp', 'a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' Not found.') - - def test_views_command_single_view_traversal(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context', view_name='a') - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyView') - - def test_views_command_single_view_function_traversal(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def view(): pass - view.__request_attrs__ = {'context': 'context', 'view_name': 'a'} - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.view') - - def test_views_command_single_view_traversal_with_permission(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context', view_name='a') - view.__permission__ = 'test' - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyView') - self.assertEqual(L[9], ' required permission = test') - - def test_views_command_single_view_traversal_with_predicates(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def predicate(): pass - predicate.__text__ = "predicate = x" - view = DummyView(context='context', view_name='a') - view.__predicates__ = [predicate] - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyView') - self.assertEqual(L[9], ' view predicates (predicate = x)') - - def test_views_command_single_view_route(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - route = DummyRoute('a', '/a', matchdict={}) - view = DummyView(context='context', view_name='a', - matched_route=route, subpath='') - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[6], ' Route:') - self.assertEqual(L[8], ' route name: a') - self.assertEqual(L[9], ' route pattern: /a') - self.assertEqual(L[10], ' route path: /a') - self.assertEqual(L[11], ' subpath: ') - self.assertEqual(L[15], ' pyramid.tests.test_paster.DummyView') - - def test_views_command_multi_view_nested(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view1 = DummyView(context='context', view_name='a1') - view1.__name__ = 'view1' - view1.__view_attr__ = 'call' - multiview1 = DummyMultiView(view1, context='context', view_name='a1') - multiview2 = DummyMultiView(multiview1, context='context', - view_name='a') - command._find_view = lambda arg1, arg2: multiview2 - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.DummyMultiView') - self.assertEqual(L[12], ' pyramid.tests.test_paster.view1.call') - - def test_views_command_single_view_route_with_route_predicates(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def predicate(): pass - predicate.__text__ = "predicate = x" - route = DummyRoute('a', '/a', matchdict={}, predicate=predicate) - view = DummyView(context='context', view_name='a', - matched_route=route, subpath='') - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[6], ' Route:') - self.assertEqual(L[8], ' route name: a') - self.assertEqual(L[9], ' route pattern: /a') - self.assertEqual(L[10], ' route path: /a') - self.assertEqual(L[11], ' subpath: ') - self.assertEqual(L[12], ' route predicates (predicate = x)') - self.assertEqual(L[16], ' pyramid.tests.test_paster.DummyView') - - def test_views_command_multiview(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context') - view.__name__ = 'view' - view.__view_attr__ = 'call' - multiview = DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.view.call') - - def test_views_command_multiview_with_permission(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context') - view.__name__ = 'view' - view.__view_attr__ = 'call' - view.__permission__ = 'test' - multiview = DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.view.call') - self.assertEqual(L[9], ' required permission = test') - - def test_views_command_multiview_with_predicates(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def predicate(): pass - predicate.__text__ = "predicate = x" - view = DummyView(context='context') - view.__name__ = 'view' - view.__view_attr__ = 'call' - view.__predicates__ = [predicate] - multiview = DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_paster.view.call') - self.assertEqual(L[9], ' view predicates (predicate = x)') - -@skip_on('py3') class TestGetApp(unittest.TestCase): def _callFUT(self, config_file, section_name, loadapp): from pyramid.paster import get_app @@ -864,7 +35,6 @@ class TestGetApp(unittest.TestCase): self.assertEqual(loadapp.relative_to, os.getcwd()) self.assertEqual(result, app) -@skip_on('py3') class TestBootstrap(unittest.TestCase): def _callFUT(self, config_uri, request=None): from pyramid.paster import bootstrap @@ -904,70 +74,9 @@ class TestBootstrap(unittest.TestCase): self.assertEqual(result['root'], self.root) self.assert_('closer' in result) -@skip_on('py3') -class TestPTweensCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.paster import PTweensCommand - return PTweensCommand - - def _makeOne(self): - cmd = self._getTargetClass()('ptweens') - cmd.bootstrap = (DummyBootstrap(),) - cmd.args = ('/foo/bar/myapp.ini#myapp',) - return cmd - - def test_command_no_tweens(self): - command = self._makeOne() - command._get_tweens = lambda *arg: None - L = [] - command.out = L.append - result = command.command() - self.assertEqual(result, None) - self.assertEqual(L, []) - - def test_command_implicit_tweens_only(self): - command = self._makeOne() - tweens = DummyTweens([('name', 'item')], None) - command._get_tweens = lambda *arg: tweens - L = [] - command.out = L.append - result = command.command() - self.assertEqual(result, None) - self.assertEqual( - L[0], - '"pyramid.tweens" config value NOT set (implicitly ordered tweens ' - 'used)') - - def test_command_implicit_and_explicit_tweens(self): - command = self._makeOne() - tweens = DummyTweens([('name', 'item')], [('name2', 'item2')]) - command._get_tweens = lambda *arg: tweens - L = [] - command.out = L.append - result = command.command() - self.assertEqual(result, None) - self.assertEqual( - L[0], - '"pyramid.tweens" config value set (explicitly ordered tweens used)') - - def test__get_tweens(self): - command = self._makeOne() - registry = DummyRegistry() - self.assertEqual(command._get_tweens(registry), None) - -class DummyTweens(object): - def __init__(self, implicit, explicit): - self._implicit = implicit - self.explicit = explicit - self.name_to_alias = {} - def implicit(self): - return self._implicit - class Dummy: pass -dummy_root = Dummy() - class DummyRegistry(object): settings = {} def queryUtility(self, iface, default=None, name=''): @@ -975,35 +84,6 @@ class DummyRegistry(object): dummy_registry = DummyRegistry() -class DummyShell(object): - env = {} - help = '' - - def __call__(self, env, help): - self.env = env - self.help = help - -class DummyInteractor: - def __call__(self, banner, local): - self.banner = banner - self.local = local - -class DummyIPShell(object): - IP = Dummy() - IP.BANNER = 'foo' - - def set_banner(self, banner): - self.banner = banner - - def __call__(self): - self.called = True - -class DummyIPShellFactory(object): - def __call__(self, **kw): - self.kw = kw - self.shell = DummyIPShell() - return self.shell - class DummyLoadApp: def __init__(self, app): self.app = app @@ -1018,28 +98,6 @@ class DummyApp: def __init__(self): self.registry = dummy_registry -class DummyMapper(object): - def __init__(self, *routes): - self.routes = routes - - def get_routes(self): - return self.routes - -class DummyRoute(object): - def __init__(self, name, pattern, factory=None, - matchdict=None, predicate=None): - self.name = name - self.path = pattern - self.pattern = pattern - self.factory = factory - self.matchdict = matchdict - self.predicates = [] - if predicate is not None: - self.predicates = [predicate] - - def match(self, route): - return self.matchdict - class DummyRequest: application_url = 'http://example.com:5432' script_name = '' @@ -1047,72 +105,3 @@ class DummyRequest: self.environ = environ self.matchdict = {} -class DummyView(object): - def __init__(self, **attrs): - self.__request_attrs__ = attrs - -from zope.interface import implementer -from pyramid.interfaces import IMultiView -@implementer(IMultiView) -class DummyMultiView(object): - - def __init__(self, *views, **attrs): - self.views = [(None, view, None) for view in views] - self.__request_attrs__ = attrs - -class DummyConfigParser(object): - def __init__(self, result): - self.result = result - - def read(self, filename): - self.filename = filename - - def items(self, section): - self.section = section - if self.result is None: - from ConfigParser import NoSectionError - raise NoSectionError(section) - return self.result - -class DummyConfigParserFactory(object): - items = None - - def __call__(self): - self.parser = DummyConfigParser(self.items) - return self.parser - -class DummyCloser(object): - def __call__(self): - self.called = True - -class DummyBootstrap(object): - def __init__(self, app=None, registry=None, request=None, root=None, - root_factory=None, closer=None): - self.app = app or DummyApp() - if registry is None: - registry = DummyRegistry() - self.registry = registry - if request is None: - request = DummyRequest({}) - self.request = request - if root is None: - root = Dummy() - self.root = root - if root_factory is None: - root_factory = Dummy() - self.root_factory = root_factory - if closer is None: - closer = DummyCloser() - self.closer = closer - - def __call__(self, *a, **kw): - self.a = a - self.kw = kw - return { - 'app': self.app, - 'registry': self.registry, - 'request': self.request, - 'root': self.root, - 'root_factory': self.root_factory, - 'closer': self.closer, - } diff --git a/pyramid/tests/test_scripts.py b/pyramid/tests/test_scripts.py new file mode 100644 index 000000000..82f4b2c52 --- /dev/null +++ b/pyramid/tests/test_scripts.py @@ -0,0 +1,1037 @@ +import unittest + +class TestPShellCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.pshell import PShellCommand + return PShellCommand + + def _makeOne(self, patch_bootstrap=True, patch_config=True, + patch_args=True, patch_options=True): + cmd = self._getTargetClass()([]) + if patch_bootstrap: + self.bootstrap = DummyBootstrap() + cmd.bootstrap = (self.bootstrap,) + if patch_config: + self.config_factory = DummyConfigParserFactory() + cmd.ConfigParser = self.config_factory + if patch_args: + self.args = ('/foo/bar/myapp.ini#myapp',) + cmd.args = self.args + if patch_options: + class Options(object): pass + self.options = Options() + self.options.disable_ipython = True + self.options.setup = None + cmd.options = self.options + return cmd + + def test_make_default_shell(self): + command = self._makeOne() + interact = DummyInteractor() + shell = command.make_default_shell(interact) + shell({'foo': 'bar'}, 'a help message') + self.assertEqual(interact.local, {'foo': 'bar'}) + self.assertTrue('a help message' in interact.banner) + + def test_make_ipython_v0_11_shell(self): + command = self._makeOne() + ipshell_factory = DummyIPShellFactory() + shell = command.make_ipython_v0_11_shell(ipshell_factory) + shell({'foo': 'bar'}, 'a help message') + self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) + self.assertTrue('a help message' in ipshell_factory.kw['banner2']) + self.assertTrue(ipshell_factory.shell.called) + + def test_make_ipython_v0_10_shell(self): + command = self._makeOne() + ipshell_factory = DummyIPShellFactory() + shell = command.make_ipython_v0_10_shell(ipshell_factory) + shell({'foo': 'bar'}, 'a help message') + self.assertEqual(ipshell_factory.kw['argv'], []) + self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) + self.assertTrue('a help message' in ipshell_factory.shell.banner) + self.assertTrue(ipshell_factory.shell.called) + + def test_command_loads_default_shell(self): + command = self._makeOne() + shell = DummyShell() + command.make_ipython_v0_11_shell = lambda: None + command.make_ipython_v0_10_shell = lambda: None + command.make_default_shell = lambda: shell + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_default_shell_with_ipython_disabled(self): + command = self._makeOne() + shell = DummyShell() + bad_shell = DummyShell() + command.make_ipython_v0_11_shell = lambda: bad_shell + command.make_ipython_v0_10_shell = lambda: bad_shell + command.make_default_shell = lambda: shell + command.options.disable_ipython = True + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertEqual(bad_shell.env, {}) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_ipython_v0_11(self): + command = self._makeOne() + shell = DummyShell() + command.make_ipython_v0_11_shell = lambda: shell + command.make_ipython_v0_10_shell = lambda: None + command.make_default_shell = lambda: None + command.options.disable_ipython = False + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_ipython_v0_10(self): + command = self._makeOne() + shell = DummyShell() + command.make_ipython_v0_11_shell = lambda: None + command.make_ipython_v0_10_shell = lambda: shell + command.make_default_shell = lambda: None + command.options.disable_ipython = False + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_custom_items(self): + command = self._makeOne() + model = Dummy() + self.config_factory.items = [('m', model)] + shell = DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'm':model, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_setup(self): + command = self._makeOne() + def setup(env): + env['a'] = 1 + env['root'] = 'root override' + self.config_factory.items = [('setup', setup)] + shell = DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':'root override', + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'a':1, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_check_variable_override_order(self): + command = self._makeOne() + model = Dummy() + def setup(env): + env['a'] = 1 + env['m'] = 'model override' + env['root'] = 'root override' + self.config_factory.items = [('setup', setup), ('m', model)] + shell = DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':'root override', + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'a':1, 'm':model, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_setup_from_options(self): + command = self._makeOne() + def setup(env): + env['a'] = 1 + env['root'] = 'root override' + model = Dummy() + self.config_factory.items = [('setup', 'abc'), + ('m', model)] + command.options.setup = setup + shell = DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':'root override', + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'a':1, 'm':model, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_custom_section_override(self): + command = self._makeOne() + dummy = Dummy() + self.config_factory.items = [('app', dummy), ('root', dummy), + ('registry', dummy), ('request', dummy)] + shell = DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':dummy, 'root':dummy, 'registry':dummy, 'request':dummy, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + +class TestPRoutesCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.proutes import PRoutesCommand + return PRoutesCommand + + def _makeOne(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (DummyBootstrap(),) + cmd.args = ('/foo/bar/myapp.ini#myapp',) + return cmd + + def test_no_routes(self): + command = self._makeOne() + mapper = DummyMapper() + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L, []) + + def test_no_mapper(self): + command = self._makeOne() + command._get_mapper = lambda *arg:None + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L, []) + + def test_single_route_no_route_registered(self): + command = self._makeOne() + route = DummyRoute('a', '/a') + mapper = DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split(), ['a', '/a', '']) + + def test_single_route_no_views_registered(self): + from zope.interface import Interface + from pyramid.registry import Registry + from pyramid.interfaces import IRouteRequest + registry = Registry() + def view():pass + class IMyRoute(Interface): + pass + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = DummyRoute('a', '/a') + mapper = DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = (DummyBootstrap(registry=registry),) + result = command.run() + self.assertEqual(result, None) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split()[:3], ['a', '/a', 'None']) + + def test_single_route_one_view_registered(self): + from zope.interface import Interface + from pyramid.registry import Registry + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + registry = Registry() + def view():pass + class IMyRoute(Interface): + pass + registry.registerAdapter(view, + (IViewClassifier, IMyRoute, Interface), + IView, '') + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = DummyRoute('a', '/a') + mapper = DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = (DummyBootstrap(registry=registry),) + result = command.run() + self.assertEqual(result, None) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split()[:4], ['a', '/a', '']) + + def test__get_mapper(self): + from pyramid.registry import Registry + from pyramid.urldispatch import RoutesMapper + command = self._makeOne() + registry = Registry() + result = command._get_mapper(registry) + self.assertEqual(result.__class__, RoutesMapper) + +class TestPViewsCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.pviews import PViewsCommand + return PViewsCommand + + def _makeOne(self, registry=None): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (DummyBootstrap(registry=registry),) + cmd.args = ('/foo/bar/myapp.ini#myapp',) + return cmd + + def _register_mapper(self, registry, routes): + from pyramid.interfaces import IRoutesMapper + mapper = DummyMapper(*routes) + registry.registerUtility(mapper, IRoutesMapper) + + def test__find_view_no_match(self): + from pyramid.registry import Registry + registry = Registry() + self._register_mapper(registry, []) + command = self._makeOne(registry) + result = command._find_view('/a', registry) + self.assertEqual(result, None) + + def test__find_view_no_match_multiview_registered(self): + from zope.interface import implementer + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + @implementer(IMultiView) + class View1(object): + pass + request = DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter(View1(), + (IViewClassifier, IRequest, root_iface), + IMultiView) + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + result = command._find_view('/x', registry) + self.assertEqual(result, None) + + def test__find_view_traversal(self): + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1(): pass + request = DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter(view1, + (IViewClassifier, IRequest, root_iface), + IView, name='a') + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertEqual(result, view1) + + def test__find_view_traversal_multiview(self): + from zope.interface import implementer + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + @implementer(IMultiView) + class View1(object): + pass + request = DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + view = View1() + registry.registerAdapter(view, + (IViewClassifier, IRequest, root_iface), + IMultiView, name='a') + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertEqual(result, view) + + def test__find_view_route_no_multiview(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.registry import Registry + registry = Registry() + def view():pass + class IMyRoot(Interface): + pass + class IMyRoute(Interface): + pass + registry.registerAdapter(view, + (IViewClassifier, IMyRoute, IMyRoot), + IView, '') + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + routes = [DummyRoute('a', '/a', factory=Factory, matchdict={}), + DummyRoute('b', '/b', factory=Factory)] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertEqual(result, view) + + def test__find_view_route_multiview_no_view_registered(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1():pass + def view2():pass + class IMyRoot(Interface): + pass + class IMyRoute1(Interface): + pass + class IMyRoute2(Interface): + pass + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + registry.registerUtility(Factory, IRootFactory) + routes = [DummyRoute('a', '/a', matchdict={}), + DummyRoute('b', '/a', matchdict={})] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertTrue(IMultiView.providedBy(result)) + + def test__find_view_route_multiview(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1():pass + def view2():pass + class IMyRoot(Interface): + pass + class IMyRoute1(Interface): + pass + class IMyRoute2(Interface): + pass + registry.registerAdapter(view1, + (IViewClassifier, IMyRoute1, IMyRoot), + IView, '') + registry.registerAdapter(view2, + (IViewClassifier, IMyRoute2, IMyRoot), + IView, '') + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + registry.registerUtility(Factory, IRootFactory) + routes = [DummyRoute('a', '/a', matchdict={}), + DummyRoute('b', '/a', matchdict={})] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertTrue(IMultiView.providedBy(result)) + self.assertEqual(len(result.views), 2) + self.assertTrue((None, view1, None) in result.views) + self.assertTrue((None, view2, None) in result.views) + + def test__find_multi_routes_all_match(self): + command = self._makeOne() + def factory(request): pass + routes = [DummyRoute('a', '/a', factory=factory, matchdict={}), + DummyRoute('b', '/a', factory=factory, matchdict={})] + mapper = DummyMapper(*routes) + request = DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, [{'match':{}, 'route':routes[0]}, + {'match':{}, 'route':routes[1]}]) + + def test__find_multi_routes_some_match(self): + command = self._makeOne() + def factory(request): pass + routes = [DummyRoute('a', '/a', factory=factory), + DummyRoute('b', '/a', factory=factory, matchdict={})] + mapper = DummyMapper(*routes) + request = DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, [{'match':{}, 'route':routes[1]}]) + + def test__find_multi_routes_none_match(self): + command = self._makeOne() + def factory(request): pass + routes = [DummyRoute('a', '/a', factory=factory), + DummyRoute('b', '/a', factory=factory)] + mapper = DummyMapper(*routes) + request = DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, []) + + def test_views_command_not_found(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + command._find_view = lambda arg1, arg2: None + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' Not found.') + + def test_views_command_not_found_url_starts_without_slash(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + command._find_view = lambda arg1, arg2: None + command.args = ('/foo/bar/myapp.ini#myapp', 'a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' Not found.') + + def test_views_command_single_view_traversal(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = DummyView(context='context', view_name='a') + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyView') + + def test_views_command_single_view_function_traversal(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def view(): pass + view.__request_attrs__ = {'context': 'context', 'view_name': 'a'} + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.view') + + def test_views_command_single_view_traversal_with_permission(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = DummyView(context='context', view_name='a') + view.__permission__ = 'test' + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyView') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_single_view_traversal_with_predicates(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + view = DummyView(context='context', view_name='a') + view.__predicates__ = [predicate] + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyView') + self.assertEqual(L[9], ' view predicates (predicate = x)') + + def test_views_command_single_view_route(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + route = DummyRoute('a', '/a', matchdict={}) + view = DummyView(context='context', view_name='a', + matched_route=route, subpath='') + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[15], ' pyramid.tests.test_scripts.DummyView') + + def test_views_command_multi_view_nested(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view1 = DummyView(context='context', view_name='a1') + view1.__name__ = 'view1' + view1.__view_attr__ = 'call' + multiview1 = DummyMultiView(view1, context='context', view_name='a1') + multiview2 = DummyMultiView(multiview1, context='context', + view_name='a') + command._find_view = lambda arg1, arg2: multiview2 + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyMultiView') + self.assertEqual(L[12], ' pyramid.tests.test_scripts.view1.call') + + def test_views_command_single_view_route_with_route_predicates(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + route = DummyRoute('a', '/a', matchdict={}, predicate=predicate) + view = DummyView(context='context', view_name='a', + matched_route=route, subpath='') + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[12], ' route predicates (predicate = x)') + self.assertEqual(L[16], ' pyramid.tests.test_scripts.DummyView') + + def test_views_command_multiview(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + multiview = DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.view.call') + + def test_views_command_multiview_with_permission(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__permission__ = 'test' + multiview = DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.view.call') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_multiview_with_predicates(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + view = DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__predicates__ = [predicate] + multiview = DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], ' pyramid.tests.test_scripts.view.call') + self.assertEqual(L[9], ' view predicates (predicate = x)') + +class TestPTweensCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.ptweens import PTweensCommand + return PTweensCommand + + def _makeOne(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (DummyBootstrap(),) + cmd.args = ('/foo/bar/myapp.ini#myapp',) + return cmd + + def test_command_no_tweens(self): + command = self._makeOne() + command._get_tweens = lambda *arg: None + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L, []) + + def test_command_implicit_tweens_only(self): + command = self._makeOne() + tweens = DummyTweens([('name', 'item')], None) + command._get_tweens = lambda *arg: tweens + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual( + L[0], + '"pyramid.tweens" config value NOT set (implicitly ordered tweens ' + 'used)') + + def test_command_implicit_and_explicit_tweens(self): + command = self._makeOne() + tweens = DummyTweens([('name', 'item')], [('name2', 'item2')]) + command._get_tweens = lambda *arg: tweens + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual( + L[0], + '"pyramid.tweens" config value set (explicitly ordered tweens used)') + + def test__get_tweens(self): + command = self._makeOne() + registry = DummyRegistry() + self.assertEqual(command._get_tweens(registry), None) + +class DummyTweens(object): + def __init__(self, implicit, explicit): + self._implicit = implicit + self.explicit = explicit + self.name_to_alias = {} + def implicit(self): + return self._implicit + +class Dummy: + pass + +dummy_root = Dummy() + +class DummyRegistry(object): + settings = {} + def queryUtility(self, iface, default=None, name=''): + return default + +dummy_registry = DummyRegistry() + +class DummyShell(object): + env = {} + help = '' + + def __call__(self, env, help): + self.env = env + self.help = help + +class DummyInteractor: + def __call__(self, banner, local): + self.banner = banner + self.local = local + +class DummyIPShell(object): + IP = Dummy() + IP.BANNER = 'foo' + + def set_banner(self, banner): + self.banner = banner + + def __call__(self): + self.called = True + +class DummyIPShellFactory(object): + def __call__(self, **kw): + self.kw = kw + self.shell = DummyIPShell() + return self.shell + +class DummyLoadApp: + def __init__(self, app): + self.app = app + + def __call__(self, config_name, name=None, relative_to=None): + self.config_name = config_name + self.section_name = name + self.relative_to = relative_to + return self.app + +class DummyApp: + def __init__(self): + self.registry = dummy_registry + +class DummyMapper(object): + def __init__(self, *routes): + self.routes = routes + + def get_routes(self): + return self.routes + +class DummyRoute(object): + def __init__(self, name, pattern, factory=None, + matchdict=None, predicate=None): + self.name = name + self.path = pattern + self.pattern = pattern + self.factory = factory + self.matchdict = matchdict + self.predicates = [] + if predicate is not None: + self.predicates = [predicate] + + def match(self, route): + return self.matchdict + +class DummyRequest: + application_url = 'http://example.com:5432' + script_name = '' + def __init__(self, environ): + self.environ = environ + self.matchdict = {} + +class DummyView(object): + def __init__(self, **attrs): + self.__request_attrs__ = attrs + +from zope.interface import implementer +from pyramid.interfaces import IMultiView +@implementer(IMultiView) +class DummyMultiView(object): + + def __init__(self, *views, **attrs): + self.views = [(None, view, None) for view in views] + self.__request_attrs__ = attrs + +class DummyConfigParser(object): + def __init__(self, result): + self.result = result + + def read(self, filename): + self.filename = filename + + def items(self, section): + self.section = section + if self.result is None: + from ConfigParser import NoSectionError + raise NoSectionError(section) + return self.result + +class DummyConfigParserFactory(object): + items = None + + def __call__(self): + self.parser = DummyConfigParser(self.items) + return self.parser + +class DummyCloser(object): + def __call__(self): + self.called = True + +class DummyBootstrap(object): + def __init__(self, app=None, registry=None, request=None, root=None, + root_factory=None, closer=None): + self.app = app or DummyApp() + if registry is None: + registry = DummyRegistry() + self.registry = registry + if request is None: + request = DummyRequest({}) + self.request = request + if root is None: + root = Dummy() + self.root = root + if root_factory is None: + root_factory = Dummy() + self.root_factory = root_factory + if closer is None: + closer = DummyCloser() + self.closer = closer + + def __call__(self, *a, **kw): + self.a = a + self.kw = kw + return { + 'app': self.app, + 'registry': self.registry, + 'request': self.request, + 'root': self.root, + 'root_factory': self.root_factory, + 'closer': self.closer, + } diff --git a/setup.py b/setup.py index 1602f9aa7..559467fe8 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ install_requires=[ 'zope.deprecation >= 3.5.0', # py3 compat 'venusian >= 1.0a1', # ``onerror`` 'translationstring >= 0.4', # py3 compat - 'glue', + 'PasteDeploy >= 1.5.0', # py3 compat ] tests_require = install_requires + [ @@ -89,15 +89,14 @@ setup(name='pyramid', zodb=pyramid.scaffolds:ZODBProjectTemplate routesalchemy=pyramid.scaffolds:RoutesAlchemyProjectTemplate alchemy=pyramid.scaffolds:AlchemyProjectTemplate - [glue.command] - pshell=pyramid.paster:PShellCommand - proutes=pyramid.paster:PRoutesCommand - pviews=pyramid.paster:PViewsCommand - ptweens=pyramid.paster:PTweensCommand [console_scripts] bfg2pyramid = pyramid.fixers.fix_bfg_imports:main pcreate = pyramid.scripts.pcreate:main pserve = pyramid.scripts.pserve:main + pshell = pyramid.scripts.pshell:main + proutes = pyramid.scripts.proutes:main + pviews = pyramid.scripts.pviews:main + ptweens = pyramid.scripts.ptweens:main [paste.server_runner] wsgiref = pyramid.scripts.pserve:wsgiref_server_runner """ -- cgit v1.2.3 From 338cb9482bd6dae479eded3a537f1edb7f6b9791 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 09:10:44 -0400 Subject: coverage --- pyramid/compat.py | 11 +++-------- pyramid/testing.py | 4 ++-- pyramid/tests/test_paster.py | 2 -- pyramid/tests/test_scripts.py | 10 ---------- pyramid/tests/test_util.py | 2 +- 5 files changed, 6 insertions(+), 23 deletions(-) diff --git a/pyramid/compat.py b/pyramid/compat.py index 552a90b4b..f464a218c 100644 --- a/pyramid/compat.py +++ b/pyramid/compat.py @@ -212,8 +212,7 @@ else: try: # pragma: no cover import configparser except ImportError: # pragma: no cover - import ConfigParser - configparser = ConfigParser + import ConfigParser as configparser try: from Cookie import SimpleCookie @@ -225,12 +224,8 @@ if PY3: # pragma: no cover else: from cgi import escape -try: +try: # pragma: no cover input_ = raw_input -except NameError: +except NameError: # pragma: no cover input_ = input -try: # pragma: no cover - import configparser -except ImportError: - import ConfigParser as configparser diff --git a/pyramid/testing.py b/pyramid/testing.py index b24a6cac7..148575782 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -807,8 +807,8 @@ def setUp(registry=None, request=None, hook_zca=True, autocommit=True, global have_zca try: have_zca and hook_zca and config.hook_zca() - except ImportError: - # pragma: no cover (dont choke on not being able to import z.component) + except ImportError: # pragma: no cover + # (dont choke on not being able to import z.component) have_zca = False config.begin(request=request) return config diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py index 0182ea08b..bce5399ce 100644 --- a/pyramid/tests/test_paster.py +++ b/pyramid/tests/test_paster.py @@ -79,8 +79,6 @@ class Dummy: class DummyRegistry(object): settings = {} - def queryUtility(self, iface, default=None, name=''): - return default dummy_registry = DummyRegistry() diff --git a/pyramid/tests/test_scripts.py b/pyramid/tests/test_scripts.py index 82f4b2c52..c70b91908 100644 --- a/pyramid/tests/test_scripts.py +++ b/pyramid/tests/test_scripts.py @@ -923,16 +923,6 @@ class DummyIPShellFactory(object): self.shell = DummyIPShell() return self.shell -class DummyLoadApp: - def __init__(self, app): - self.app = app - - def __call__(self, config_name, name=None, relative_to=None): - self.config_name = config_name - self.section_name = name - self.relative_to = relative_to - return self.app - class DummyApp: def __init__(self): self.registry = dummy_registry diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index 7c870bc45..57bcd08d7 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -17,7 +17,7 @@ class TestDottedNameResolver(unittest.TestCase): def test_zope_dottedname_style_resolve_builtin(self): typ = self._makeOne() - if PY3: + if PY3: # pragma: no cover result = typ._zope_dottedname_style('builtins.str') else: result = typ._zope_dottedname_style('__builtin__.str') -- cgit v1.2.3 From 9344609a3a8c4d2e72caf8d7849d6fd2f372c43c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 09:26:36 -0400 Subject: make pass on py3 --- pyramid/scripts/pshell.py | 2 +- pyramid/tests/test_scripts.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 85c7e16ea..12215bedb 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -113,7 +113,7 @@ class PShellCommand(object): setup(env) # remove any objects from default help that were overidden - for k, v in env.iteritems(): + for k, v in env.items(): if k not in orig_env or env[k] != orig_env[k]: env_help[k] = v diff --git a/pyramid/tests/test_scripts.py b/pyramid/tests/test_scripts.py index c70b91908..c76839f80 100644 --- a/pyramid/tests/test_scripts.py +++ b/pyramid/tests/test_scripts.py @@ -979,8 +979,8 @@ class DummyConfigParser(object): def items(self, section): self.section = section if self.result is None: - from ConfigParser import NoSectionError - raise NoSectionError(section) + from pyramid.compat import configparser + raise configparser.NoSectionError(section) return self.result class DummyConfigParserFactory(object): -- cgit v1.2.3 From 79788d223db1312465c1437f2ec78a5e1db38167 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 09:28:00 -0400 Subject: these now run on py3 --- pyramid/tests/test_scaffolds.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyramid/tests/test_scaffolds.py b/pyramid/tests/test_scaffolds.py index 265e20c3b..ed2c5a993 100644 --- a/pyramid/tests/test_scaffolds.py +++ b/pyramid/tests/test_scaffolds.py @@ -1,7 +1,5 @@ import unittest -from pyramid.testing import skip_on -@skip_on('py3') class TestPyramidTemplate(unittest.TestCase): def _getTargetClass(self): from pyramid.scaffolds import PyramidTemplate -- cgit v1.2.3 From 8203994e611512614e4c9658d03da01cac82c73f Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 09:29:10 -0400 Subject: squash warning under py3 --- pyramid/tests/test_paster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py index bce5399ce..d23c156c2 100644 --- a/pyramid/tests/test_paster.py +++ b/pyramid/tests/test_paster.py @@ -72,7 +72,7 @@ class TestBootstrap(unittest.TestCase): result = self._callFUT('/foo/bar/myapp.ini', request) self.assertEqual(result['app'], self.app) self.assertEqual(result['root'], self.root) - self.assert_('closer' in result) + self.assertTrue('closer' in result) class Dummy: pass -- cgit v1.2.3 From eb0432c504f4f4d45cc714e94709ee2976bb7b8f Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 09:30:56 -0400 Subject: get license headers correct --- pyramid/scripts/pcreate.py | 4 ++++ pyramid/scripts/pserve.py | 11 +++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py index bece38e68..39aca6b70 100644 --- a/pyramid/scripts/pcreate.py +++ b/pyramid/scripts/pcreate.py @@ -1,3 +1,7 @@ +# (c) 2005 Ian Bicking and contributors; written for Paste +# (http://pythonpaste.org) Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license.php + import optparse import os import pkg_resources diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index f3414d2cf..561085119 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -1,9 +1,12 @@ # (c) 2005 Ian Bicking and contributors; written for Paste # (http://pythonpaste.org) Licensed under the MIT license: -# http://www.opensource.org/licenses/mit-license.php @@: This should be moved -# to paste.deploy For discussion of daemonizing: -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 Code taken -# also from QP: http://www.mems-exchange.org/software/qp/ From lib/site.py +# http://www.opensource.org/licenses/mit-license.php +# +# For discussion of daemonizing: +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731 +# +# Code taken also from QP: http://www.mems-exchange.org/software/qp/ From +# lib/site.py import re import optparse -- cgit v1.2.3 From 262cea6db9ff01068d0a40726959849006d3b27c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 22:05:41 -0400 Subject: break script tests into separate files --- pyramid/tests/test_scripts.py | 1027 ---------------------------- pyramid/tests/test_scripts/__init__.py | 1 + pyramid/tests/test_scripts/dummy.py | 151 ++++ pyramid/tests/test_scripts/test_proutes.py | 129 ++++ pyramid/tests/test_scripts/test_pshell.py | 249 +++++++ pyramid/tests/test_scripts/test_ptweens.py | 53 ++ pyramid/tests/test_scripts/test_pviews.py | 467 +++++++++++++ 7 files changed, 1050 insertions(+), 1027 deletions(-) delete mode 100644 pyramid/tests/test_scripts.py create mode 100644 pyramid/tests/test_scripts/__init__.py create mode 100644 pyramid/tests/test_scripts/dummy.py create mode 100644 pyramid/tests/test_scripts/test_proutes.py create mode 100644 pyramid/tests/test_scripts/test_pshell.py create mode 100644 pyramid/tests/test_scripts/test_ptweens.py create mode 100644 pyramid/tests/test_scripts/test_pviews.py diff --git a/pyramid/tests/test_scripts.py b/pyramid/tests/test_scripts.py deleted file mode 100644 index c76839f80..000000000 --- a/pyramid/tests/test_scripts.py +++ /dev/null @@ -1,1027 +0,0 @@ -import unittest - -class TestPShellCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.scripts.pshell import PShellCommand - return PShellCommand - - def _makeOne(self, patch_bootstrap=True, patch_config=True, - patch_args=True, patch_options=True): - cmd = self._getTargetClass()([]) - if patch_bootstrap: - self.bootstrap = DummyBootstrap() - cmd.bootstrap = (self.bootstrap,) - if patch_config: - self.config_factory = DummyConfigParserFactory() - cmd.ConfigParser = self.config_factory - if patch_args: - self.args = ('/foo/bar/myapp.ini#myapp',) - cmd.args = self.args - if patch_options: - class Options(object): pass - self.options = Options() - self.options.disable_ipython = True - self.options.setup = None - cmd.options = self.options - return cmd - - def test_make_default_shell(self): - command = self._makeOne() - interact = DummyInteractor() - shell = command.make_default_shell(interact) - shell({'foo': 'bar'}, 'a help message') - self.assertEqual(interact.local, {'foo': 'bar'}) - self.assertTrue('a help message' in interact.banner) - - def test_make_ipython_v0_11_shell(self): - command = self._makeOne() - ipshell_factory = DummyIPShellFactory() - shell = command.make_ipython_v0_11_shell(ipshell_factory) - shell({'foo': 'bar'}, 'a help message') - self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) - self.assertTrue('a help message' in ipshell_factory.kw['banner2']) - self.assertTrue(ipshell_factory.shell.called) - - def test_make_ipython_v0_10_shell(self): - command = self._makeOne() - ipshell_factory = DummyIPShellFactory() - shell = command.make_ipython_v0_10_shell(ipshell_factory) - shell({'foo': 'bar'}, 'a help message') - self.assertEqual(ipshell_factory.kw['argv'], []) - self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) - self.assertTrue('a help message' in ipshell_factory.shell.banner) - self.assertTrue(ipshell_factory.shell.called) - - def test_command_loads_default_shell(self): - command = self._makeOne() - shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: None - command.make_ipython_v0_10_shell = lambda: None - command.make_default_shell = lambda: shell - command.run() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_default_shell_with_ipython_disabled(self): - command = self._makeOne() - shell = DummyShell() - bad_shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: bad_shell - command.make_ipython_v0_10_shell = lambda: bad_shell - command.make_default_shell = lambda: shell - command.options.disable_ipython = True - command.run() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertEqual(bad_shell.env, {}) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_ipython_v0_11(self): - command = self._makeOne() - shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: shell - command.make_ipython_v0_10_shell = lambda: None - command.make_default_shell = lambda: None - command.options.disable_ipython = False - command.run() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_ipython_v0_10(self): - command = self._makeOne() - shell = DummyShell() - command.make_ipython_v0_11_shell = lambda: None - command.make_ipython_v0_10_shell = lambda: shell - command.make_default_shell = lambda: None - command.options.disable_ipython = False - command.run() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_custom_items(self): - command = self._makeOne() - model = Dummy() - self.config_factory.items = [('m', model)] - shell = DummyShell() - command.run(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'm':model, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_setup(self): - command = self._makeOne() - def setup(env): - env['a'] = 1 - env['root'] = 'root override' - self.config_factory.items = [('setup', setup)] - shell = DummyShell() - command.run(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':'root override', - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'a':1, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_check_variable_override_order(self): - command = self._makeOne() - model = Dummy() - def setup(env): - env['a'] = 1 - env['m'] = 'model override' - env['root'] = 'root override' - self.config_factory.items = [('setup', setup), ('m', model)] - shell = DummyShell() - command.run(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':'root override', - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'a':1, 'm':model, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_loads_setup_from_options(self): - command = self._makeOne() - def setup(env): - env['a'] = 1 - env['root'] = 'root override' - model = Dummy() - self.config_factory.items = [('setup', 'abc'), - ('m', model)] - command.options.setup = setup - shell = DummyShell() - command.run(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':self.bootstrap.app, 'root':'root override', - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - 'a':1, 'm':model, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - - def test_command_custom_section_override(self): - command = self._makeOne() - dummy = Dummy() - self.config_factory.items = [('app', dummy), ('root', dummy), - ('registry', dummy), ('request', dummy)] - shell = DummyShell() - command.run(shell) - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.env, { - 'app':dummy, 'root':dummy, 'registry':dummy, 'request':dummy, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.help) - -class TestPRoutesCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.scripts.proutes import PRoutesCommand - return PRoutesCommand - - def _makeOne(self): - cmd = self._getTargetClass()([]) - cmd.bootstrap = (DummyBootstrap(),) - cmd.args = ('/foo/bar/myapp.ini#myapp',) - return cmd - - def test_no_routes(self): - command = self._makeOne() - mapper = DummyMapper() - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L, []) - - def test_no_mapper(self): - command = self._makeOne() - command._get_mapper = lambda *arg:None - L = [] - command.out = L.append - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L, []) - - def test_single_route_no_route_registered(self): - command = self._makeOne() - route = DummyRoute('a', '/a') - mapper = DummyMapper(route) - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - result = command.run() - self.assertEqual(result, None) - self.assertEqual(len(L), 3) - self.assertEqual(L[-1].split(), ['a', '/a', '']) - - def test_single_route_no_views_registered(self): - from zope.interface import Interface - from pyramid.registry import Registry - from pyramid.interfaces import IRouteRequest - registry = Registry() - def view():pass - class IMyRoute(Interface): - pass - registry.registerUtility(IMyRoute, IRouteRequest, name='a') - command = self._makeOne() - route = DummyRoute('a', '/a') - mapper = DummyMapper(route) - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - command.bootstrap = (DummyBootstrap(registry=registry),) - result = command.run() - self.assertEqual(result, None) - self.assertEqual(len(L), 3) - self.assertEqual(L[-1].split()[:3], ['a', '/a', 'None']) - - def test_single_route_one_view_registered(self): - from zope.interface import Interface - from pyramid.registry import Registry - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - registry = Registry() - def view():pass - class IMyRoute(Interface): - pass - registry.registerAdapter(view, - (IViewClassifier, IMyRoute, Interface), - IView, '') - registry.registerUtility(IMyRoute, IRouteRequest, name='a') - command = self._makeOne() - route = DummyRoute('a', '/a') - mapper = DummyMapper(route) - command._get_mapper = lambda *arg: mapper - L = [] - command.out = L.append - command.bootstrap = (DummyBootstrap(registry=registry),) - result = command.run() - self.assertEqual(result, None) - self.assertEqual(len(L), 3) - self.assertEqual(L[-1].split()[:4], ['a', '/a', '']) - - def test__get_mapper(self): - from pyramid.registry import Registry - from pyramid.urldispatch import RoutesMapper - command = self._makeOne() - registry = Registry() - result = command._get_mapper(registry) - self.assertEqual(result.__class__, RoutesMapper) - -class TestPViewsCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.scripts.pviews import PViewsCommand - return PViewsCommand - - def _makeOne(self, registry=None): - cmd = self._getTargetClass()([]) - cmd.bootstrap = (DummyBootstrap(registry=registry),) - cmd.args = ('/foo/bar/myapp.ini#myapp',) - return cmd - - def _register_mapper(self, registry, routes): - from pyramid.interfaces import IRoutesMapper - mapper = DummyMapper(*routes) - registry.registerUtility(mapper, IRoutesMapper) - - def test__find_view_no_match(self): - from pyramid.registry import Registry - registry = Registry() - self._register_mapper(registry, []) - command = self._makeOne(registry) - result = command._find_view('/a', registry) - self.assertEqual(result, None) - - def test__find_view_no_match_multiview_registered(self): - from zope.interface import implementer - from zope.interface import providedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - from pyramid.traversal import DefaultRootFactory - from pyramid.registry import Registry - registry = Registry() - @implementer(IMultiView) - class View1(object): - pass - request = DummyRequest({'PATH_INFO':'/a'}) - root = DefaultRootFactory(request) - root_iface = providedBy(root) - registry.registerAdapter(View1(), - (IViewClassifier, IRequest, root_iface), - IMultiView) - self._register_mapper(registry, []) - command = self._makeOne(registry=registry) - result = command._find_view('/x', registry) - self.assertEqual(result, None) - - def test__find_view_traversal(self): - from zope.interface import providedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - from pyramid.traversal import DefaultRootFactory - from pyramid.registry import Registry - registry = Registry() - def view1(): pass - request = DummyRequest({'PATH_INFO':'/a'}) - root = DefaultRootFactory(request) - root_iface = providedBy(root) - registry.registerAdapter(view1, - (IViewClassifier, IRequest, root_iface), - IView, name='a') - self._register_mapper(registry, []) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertEqual(result, view1) - - def test__find_view_traversal_multiview(self): - from zope.interface import implementer - from zope.interface import providedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - from pyramid.traversal import DefaultRootFactory - from pyramid.registry import Registry - registry = Registry() - @implementer(IMultiView) - class View1(object): - pass - request = DummyRequest({'PATH_INFO':'/a'}) - root = DefaultRootFactory(request) - root_iface = providedBy(root) - view = View1() - registry.registerAdapter(view, - (IViewClassifier, IRequest, root_iface), - IMultiView, name='a') - self._register_mapper(registry, []) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertEqual(result, view) - - def test__find_view_route_no_multiview(self): - from zope.interface import Interface - from zope.interface import implementer - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - from pyramid.registry import Registry - registry = Registry() - def view():pass - class IMyRoot(Interface): - pass - class IMyRoute(Interface): - pass - registry.registerAdapter(view, - (IViewClassifier, IMyRoute, IMyRoot), - IView, '') - registry.registerUtility(IMyRoute, IRouteRequest, name='a') - @implementer(IMyRoot) - class Factory(object): - def __init__(self, request): - pass - routes = [DummyRoute('a', '/a', factory=Factory, matchdict={}), - DummyRoute('b', '/b', factory=Factory)] - self._register_mapper(registry, routes) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertEqual(result, view) - - def test__find_view_route_multiview_no_view_registered(self): - from zope.interface import Interface - from zope.interface import implementer - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IRootFactory - from pyramid.registry import Registry - registry = Registry() - def view1():pass - def view2():pass - class IMyRoot(Interface): - pass - class IMyRoute1(Interface): - pass - class IMyRoute2(Interface): - pass - registry.registerUtility(IMyRoute1, IRouteRequest, name='a') - registry.registerUtility(IMyRoute2, IRouteRequest, name='b') - @implementer(IMyRoot) - class Factory(object): - def __init__(self, request): - pass - registry.registerUtility(Factory, IRootFactory) - routes = [DummyRoute('a', '/a', matchdict={}), - DummyRoute('b', '/a', matchdict={})] - self._register_mapper(registry, routes) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertTrue(IMultiView.providedBy(result)) - - def test__find_view_route_multiview(self): - from zope.interface import Interface - from zope.interface import implementer - from pyramid.interfaces import IRouteRequest - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IRootFactory - from pyramid.registry import Registry - registry = Registry() - def view1():pass - def view2():pass - class IMyRoot(Interface): - pass - class IMyRoute1(Interface): - pass - class IMyRoute2(Interface): - pass - registry.registerAdapter(view1, - (IViewClassifier, IMyRoute1, IMyRoot), - IView, '') - registry.registerAdapter(view2, - (IViewClassifier, IMyRoute2, IMyRoot), - IView, '') - registry.registerUtility(IMyRoute1, IRouteRequest, name='a') - registry.registerUtility(IMyRoute2, IRouteRequest, name='b') - @implementer(IMyRoot) - class Factory(object): - def __init__(self, request): - pass - registry.registerUtility(Factory, IRootFactory) - routes = [DummyRoute('a', '/a', matchdict={}), - DummyRoute('b', '/a', matchdict={})] - self._register_mapper(registry, routes) - command = self._makeOne(registry=registry) - result = command._find_view('/a', registry) - self.assertTrue(IMultiView.providedBy(result)) - self.assertEqual(len(result.views), 2) - self.assertTrue((None, view1, None) in result.views) - self.assertTrue((None, view2, None) in result.views) - - def test__find_multi_routes_all_match(self): - command = self._makeOne() - def factory(request): pass - routes = [DummyRoute('a', '/a', factory=factory, matchdict={}), - DummyRoute('b', '/a', factory=factory, matchdict={})] - mapper = DummyMapper(*routes) - request = DummyRequest({'PATH_INFO':'/a'}) - result = command._find_multi_routes(mapper, request) - self.assertEqual(result, [{'match':{}, 'route':routes[0]}, - {'match':{}, 'route':routes[1]}]) - - def test__find_multi_routes_some_match(self): - command = self._makeOne() - def factory(request): pass - routes = [DummyRoute('a', '/a', factory=factory), - DummyRoute('b', '/a', factory=factory, matchdict={})] - mapper = DummyMapper(*routes) - request = DummyRequest({'PATH_INFO':'/a'}) - result = command._find_multi_routes(mapper, request) - self.assertEqual(result, [{'match':{}, 'route':routes[1]}]) - - def test__find_multi_routes_none_match(self): - command = self._makeOne() - def factory(request): pass - routes = [DummyRoute('a', '/a', factory=factory), - DummyRoute('b', '/a', factory=factory)] - mapper = DummyMapper(*routes) - request = DummyRequest({'PATH_INFO':'/a'}) - result = command._find_multi_routes(mapper, request) - self.assertEqual(result, []) - - def test_views_command_not_found(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - command._find_view = lambda arg1, arg2: None - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' Not found.') - - def test_views_command_not_found_url_starts_without_slash(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - command._find_view = lambda arg1, arg2: None - command.args = ('/foo/bar/myapp.ini#myapp', 'a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' Not found.') - - def test_views_command_single_view_traversal(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context', view_name='a') - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyView') - - def test_views_command_single_view_function_traversal(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def view(): pass - view.__request_attrs__ = {'context': 'context', 'view_name': 'a'} - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.view') - - def test_views_command_single_view_traversal_with_permission(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context', view_name='a') - view.__permission__ = 'test' - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyView') - self.assertEqual(L[9], ' required permission = test') - - def test_views_command_single_view_traversal_with_predicates(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def predicate(): pass - predicate.__text__ = "predicate = x" - view = DummyView(context='context', view_name='a') - view.__predicates__ = [predicate] - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyView') - self.assertEqual(L[9], ' view predicates (predicate = x)') - - def test_views_command_single_view_route(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - route = DummyRoute('a', '/a', matchdict={}) - view = DummyView(context='context', view_name='a', - matched_route=route, subpath='') - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[6], ' Route:') - self.assertEqual(L[8], ' route name: a') - self.assertEqual(L[9], ' route pattern: /a') - self.assertEqual(L[10], ' route path: /a') - self.assertEqual(L[11], ' subpath: ') - self.assertEqual(L[15], ' pyramid.tests.test_scripts.DummyView') - - def test_views_command_multi_view_nested(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view1 = DummyView(context='context', view_name='a1') - view1.__name__ = 'view1' - view1.__view_attr__ = 'call' - multiview1 = DummyMultiView(view1, context='context', view_name='a1') - multiview2 = DummyMultiView(multiview1, context='context', - view_name='a') - command._find_view = lambda arg1, arg2: multiview2 - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.DummyMultiView') - self.assertEqual(L[12], ' pyramid.tests.test_scripts.view1.call') - - def test_views_command_single_view_route_with_route_predicates(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def predicate(): pass - predicate.__text__ = "predicate = x" - route = DummyRoute('a', '/a', matchdict={}, predicate=predicate) - view = DummyView(context='context', view_name='a', - matched_route=route, subpath='') - command._find_view = lambda arg1, arg2: view - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[6], ' Route:') - self.assertEqual(L[8], ' route name: a') - self.assertEqual(L[9], ' route pattern: /a') - self.assertEqual(L[10], ' route path: /a') - self.assertEqual(L[11], ' subpath: ') - self.assertEqual(L[12], ' route predicates (predicate = x)') - self.assertEqual(L[16], ' pyramid.tests.test_scripts.DummyView') - - def test_views_command_multiview(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context') - view.__name__ = 'view' - view.__view_attr__ = 'call' - multiview = DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.view.call') - - def test_views_command_multiview_with_permission(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - view = DummyView(context='context') - view.__name__ = 'view' - view.__view_attr__ = 'call' - view.__permission__ = 'test' - multiview = DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.view.call') - self.assertEqual(L[9], ' required permission = test') - - def test_views_command_multiview_with_predicates(self): - from pyramid.registry import Registry - registry = Registry() - command = self._makeOne(registry=registry) - L = [] - command.out = L.append - def predicate(): pass - predicate.__text__ = "predicate = x" - view = DummyView(context='context') - view.__name__ = 'view' - view.__view_attr__ = 'call' - view.__predicates__ = [predicate] - multiview = DummyMultiView(view, context='context', view_name='a') - command._find_view = lambda arg1, arg2: multiview - command.args = ('/foo/bar/myapp.ini#myapp', '/a') - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L[1], 'URL = /a') - self.assertEqual(L[3], ' context: context') - self.assertEqual(L[4], ' view name: a') - self.assertEqual(L[8], ' pyramid.tests.test_scripts.view.call') - self.assertEqual(L[9], ' view predicates (predicate = x)') - -class TestPTweensCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.scripts.ptweens import PTweensCommand - return PTweensCommand - - def _makeOne(self): - cmd = self._getTargetClass()([]) - cmd.bootstrap = (DummyBootstrap(),) - cmd.args = ('/foo/bar/myapp.ini#myapp',) - return cmd - - def test_command_no_tweens(self): - command = self._makeOne() - command._get_tweens = lambda *arg: None - L = [] - command.out = L.append - result = command.run() - self.assertEqual(result, None) - self.assertEqual(L, []) - - def test_command_implicit_tweens_only(self): - command = self._makeOne() - tweens = DummyTweens([('name', 'item')], None) - command._get_tweens = lambda *arg: tweens - L = [] - command.out = L.append - result = command.run() - self.assertEqual(result, None) - self.assertEqual( - L[0], - '"pyramid.tweens" config value NOT set (implicitly ordered tweens ' - 'used)') - - def test_command_implicit_and_explicit_tweens(self): - command = self._makeOne() - tweens = DummyTweens([('name', 'item')], [('name2', 'item2')]) - command._get_tweens = lambda *arg: tweens - L = [] - command.out = L.append - result = command.run() - self.assertEqual(result, None) - self.assertEqual( - L[0], - '"pyramid.tweens" config value set (explicitly ordered tweens used)') - - def test__get_tweens(self): - command = self._makeOne() - registry = DummyRegistry() - self.assertEqual(command._get_tweens(registry), None) - -class DummyTweens(object): - def __init__(self, implicit, explicit): - self._implicit = implicit - self.explicit = explicit - self.name_to_alias = {} - def implicit(self): - return self._implicit - -class Dummy: - pass - -dummy_root = Dummy() - -class DummyRegistry(object): - settings = {} - def queryUtility(self, iface, default=None, name=''): - return default - -dummy_registry = DummyRegistry() - -class DummyShell(object): - env = {} - help = '' - - def __call__(self, env, help): - self.env = env - self.help = help - -class DummyInteractor: - def __call__(self, banner, local): - self.banner = banner - self.local = local - -class DummyIPShell(object): - IP = Dummy() - IP.BANNER = 'foo' - - def set_banner(self, banner): - self.banner = banner - - def __call__(self): - self.called = True - -class DummyIPShellFactory(object): - def __call__(self, **kw): - self.kw = kw - self.shell = DummyIPShell() - return self.shell - -class DummyApp: - def __init__(self): - self.registry = dummy_registry - -class DummyMapper(object): - def __init__(self, *routes): - self.routes = routes - - def get_routes(self): - return self.routes - -class DummyRoute(object): - def __init__(self, name, pattern, factory=None, - matchdict=None, predicate=None): - self.name = name - self.path = pattern - self.pattern = pattern - self.factory = factory - self.matchdict = matchdict - self.predicates = [] - if predicate is not None: - self.predicates = [predicate] - - def match(self, route): - return self.matchdict - -class DummyRequest: - application_url = 'http://example.com:5432' - script_name = '' - def __init__(self, environ): - self.environ = environ - self.matchdict = {} - -class DummyView(object): - def __init__(self, **attrs): - self.__request_attrs__ = attrs - -from zope.interface import implementer -from pyramid.interfaces import IMultiView -@implementer(IMultiView) -class DummyMultiView(object): - - def __init__(self, *views, **attrs): - self.views = [(None, view, None) for view in views] - self.__request_attrs__ = attrs - -class DummyConfigParser(object): - def __init__(self, result): - self.result = result - - def read(self, filename): - self.filename = filename - - def items(self, section): - self.section = section - if self.result is None: - from pyramid.compat import configparser - raise configparser.NoSectionError(section) - return self.result - -class DummyConfigParserFactory(object): - items = None - - def __call__(self): - self.parser = DummyConfigParser(self.items) - return self.parser - -class DummyCloser(object): - def __call__(self): - self.called = True - -class DummyBootstrap(object): - def __init__(self, app=None, registry=None, request=None, root=None, - root_factory=None, closer=None): - self.app = app or DummyApp() - if registry is None: - registry = DummyRegistry() - self.registry = registry - if request is None: - request = DummyRequest({}) - self.request = request - if root is None: - root = Dummy() - self.root = root - if root_factory is None: - root_factory = Dummy() - self.root_factory = root_factory - if closer is None: - closer = DummyCloser() - self.closer = closer - - def __call__(self, *a, **kw): - self.a = a - self.kw = kw - return { - 'app': self.app, - 'registry': self.registry, - 'request': self.request, - 'root': self.root, - 'root_factory': self.root_factory, - 'closer': self.closer, - } diff --git a/pyramid/tests/test_scripts/__init__.py b/pyramid/tests/test_scripts/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/tests/test_scripts/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/tests/test_scripts/dummy.py b/pyramid/tests/test_scripts/dummy.py new file mode 100644 index 000000000..3275f7804 --- /dev/null +++ b/pyramid/tests/test_scripts/dummy.py @@ -0,0 +1,151 @@ +class DummyTweens(object): + def __init__(self, implicit, explicit): + self._implicit = implicit + self.explicit = explicit + self.name_to_alias = {} + def implicit(self): + return self._implicit + +class Dummy: + pass + +dummy_root = Dummy() + +class DummyRegistry(object): + settings = {} + def queryUtility(self, iface, default=None, name=''): + return default + +dummy_registry = DummyRegistry() + +class DummyShell(object): + env = {} + help = '' + + def __call__(self, env, help): + self.env = env + self.help = help + +class DummyInteractor: + def __call__(self, banner, local): + self.banner = banner + self.local = local + +class DummyIPShell(object): + IP = Dummy() + IP.BANNER = 'foo' + + def set_banner(self, banner): + self.banner = banner + + def __call__(self): + self.called = True + +class DummyIPShellFactory(object): + def __call__(self, **kw): + self.kw = kw + self.shell = DummyIPShell() + return self.shell + +class DummyApp: + def __init__(self): + self.registry = dummy_registry + +class DummyMapper(object): + def __init__(self, *routes): + self.routes = routes + + def get_routes(self): + return self.routes + +class DummyRoute(object): + def __init__(self, name, pattern, factory=None, + matchdict=None, predicate=None): + self.name = name + self.path = pattern + self.pattern = pattern + self.factory = factory + self.matchdict = matchdict + self.predicates = [] + if predicate is not None: + self.predicates = [predicate] + + def match(self, route): + return self.matchdict + +class DummyRequest: + application_url = 'http://example.com:5432' + script_name = '' + def __init__(self, environ): + self.environ = environ + self.matchdict = {} + +class DummyView(object): + def __init__(self, **attrs): + self.__request_attrs__ = attrs + +from zope.interface import implementer +from pyramid.interfaces import IMultiView +@implementer(IMultiView) +class DummyMultiView(object): + + def __init__(self, *views, **attrs): + self.views = [(None, view, None) for view in views] + self.__request_attrs__ = attrs + +class DummyConfigParser(object): + def __init__(self, result): + self.result = result + + def read(self, filename): + self.filename = filename + + def items(self, section): + self.section = section + if self.result is None: + from pyramid.compat import configparser + raise configparser.NoSectionError(section) + return self.result + +class DummyConfigParserFactory(object): + items = None + + def __call__(self): + self.parser = DummyConfigParser(self.items) + return self.parser + +class DummyCloser(object): + def __call__(self): + self.called = True + +class DummyBootstrap(object): + def __init__(self, app=None, registry=None, request=None, root=None, + root_factory=None, closer=None): + self.app = app or DummyApp() + if registry is None: + registry = DummyRegistry() + self.registry = registry + if request is None: + request = DummyRequest({}) + self.request = request + if root is None: + root = Dummy() + self.root = root + if root_factory is None: + root_factory = Dummy() + self.root_factory = root_factory + if closer is None: + closer = DummyCloser() + self.closer = closer + + def __call__(self, *a, **kw): + self.a = a + self.kw = kw + return { + 'app': self.app, + 'registry': self.registry, + 'request': self.request, + 'root': self.root, + 'root_factory': self.root_factory, + 'closer': self.closer, + } diff --git a/pyramid/tests/test_scripts/test_proutes.py b/pyramid/tests/test_scripts/test_proutes.py new file mode 100644 index 000000000..a8b577316 --- /dev/null +++ b/pyramid/tests/test_scripts/test_proutes.py @@ -0,0 +1,129 @@ +import unittest +from pyramid.tests.test_scripts import dummy + +class TestPRoutesCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.proutes import PRoutesCommand + return PRoutesCommand + + def _makeOne(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (dummy.DummyBootstrap(),) + cmd.args = ('/foo/bar/myapp.ini#myapp',) + return cmd + + def test_no_routes(self): + command = self._makeOne() + mapper = dummy.DummyMapper() + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L, []) + + def test_no_mapper(self): + command = self._makeOne() + command._get_mapper = lambda *arg:None + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L, []) + + def test_single_route_no_route_registered(self): + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split(), ['a', '/a', '']) + + def test_single_route_no_views_registered(self): + from zope.interface import Interface + from pyramid.registry import Registry + from pyramid.interfaces import IRouteRequest + registry = Registry() + def view():pass + class IMyRoute(Interface): + pass + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = (dummy.DummyBootstrap(registry=registry),) + result = command.run() + self.assertEqual(result, None) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split()[:3], ['a', '/a', 'None']) + + def test_single_route_one_view_registered(self): + from zope.interface import Interface + from pyramid.registry import Registry + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + registry = Registry() + def view():pass + class IMyRoute(Interface): + pass + registry.registerAdapter(view, + (IViewClassifier, IMyRoute, Interface), + IView, '') + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + route = dummy.DummyRoute('a', '/a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + command.bootstrap = (dummy.DummyBootstrap(registry=registry),) + result = command.run() + self.assertEqual(result, None) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split()[:4], ['a', '/a', '']) + + def test__get_mapper(self): + from pyramid.registry import Registry + from pyramid.urldispatch import RoutesMapper + command = self._makeOne() + registry = Registry() + result = command._get_mapper(registry) + self.assertEqual(result.__class__, RoutesMapper) + diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py new file mode 100644 index 000000000..b0ea8c5ea --- /dev/null +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -0,0 +1,249 @@ +import unittest +from pyramid.tests.test_scripts import dummy + +class TestPShellCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.pshell import PShellCommand + return PShellCommand + + def _makeOne(self, patch_bootstrap=True, patch_config=True, + patch_args=True, patch_options=True): + cmd = self._getTargetClass()([]) + if patch_bootstrap: + self.bootstrap = dummy.DummyBootstrap() + cmd.bootstrap = (self.bootstrap,) + if patch_config: + self.config_factory = dummy.DummyConfigParserFactory() + cmd.ConfigParser = self.config_factory + if patch_args: + self.args = ('/foo/bar/myapp.ini#myapp',) + cmd.args = self.args + if patch_options: + class Options(object): pass + self.options = Options() + self.options.disable_ipython = True + self.options.setup = None + cmd.options = self.options + return cmd + + def test_make_default_shell(self): + command = self._makeOne() + interact = dummy.DummyInteractor() + shell = command.make_default_shell(interact) + shell({'foo': 'bar'}, 'a help message') + self.assertEqual(interact.local, {'foo': 'bar'}) + self.assertTrue('a help message' in interact.banner) + + def test_make_ipython_v0_11_shell(self): + command = self._makeOne() + ipshell_factory = dummy.DummyIPShellFactory() + shell = command.make_ipython_v0_11_shell(ipshell_factory) + shell({'foo': 'bar'}, 'a help message') + self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) + self.assertTrue('a help message' in ipshell_factory.kw['banner2']) + self.assertTrue(ipshell_factory.shell.called) + + def test_make_ipython_v0_10_shell(self): + command = self._makeOne() + ipshell_factory = dummy.DummyIPShellFactory() + shell = command.make_ipython_v0_10_shell(ipshell_factory) + shell({'foo': 'bar'}, 'a help message') + self.assertEqual(ipshell_factory.kw['argv'], []) + self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) + self.assertTrue('a help message' in ipshell_factory.shell.banner) + self.assertTrue(ipshell_factory.shell.called) + + def test_command_loads_default_shell(self): + command = self._makeOne() + shell = dummy.DummyShell() + command.make_ipython_v0_11_shell = lambda: None + command.make_ipython_v0_10_shell = lambda: None + command.make_default_shell = lambda: shell + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_default_shell_with_ipython_disabled(self): + command = self._makeOne() + shell = dummy.DummyShell() + bad_shell = dummy.DummyShell() + command.make_ipython_v0_11_shell = lambda: bad_shell + command.make_ipython_v0_10_shell = lambda: bad_shell + command.make_default_shell = lambda: shell + command.options.disable_ipython = True + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertEqual(bad_shell.env, {}) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_ipython_v0_11(self): + command = self._makeOne() + shell = dummy.DummyShell() + command.make_ipython_v0_11_shell = lambda: shell + command.make_ipython_v0_10_shell = lambda: None + command.make_default_shell = lambda: None + command.options.disable_ipython = False + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_ipython_v0_10(self): + command = self._makeOne() + shell = dummy.DummyShell() + command.make_ipython_v0_11_shell = lambda: None + command.make_ipython_v0_10_shell = lambda: shell + command.make_default_shell = lambda: None + command.options.disable_ipython = False + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_custom_items(self): + command = self._makeOne() + model = dummy.Dummy() + self.config_factory.items = [('m', model)] + shell = dummy.DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'm':model, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_setup(self): + command = self._makeOne() + def setup(env): + env['a'] = 1 + env['root'] = 'root override' + self.config_factory.items = [('setup', setup)] + shell = dummy.DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':'root override', + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'a':1, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_check_variable_override_order(self): + command = self._makeOne() + model = dummy.Dummy() + def setup(env): + env['a'] = 1 + env['m'] = 'model override' + env['root'] = 'root override' + self.config_factory.items = [('setup', setup), ('m', model)] + shell = dummy.DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':'root override', + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'a':1, 'm':model, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_loads_setup_from_options(self): + command = self._makeOne() + def setup(env): + env['a'] = 1 + env['root'] = 'root override' + model = dummy.Dummy() + self.config_factory.items = [('setup', 'abc'), + ('m', model)] + command.options.setup = setup + shell = dummy.DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':self.bootstrap.app, 'root':'root override', + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + 'a':1, 'm':model, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + + def test_command_custom_section_override(self): + command = self._makeOne() + dummy_ = dummy.Dummy() + self.config_factory.items = [('app', dummy_), ('root', dummy_), + ('registry', dummy_), ('request', dummy_)] + shell = dummy.DummyShell() + command.run(shell) + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.env, { + 'app':dummy_, 'root':dummy_, 'registry':dummy_, 'request':dummy_, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.help) + diff --git a/pyramid/tests/test_scripts/test_ptweens.py b/pyramid/tests/test_scripts/test_ptweens.py new file mode 100644 index 000000000..4ee38dd85 --- /dev/null +++ b/pyramid/tests/test_scripts/test_ptweens.py @@ -0,0 +1,53 @@ +import unittest +from pyramid.tests.test_scripts import dummy + +class TestPTweensCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.ptweens import PTweensCommand + return PTweensCommand + + def _makeOne(self): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (dummy.DummyBootstrap(),) + cmd.args = ('/foo/bar/myapp.ini#myapp',) + return cmd + + def test_command_no_tweens(self): + command = self._makeOne() + command._get_tweens = lambda *arg: None + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L, []) + + def test_command_implicit_tweens_only(self): + command = self._makeOne() + tweens = dummy.DummyTweens([('name', 'item')], None) + command._get_tweens = lambda *arg: tweens + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual( + L[0], + '"pyramid.tweens" config value NOT set (implicitly ordered tweens ' + 'used)') + + def test_command_implicit_and_explicit_tweens(self): + command = self._makeOne() + tweens = dummy.DummyTweens([('name', 'item')], [('name2', 'item2')]) + command._get_tweens = lambda *arg: tweens + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual( + L[0], + '"pyramid.tweens" config value set (explicitly ordered tweens used)') + + def test__get_tweens(self): + command = self._makeOne() + registry = dummy.DummyRegistry() + self.assertEqual(command._get_tweens(registry), None) + diff --git a/pyramid/tests/test_scripts/test_pviews.py b/pyramid/tests/test_scripts/test_pviews.py new file mode 100644 index 000000000..473ba5598 --- /dev/null +++ b/pyramid/tests/test_scripts/test_pviews.py @@ -0,0 +1,467 @@ +import unittest +from pyramid.tests.test_scripts import dummy + +class TestPViewsCommand(unittest.TestCase): + def _getTargetClass(self): + from pyramid.scripts.pviews import PViewsCommand + return PViewsCommand + + def _makeOne(self, registry=None): + cmd = self._getTargetClass()([]) + cmd.bootstrap = (dummy.DummyBootstrap(registry=registry),) + cmd.args = ('/foo/bar/myapp.ini#myapp',) + return cmd + + def _register_mapper(self, registry, routes): + from pyramid.interfaces import IRoutesMapper + mapper = dummy.DummyMapper(*routes) + registry.registerUtility(mapper, IRoutesMapper) + + def test__find_view_no_match(self): + from pyramid.registry import Registry + registry = Registry() + self._register_mapper(registry, []) + command = self._makeOne(registry) + result = command._find_view('/a', registry) + self.assertEqual(result, None) + + def test__find_view_no_match_multiview_registered(self): + from zope.interface import implementer + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + @implementer(IMultiView) + class View1(object): + pass + request = dummy.DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter(View1(), + (IViewClassifier, IRequest, root_iface), + IMultiView) + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + result = command._find_view('/x', registry) + self.assertEqual(result, None) + + def test__find_view_traversal(self): + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1(): pass + request = dummy.DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + registry.registerAdapter(view1, + (IViewClassifier, IRequest, root_iface), + IView, name='a') + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertEqual(result, view1) + + def test__find_view_traversal_multiview(self): + from zope.interface import implementer + from zope.interface import providedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + from pyramid.traversal import DefaultRootFactory + from pyramid.registry import Registry + registry = Registry() + @implementer(IMultiView) + class View1(object): + pass + request = dummy.DummyRequest({'PATH_INFO':'/a'}) + root = DefaultRootFactory(request) + root_iface = providedBy(root) + view = View1() + registry.registerAdapter(view, + (IViewClassifier, IRequest, root_iface), + IMultiView, name='a') + self._register_mapper(registry, []) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertEqual(result, view) + + def test__find_view_route_no_multiview(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.registry import Registry + registry = Registry() + def view():pass + class IMyRoot(Interface): + pass + class IMyRoute(Interface): + pass + registry.registerAdapter(view, + (IViewClassifier, IMyRoute, IMyRoot), + IView, '') + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + routes = [dummy.DummyRoute('a', '/a', factory=Factory, matchdict={}), + dummy.DummyRoute('b', '/b', factory=Factory)] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertEqual(result, view) + + def test__find_view_route_multiview_no_view_registered(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1():pass + def view2():pass + class IMyRoot(Interface): + pass + class IMyRoute1(Interface): + pass + class IMyRoute2(Interface): + pass + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + registry.registerUtility(Factory, IRootFactory) + routes = [dummy.DummyRoute('a', '/a', matchdict={}), + dummy.DummyRoute('b', '/a', matchdict={})] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertTrue(IMultiView.providedBy(result)) + + def test__find_view_route_multiview(self): + from zope.interface import Interface + from zope.interface import implementer + from pyramid.interfaces import IRouteRequest + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IRootFactory + from pyramid.registry import Registry + registry = Registry() + def view1():pass + def view2():pass + class IMyRoot(Interface): + pass + class IMyRoute1(Interface): + pass + class IMyRoute2(Interface): + pass + registry.registerAdapter(view1, + (IViewClassifier, IMyRoute1, IMyRoot), + IView, '') + registry.registerAdapter(view2, + (IViewClassifier, IMyRoute2, IMyRoot), + IView, '') + registry.registerUtility(IMyRoute1, IRouteRequest, name='a') + registry.registerUtility(IMyRoute2, IRouteRequest, name='b') + @implementer(IMyRoot) + class Factory(object): + def __init__(self, request): + pass + registry.registerUtility(Factory, IRootFactory) + routes = [dummy.DummyRoute('a', '/a', matchdict={}), + dummy.DummyRoute('b', '/a', matchdict={})] + self._register_mapper(registry, routes) + command = self._makeOne(registry=registry) + result = command._find_view('/a', registry) + self.assertTrue(IMultiView.providedBy(result)) + self.assertEqual(len(result.views), 2) + self.assertTrue((None, view1, None) in result.views) + self.assertTrue((None, view2, None) in result.views) + + def test__find_multi_routes_all_match(self): + command = self._makeOne() + def factory(request): pass + routes = [dummy.DummyRoute('a', '/a', factory=factory, matchdict={}), + dummy.DummyRoute('b', '/a', factory=factory, matchdict={})] + mapper = dummy.DummyMapper(*routes) + request = dummy.DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, [{'match':{}, 'route':routes[0]}, + {'match':{}, 'route':routes[1]}]) + + def test__find_multi_routes_some_match(self): + command = self._makeOne() + def factory(request): pass + routes = [dummy.DummyRoute('a', '/a', factory=factory), + dummy.DummyRoute('b', '/a', factory=factory, matchdict={})] + mapper = dummy.DummyMapper(*routes) + request = dummy.DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, [{'match':{}, 'route':routes[1]}]) + + def test__find_multi_routes_none_match(self): + command = self._makeOne() + def factory(request): pass + routes = [dummy.DummyRoute('a', '/a', factory=factory), + dummy.DummyRoute('b', '/a', factory=factory)] + mapper = dummy.DummyMapper(*routes) + request = dummy.DummyRequest({'PATH_INFO':'/a'}) + result = command._find_multi_routes(mapper, request) + self.assertEqual(result, []) + + def test_views_command_not_found(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + command._find_view = lambda arg1, arg2: None + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' Not found.') + + def test_views_command_not_found_url_starts_without_slash(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + command._find_view = lambda arg1, arg2: None + command.args = ('/foo/bar/myapp.ini#myapp', 'a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' Not found.') + + def test_views_command_single_view_traversal(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context', view_name='a') + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.dummy.DummyView') + + def test_views_command_single_view_function_traversal(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def view(): pass + view.__request_attrs__ = {'context': 'context', 'view_name': 'a'} + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.test_pviews.view') + + def test_views_command_single_view_traversal_with_permission(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context', view_name='a') + view.__permission__ = 'test' + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.dummy.DummyView') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_single_view_traversal_with_predicates(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + view = dummy.DummyView(context='context', view_name='a') + view.__predicates__ = [predicate] + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.dummy.DummyView') + self.assertEqual(L[9], ' view predicates (predicate = x)') + + def test_views_command_single_view_route(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + route = dummy.DummyRoute('a', '/a', matchdict={}) + view = dummy.DummyView(context='context', view_name='a', + matched_route=route, subpath='') + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[15], + ' pyramid.tests.test_scripts.dummy.DummyView') + + def test_views_command_multi_view_nested(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view1 = dummy.DummyView(context='context', view_name='a1') + view1.__name__ = 'view1' + view1.__view_attr__ = 'call' + multiview1 = dummy.DummyMultiView(view1, context='context', + view_name='a1') + multiview2 = dummy.DummyMultiView(multiview1, context='context', + view_name='a') + command._find_view = lambda arg1, arg2: multiview2 + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.dummy.DummyMultiView') + self.assertEqual(L[12], + ' pyramid.tests.test_scripts.dummy.view1.call') + + def test_views_command_single_view_route_with_route_predicates(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + route = dummy.DummyRoute('a', '/a', matchdict={}, predicate=predicate) + view = dummy.DummyView(context='context', view_name='a', + matched_route=route, subpath='') + command._find_view = lambda arg1, arg2: view + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[6], ' Route:') + self.assertEqual(L[8], ' route name: a') + self.assertEqual(L[9], ' route pattern: /a') + self.assertEqual(L[10], ' route path: /a') + self.assertEqual(L[11], ' subpath: ') + self.assertEqual(L[12], ' route predicates (predicate = x)') + self.assertEqual(L[16], + ' pyramid.tests.test_scripts.dummy.DummyView') + + def test_views_command_multiview(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + multiview = dummy.DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.dummy.view.call') + + def test_views_command_multiview_with_permission(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + view = dummy.DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__permission__ = 'test' + multiview = dummy.DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.dummy.view.call') + self.assertEqual(L[9], ' required permission = test') + + def test_views_command_multiview_with_predicates(self): + from pyramid.registry import Registry + registry = Registry() + command = self._makeOne(registry=registry) + L = [] + command.out = L.append + def predicate(): pass + predicate.__text__ = "predicate = x" + view = dummy.DummyView(context='context') + view.__name__ = 'view' + view.__view_attr__ = 'call' + view.__predicates__ = [predicate] + multiview = dummy.DummyMultiView(view, context='context', view_name='a') + command._find_view = lambda arg1, arg2: multiview + command.args = ('/foo/bar/myapp.ini#myapp', '/a') + result = command.run() + self.assertEqual(result, None) + self.assertEqual(L[1], 'URL = /a') + self.assertEqual(L[3], ' context: context') + self.assertEqual(L[4], ' view name: a') + self.assertEqual(L[8], + ' pyramid.tests.test_scripts.dummy.view.call') + self.assertEqual(L[9], ' view predicates (predicate = x)') + -- cgit v1.2.3 From 126afb7d1e85ca771b350ad217ff578e52d63e87 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 23:12:15 -0400 Subject: test coverage for pcreate command --- pyramid/compat.py | 6 ++++ pyramid/scripts/pcreate.py | 86 ++++++++++++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/pyramid/compat.py b/pyramid/compat.py index f464a218c..3ac235b0f 100644 --- a/pyramid/compat.py +++ b/pyramid/compat.py @@ -229,3 +229,9 @@ try: # pragma: no cover except NameError: # pragma: no cover input_ = input + +try: + from StringIO import StringIO as NativeIO +except ImportError: # pragma: no cover + from io import StringIO as NativeIO + diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py index 39aca6b70..effb54734 100644 --- a/pyramid/scripts/pcreate.py +++ b/pyramid/scripts/pcreate.py @@ -11,14 +11,14 @@ import sys _bad_chars_re = re.compile('[^a-zA-Z0-9_]') def main(argv=sys.argv): - command = CreateCommand(argv) + command = PCreateCommand(argv) return command.run() -class CreateCommand(object): +class PCreateCommand(object): verbose = True interactive = False simulate = False - usage = "usage: %prog [options] project" + usage = "usage: %prog [options] distribution_name" parser = optparse.OptionParser(usage) parser.add_option('-s', '--scaffold', dest='scaffold_name', @@ -37,37 +37,41 @@ class CreateCommand(object): dest='overwrite', action='store_true', help='Always overwrite') + parser.add_option('-q', '--quiet', + dest='quiet', + action='store_true', + help='Dont emit any output') def __init__(self, argv): self.options, self.args = self.parser.parse_args(argv[1:]) - self.scaffolds = all_scaffolds() - self.available_scaffoldnames = [x.name for x in self.scaffolds] + self.scaffolds = self.all_scaffolds() def run(self): if self.options.list: return self.show_scaffolds() if not self.options.scaffold_name: - print('You must provide at least one scaffold name') + self.out('You must provide at least one scaffold name') return if not self.args: - print('You must provide a project name') + self.out('You must provide a project name') return - diff = set(self.options.scaffold_name).difference( - self.available_scaffoldnames) + available = [x.name for x in self.scaffolds] + diff = set(self.options.scaffold_name).difference(available) if diff: - print('Unavailable scaffolds: %s' % list(diff)) - self.render_scaffolds() + self.out('Unavailable scaffolds: %s' % list(diff)) + return + return self.render_scaffolds() def render_scaffolds(self): options = self.options args = self.args - dist_name = args[0].lstrip(os.path.sep) - output_dir = os.path.normpath(os.path.join(os.getcwd(), dist_name)) - pkg_name = _bad_chars_re.sub('', dist_name.lower()) - safe_name = pkg_resources.safe_name(dist_name) - egg_name = pkg_resources.to_filename(safe_name), + project_name = args[0].lstrip(os.path.sep) + output_dir = os.path.normpath(os.path.join(os.getcwd(), project_name)) + pkg_name = _bad_chars_re.sub('', project_name.lower()) + safe_name = pkg_resources.safe_name(project_name) + egg_name = pkg_resources.to_filename(safe_name) vars = { - 'project': dist_name, + 'project': project_name, 'package': pkg_name, 'egg': egg_name, } @@ -75,28 +79,36 @@ class CreateCommand(object): for scaffold in self.scaffolds: if scaffold.name == scaffold_name: scaffold.run(self, output_dir, vars) + return True def show_scaffolds(self): - scaffolds = list(self.scaffolds) - max_name = max([len(t.name) for t in scaffolds]) - scaffolds.sort(key=lambda x: x.name) - print('Available scaffolds:') - for scaffold in scaffolds: - print(' %s:%s %s' % ( - scaffold.name, - ' '*(max_name-len(scaffold.name)), scaffold.summary)) + scaffolds = sorted(self.scaffolds, key=lambda x: x.name) + if scaffolds: + max_name = max([len(t.name) for t in scaffolds]) + self.out('Available scaffolds:') + for scaffold in scaffolds: + self.out(' %s:%s %s' % ( + scaffold.name, + ' '*(max_name-len(scaffold.name)), scaffold.summary)) + else: + self.out('No scaffolds available') + return True + + def all_scaffolds(self): + scaffolds = [] + eps = list(pkg_resources.iter_entry_points('pyramid.scaffold')) + for entry in eps: + try: + scaffold_class = entry.load() + scaffold = scaffold_class(entry.name) + scaffolds.append(scaffold) + except Exception as e: # pragma: no cover + self.out('Warning: could not load entry point %s (%s: %s)' % ( + entry.name, e.__class__.__name__, e)) + return scaffolds + def out(self, msg): # pragma: no cover + if not self.options.quiet: + print(msg) -def all_scaffolds(): - scaffolds = [] - eps = list(pkg_resources.iter_entry_points('pyramid.scaffold')) - for entry in eps: - try: - scaffold_class = entry.load() - scaffold = scaffold_class(entry.name) - scaffolds.append(scaffold) - except Exception as e: - print('Warning: could not load entry point %s (%s: %s)' % ( - entry.name, e.__class__.__name__, e)) - return scaffolds -- cgit v1.2.3 From 47106ccf390c0c38ad65aa88a50b697f43f39aad Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 23:12:40 -0400 Subject: test coverage for pcreate command, really --- pyramid/tests/test_scripts/test_pcreate.py | 116 +++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 pyramid/tests/test_scripts/test_pcreate.py diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py new file mode 100644 index 000000000..9abbbc230 --- /dev/null +++ b/pyramid/tests/test_scripts/test_pcreate.py @@ -0,0 +1,116 @@ +import unittest +from pyramid.tests.test_scripts import dummy + +class TestPCreateCommand(unittest.TestCase): + def setUp(self): + from pyramid.compat import NativeIO + self.out_ = NativeIO() + + def out(self, msg): + self.out_.write(msg) + + def _getTargetClass(self): + from pyramid.scripts.pcreate import PCreateCommand + return PCreateCommand + + def _makeOne(self, *args): + effargs = ['pcreate'] + effargs.extend(args) + cmd = self._getTargetClass()(effargs) + cmd.bootstrap = (dummy.DummyBootstrap(),) + cmd.out = self.out + return cmd + + def test_run_show_scaffolds_exist(self): + cmd = self._makeOne('-l') + result = cmd.run() + self.assertEqual(result, True) + out = self.out_.getvalue() + self.assertTrue(out.startswith('Available scaffolds')) + + def test_run_show_scaffolds_none_exist(self): + cmd = self._makeOne('-l') + cmd.scaffolds = [] + result = cmd.run() + self.assertEqual(result, True) + out = self.out_.getvalue() + self.assertTrue(out.startswith('No scaffolds available')) + + def test_run_no_scaffold_name(self): + cmd = self._makeOne() + result = cmd.run() + self.assertEqual(result, None) + out = self.out_.getvalue() + self.assertTrue(out.startswith( + 'You must provide at least one scaffold name')) + + def test_no_project_name(self): + cmd = self._makeOne('-s', 'dummy') + result = cmd.run() + self.assertEqual(result, None) + out = self.out_.getvalue() + self.assertTrue(out.startswith('You must provide a project name')) + + def test_unknown_scaffold_name(self): + cmd = self._makeOne('-s', 'dummyXX', 'distro') + result = cmd.run() + self.assertEqual(result, None) + out = self.out_.getvalue() + self.assertTrue(out.startswith('Unavailable scaffolds')) + + def test_known_scaffold_single_rendered(self): + import os + cmd = self._makeOne('-s', 'dummy', 'Distro') + scaffold = DummyScaffold('dummy') + cmd.scaffolds = [scaffold] + result = cmd.run() + self.assertEqual(result, True) + self.assertEqual( + scaffold.output_dir, + os.path.normpath(os.path.join(os.getcwd(), 'Distro')) + ) + self.assertEqual( + scaffold.vars, + {'project': 'Distro', 'egg': 'Distro', 'package': 'distro'}) + + def test_known_scaffold_multiple_rendered(self): + import os + cmd = self._makeOne('-s', 'dummy1', '-s', 'dummy2', 'Distro') + scaffold1 = DummyScaffold('dummy1') + scaffold2 = DummyScaffold('dummy2') + cmd.scaffolds = [scaffold1, scaffold2] + result = cmd.run() + self.assertEqual(result, True) + self.assertEqual( + scaffold1.output_dir, + os.path.normpath(os.path.join(os.getcwd(), 'Distro')) + ) + self.assertEqual( + scaffold1.vars, + {'project': 'Distro', 'egg': 'Distro', 'package': 'distro'}) + self.assertEqual( + scaffold2.output_dir, + os.path.normpath(os.path.join(os.getcwd(), 'Distro')) + ) + self.assertEqual( + scaffold2.vars, + {'project': 'Distro', 'egg': 'Distro', 'package': 'distro'}) + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.pcreate import main + return main(argv) + + def test_it(self): + result = self._callFUT(['pcreate', '-q']) + self.assertEqual(result, None) + +class DummyScaffold(object): + def __init__(self, name): + self.name = name + + def run(self, command, output_dir, vars): + self.command = command + self.output_dir = output_dir + self.vars = vars + -- cgit v1.2.3 From dee4fffdd2408f8838309fae0e5957ed7a1cbf73 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Wed, 5 Oct 2011 23:56:06 -0400 Subject: no need for a dummy bootstrap here --- pyramid/tests/test_scripts/test_pcreate.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py index 9abbbc230..346b35fda 100644 --- a/pyramid/tests/test_scripts/test_pcreate.py +++ b/pyramid/tests/test_scripts/test_pcreate.py @@ -1,5 +1,4 @@ import unittest -from pyramid.tests.test_scripts import dummy class TestPCreateCommand(unittest.TestCase): def setUp(self): @@ -17,7 +16,6 @@ class TestPCreateCommand(unittest.TestCase): effargs = ['pcreate'] effargs.extend(args) cmd = self._getTargetClass()(effargs) - cmd.bootstrap = (dummy.DummyBootstrap(),) cmd.out = self.out return cmd -- cgit v1.2.3 From 2e6d63ce873dd866f93215310c633a6d75dc0e13 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 01:01:52 -0400 Subject: add minimal pserve test --- pyramid/scripts/pserve.py | 394 +++++++++++++++++++++--------- pyramid/tests/test_scripts/test_pserve.py | 27 ++ 2 files changed, 303 insertions(+), 118 deletions(-) create mode 100644 pyramid/tests/test_scripts/test_pserve.py diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index 561085119..b9d2d944f 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -8,16 +8,18 @@ # Code taken also from QP: http://www.mems-exchange.org/software/qp/ From # lib/site.py -import re +import atexit +import errno +import logging import optparse import os -import errno -import sys -import time +import re import subprocess +import sys import threading -import atexit -import logging +import time +import traceback + from logging.config import fileConfig from pyramid.compat import configparser @@ -26,16 +28,14 @@ from paste.deploy import loadapp, loadserver MAXFD = 1024 -jython = sys.platform.startswith('java') - def main(argv=sys.argv): - command = ServeCommand(argv) + command = PServeCommand(argv) return command.run() class DaemonizeException(Exception): pass -class ServeCommand(object): +class PServeCommand(object): min_args = 0 usage = 'CONFIG_FILE [start|stop|restart|status] [var=value]' @@ -71,7 +71,8 @@ class ServeCommand(object): '--server-name', dest='server_name', metavar='SECTION_NAME', - help="Use the named server as defined in the configuration file (default: main)") + help=("Use the named server as defined in the configuration file " + "(default: main)")) if hasattr(os, 'fork'): parser.add_option( '--daemon', @@ -82,7 +83,8 @@ class ServeCommand(object): '--pid-file', dest='pid_file', metavar='FILENAME', - help="Save PID to file (default to pyramid.pid if running in daemon mode)") + help=("Save PID to file (default to pyramid.pid if running in " + "daemon mode)")) parser.add_option( '--log-file', dest='log_file', @@ -97,7 +99,8 @@ class ServeCommand(object): '--reload-interval', dest='reload_interval', default=1, - help="Seconds between checking files (low number can cause significant CPU usage)") + help=("Seconds between checking files (low number can cause " + "significant CPU usage)")) parser.add_option( '--monitor-restart', dest='monitor_restart', @@ -108,6 +111,11 @@ class ServeCommand(object): action='store_true', dest='show_status', help="Show the status of the (presumably daemonized) server") + parser.add_option( + '-q', '--quiet', + action='store_true', + dest='quiet', + help='Produce little or no output') if hasattr(os, 'setuid'): # I don't think these are available on Windows @@ -126,14 +134,8 @@ class ServeCommand(object): '--stop-daemon', dest='stop_daemon', action='store_true', - help='Stop a daemonized server (given a PID file, or default pyramid.pid file)') - - if jython: - parser.add_option( - '--disable-jython-reloader', - action='store_true', - dest='disable_jython_reloader', - help="Disable the Jython reloader") + help=('Stop a daemonized server (given a PID file, or default ' + 'pyramid.pid file)')) _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) @@ -147,6 +149,10 @@ class ServeCommand(object): def __init__(self, argv): self.options, self.args = self.parser.parse_args(argv[1:]) + def out(self, msg): + if not self.options.quiet: + print(msg) + def run(self): if self.options.stop_daemon: return self.stop_daemon() @@ -154,13 +160,15 @@ class ServeCommand(object): if not hasattr(self.options, 'set_user'): # Windows case: self.options.set_user = self.options.set_group = None + # @@: Is this the right stage to set the user at? self.change_user_group( self.options.set_user, self.options.set_group) if self.requires_config_file: if not self.args: - raise ValueError('You must give a config file') + self.out('You must give a config file') + return app_spec = self.args[0] if (len(self.args) > 1 and self.args[1] in self.possible_subcommands): @@ -179,37 +187,20 @@ class ServeCommand(object): cmd = None restvars = self.args[:] - jython_monitor = False if self.options.reload: - if jython and not self.options.disable_jython_reloader: - # JythonMonitor raises the special SystemRestart - # exception that'll cause the Jython interpreter to - # reload in the existing Java process (avoiding - # subprocess startup time) - try: - from paste.reloader import JythonMonitor - except ImportError: - pass - else: - jython_monitor = JythonMonitor(poll_interval=int( - self.options.reload_interval)) - if self.requires_config_file: - jython_monitor.watch_file(self.args[0]) - - if not jython_monitor: - if os.environ.get(self._reloader_environ_key): - from paste import reloader - if self.verbose > 1: - print('Running reloading file monitor') - reloader.install(int(self.options.reload_interval)) - if self.requires_config_file: - reloader.watch_file(self.args[0]) - else: - return self.restart_with_reloader() + if os.environ.get(self._reloader_environ_key): + if self.verbose > 1: + self.out('Running reloading file monitor') + install_reloader(int(self.options.reload_interval)) + if self.requires_config_file: + watch_file(self.args[0]) + else: + return self.restart_with_reloader() if cmd not in (None, 'start', 'stop', 'restart', 'status'): - raise ValueError( + self.out( 'Error: must give start|stop|restart (not %s)' % cmd) + return if cmd == 'status' or self.options.show_status: return self.show_status() @@ -218,9 +209,9 @@ class ServeCommand(object): result = self.stop_daemon() if result: if cmd == 'restart': - print("Could not stop daemon; aborting") + self.out("Could not stop daemon; aborting") else: - print("Could not stop daemon") + self.out("Could not stop daemon") return result if cmd == 'stop': return result @@ -271,7 +262,7 @@ class ServeCommand(object): self.daemonize() except DaemonizeException as ex: if self.verbose > 0: - print(str(ex)) + self.out(str(ex)) return if (self.options.monitor_restart @@ -306,7 +297,7 @@ class ServeCommand(object): msg = 'Starting server in PID %i.' % os.getpid() else: msg = 'Starting server.' - print(msg) + self.out(msg) def serve(): try: @@ -318,15 +309,9 @@ class ServeCommand(object): msg = ' '+str(e) else: msg = '' - print('Exiting%s (-v to see traceback)' % msg) + self.out('Exiting%s (-v to see traceback)' % msg) - if jython_monitor: - # JythonMonitor has to be ran from the main thread - threading.Thread(target=serve).start() - print('Starting Jython file monitor') - jython_monitor.periodic_reload() - else: - serve() + serve() def loadserver(self, server_spec, name, relative_to, **kw): return loadserver( @@ -396,7 +381,7 @@ class ServeCommand(object): % (pid, self.options.pid_file)) if self.verbose > 0: - print('Entering daemon mode') + self.out('Entering daemon mode') pid = os.fork() if pid: # The forked process also has a handle on resources, so we @@ -432,31 +417,69 @@ class ServeCommand(object): os.dup2(0, 1) # standard output (1) os.dup2(0, 2) # standard error (2) + def _remove_pid_file(self, written_pid, filename, verbosity): + current_pid = os.getpid() + if written_pid != current_pid: + # A forked process must be exiting, not the process that + # wrote the PID file + return + if not os.path.exists(filename): + return + f = open(filename) + content = f.read().strip() + f.close() + try: + pid_in_file = int(content) + except ValueError: + pass + else: + if pid_in_file != current_pid: + self.out("PID file %s contains %s, not expected PID %s" % ( + filename, pid_in_file, current_pid)) + return + if verbosity > 0: + self.out("Removing PID file %s" % filename) + try: + os.unlink(filename) + return + except OSError as e: + # Record, but don't give traceback + self.out("Cannot remove PID file: %s" % e) + # well, at least lets not leave the invalid PID around... + try: + f = open(filename, 'w') + f.write('') + f.close() + except OSError as e: + self.out('Stale PID left in file: %s (%e)' % (filename, e)) + else: + self.out('Stale PID removed') + def record_pid(self, pid_file): pid = os.getpid() if self.verbose > 1: - print('Writing PID %s to %s' % (pid, pid_file)) + self.out('Writing PID %s to %s' % (pid, pid_file)) f = open(pid_file, 'w') f.write(str(pid)) f.close() - atexit.register(_remove_pid_file, pid, pid_file, self.verbose) + atexit.register(self._remove_pid_file, pid, pid_file, self.verbose) def stop_daemon(self): pid_file = self.options.pid_file or 'pyramid.pid' if not os.path.exists(pid_file): - print('No PID file exists in %s' % pid_file) + self.out('No PID file exists in %s' % pid_file) return 1 pid = read_pidfile(pid_file) if not pid: - print("Not a valid PID file in %s" % pid_file) + self.out("Not a valid PID file in %s" % pid_file) return 1 pid = live_pidfile(pid_file) if not pid: - print("PID in %s is not valid (deleting)" % pid_file) + self.out("PID in %s is not valid (deleting)" % pid_file) try: os.unlink(pid_file) except (OSError, IOError) as e: - print("Could not delete: %s" % e) + self.out("Could not delete: %s" % e) return 2 return 1 for j in range(10): @@ -466,7 +489,7 @@ class ServeCommand(object): os.kill(pid, signal.SIGTERM) time.sleep(1) else: - print("failed to kill web process %s" % pid) + self.out("failed to kill web process %s" % pid) return 3 if os.path.exists(pid_file): os.unlink(pid_file) @@ -475,17 +498,17 @@ class ServeCommand(object): def show_status(self): pid_file = self.options.pid_file or 'pyramid.pid' if not os.path.exists(pid_file): - print('No PID file %s' % pid_file) + self.out('No PID file %s' % pid_file) return 1 pid = read_pidfile(pid_file) if not pid: - print('No PID in file %s' % pid_file) + self.out('No PID in file %s' % pid_file) return 1 pid = live_pidfile(pid_file) if not pid: - print('PID %s in %s is not running' % (pid, pid_file)) + self.out('PID %s in %s is not running' % (pid, pid_file)) return 1 - print('Server running in PID %s' % pid) + self.out('Server running in PID %s' % pid) return 0 def restart_with_reloader(self): @@ -494,9 +517,9 @@ class ServeCommand(object): def restart_with_monitor(self, reloader=False): if self.verbose > 0: if reloader: - print('Starting subprocess with file monitor') + self.out('Starting subprocess with file monitor') else: - print('Starting subprocess with monitor parent') + self.out('Starting subprocess with monitor parent') while 1: args = [self.quote_first_command_arg(sys.executable)] + sys.argv new_environ = os.environ.copy() @@ -512,7 +535,7 @@ class ServeCommand(object): exit_code = proc.wait() proc = None except KeyboardInterrupt: - print('^C caught in monitor process') + self.out('^C caught in monitor process') if self.verbose > 1: raise return 1 @@ -531,7 +554,7 @@ class ServeCommand(object): if exit_code != 3: return exit_code if self.verbose > 0: - print('-'*20, 'Restarting', '-'*20) + self.out('%s %s %s' % ('-'*20, 'Restarting', '-'*20)) def change_user_group(self, user, group): if not user and not group: @@ -563,7 +586,7 @@ class ServeCommand(object): gid = entry.pw_gid uid = entry.pw_uid if self.verbose > 0: - print('Changing user to %s:%s (%s:%s)' % ( + self.out('Changing user to %s:%s (%s:%s)' % ( user, group or '(unknown)', uid, gid)) if gid: os.setgid(gid) @@ -634,45 +657,6 @@ def read_pidfile(filename): else: return None -def _remove_pid_file(written_pid, filename, verbosity): - current_pid = os.getpid() - if written_pid != current_pid: - # A forked process must be exiting, not the process that - # wrote the PID file - return - if not os.path.exists(filename): - return - f = open(filename) - content = f.read().strip() - f.close() - try: - pid_in_file = int(content) - except ValueError: - pass - else: - if pid_in_file != current_pid: - print("PID file %s contains %s, not expected PID %s" % ( - filename, pid_in_file, current_pid)) - return - if verbosity > 0: - print("Removing PID file %s" % filename) - try: - os.unlink(filename) - return - except OSError as e: - # Record, but don't give traceback - print("Cannot remove PID file: %s" % e) - # well, at least lets not leave the invalid PID around... - try: - f = open(filename, 'w') - f.write('') - f.close() - except OSError as e: - print('Stale PID left in file: %s (%e)' % (filename, e)) - else: - print('Stale PID removed') - - def ensure_port_cleanup(bound_addresses, maxtries=30, sleeptime=2): """ This makes sure any open ports are closed. @@ -680,7 +664,6 @@ def ensure_port_cleanup(bound_addresses, maxtries=30, sleeptime=2): Does this by connecting to them until they give connection refused. Servers should call like:: - import paste.script ensure_port_cleanup([80, 443]) """ atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries, @@ -717,6 +700,181 @@ def _turn_sigterm_into_systemexit(): raise SystemExit signal.signal(signal.SIGTERM, handle_term) +def install_reloader(poll_interval=1): + """ + Install the reloading monitor. + + On some platforms server threads may not terminate when the main + thread does, causing ports to remain open/locked. The + ``raise_keyboard_interrupt`` option creates a unignorable signal + which causes the whole application to shut-down (rudely). + """ + mon = Monitor(poll_interval=poll_interval) + t = threading.Thread(target=mon.periodic_reload) + t.setDaemon(True) + t.start() + +class classinstancemethod(object): + """ + Acts like a class method when called from a class, like an + instance method when called by an instance. The method should + take two arguments, 'self' and 'cls'; one of these will be None + depending on how the method was called. + """ + + def __init__(self, func): + self.func = func + self.__doc__ = func.__doc__ + + def __get__(self, obj, type=None): + return _methodwrapper(self.func, obj=obj, type=type) + +class _methodwrapper(object): + + def __init__(self, func, obj, type): + self.func = func + self.obj = obj + self.type = type + + def __call__(self, *args, **kw): + assert not kw.has_key('self') and not kw.has_key('cls'), ( + "You cannot use 'self' or 'cls' arguments to a " + "classinstancemethod") + return self.func(*((self.obj, self.type) + args), **kw) + + def __repr__(self): + if self.obj is None: + return ('' + % (self.type.__name__, self.func.func_name)) + else: + return ('' + % (self.type.__name__, self.func.func_name, self.obj)) + + +class Monitor(object): + """ + A file monitor and server restarter. + + Use this like: + + ..code-block:: Python + + install_reloader() + + Then make sure your server is installed with a shell script like:: + + err=3 + while test "$err" -eq 3 ; do + python server.py + err="$?" + done + + or is run from this .bat file (if you use Windows):: + + @echo off + :repeat + python server.py + if %errorlevel% == 3 goto repeat + + or run a monitoring process in Python (``pserve --reload`` does + this). + + Use the ``watch_file(filename)`` function to cause a reload/restart for + other other non-Python files (e.g., configuration files). If you have + a dynamic set of files that grows over time you can use something like:: + + def watch_config_files(): + return CONFIG_FILE_CACHE.keys() + add_file_callback(watch_config_files) + + Then every time the reloader polls files it will call + ``watch_config_files`` and check all the filenames it returns. + """ + instances = [] + global_extra_files = [] + global_file_callbacks = [] + + def __init__(self, poll_interval): + self.module_mtimes = {} + self.keep_running = True + self.poll_interval = poll_interval + self.extra_files = list(self.global_extra_files) + self.instances.append(self) + self.file_callbacks = list(self.global_file_callbacks) + + def periodic_reload(self): + while True: + if not self.check_reload(): + # use os._exit() here and not sys.exit() since within a + # thread sys.exit() just closes the given thread and + # won't kill the process; note os._exit does not call + # any atexit callbacks, nor does it do finally blocks, + # flush open files, etc. In otherwords, it is rude. + os._exit(3) + break + time.sleep(self.poll_interval) + + def check_reload(self): + filenames = list(self.extra_files) + for file_callback in self.file_callbacks: + try: + filenames.extend(file_callback()) + except: + print( + "Error calling reloader callback %r:" % file_callback) + traceback.print_exc() + for module in sys.modules.values(): + try: + filename = module.__file__ + except (AttributeError, ImportError): + continue + if filename is not None: + filenames.append(filename) + for filename in filenames: + try: + stat = os.stat(filename) + if stat: + mtime = stat.st_mtime + else: + mtime = 0 + except (OSError, IOError): + continue + if filename.endswith('.pyc') and os.path.exists(filename[:-1]): + mtime = max(os.stat(filename[:-1]).st_mtime, mtime) + if not self.module_mtimes.has_key(filename): + self.module_mtimes[filename] = mtime + elif self.module_mtimes[filename] < mtime: + print("%s changed; reloading..." % filename) + return False + return True + + def watch_file(self, cls, filename): + """Watch the named file for changes""" + filename = os.path.abspath(filename) + if self is None: + for instance in cls.instances: + instance.watch_file(filename) + cls.global_extra_files.append(filename) + else: + self.extra_files.append(filename) + + watch_file = classinstancemethod(watch_file) + + def add_file_callback(self, cls, callback): + """Add a callback -- a function that takes no parameters -- that will + return a list of filenames to watch for changes.""" + if self is None: + for instance in cls.instances: + instance.add_file_callback(callback) + cls.global_file_callbacks.append(callback) + else: + self.file_callbacks.append(callback) + + add_file_callback = classinstancemethod(add_file_callback) + +watch_file = Monitor.watch_file +add_file_callback = Monitor.add_file_callback + # For paste.deploy server instantiation (egg:pyramid#wsgiref) def wsgiref_server_runner(wsgi_app, global_conf, **kw): from wsgiref.simple_server import make_server diff --git a/pyramid/tests/test_scripts/test_pserve.py b/pyramid/tests/test_scripts/test_pserve.py new file mode 100644 index 000000000..bc2e9aac8 --- /dev/null +++ b/pyramid/tests/test_scripts/test_pserve.py @@ -0,0 +1,27 @@ +import unittest + +class TestPServeCommand(unittest.TestCase): + def setUp(self): + from pyramid.compat import NativeIO + self.out_ = NativeIO() + + def out(self, msg): + self.out_.write(msg) + + def _getTargetClass(self): + from pyramid.scripts.pserve import PServeCommand + return PServeCommand + + def _makeOne(self, *args): + effargs = ['pserve'] + effargs.extend(args) + cmd = self._getTargetClass()(effargs) + cmd.out = self.out + return cmd + + def test_no_args(self): + inst = self._makeOne() + result = inst.run() + self.assertEqual(result, None) + self.assertEqual(self.out_.getvalue(), 'You must give a config file') + -- cgit v1.2.3 From 0776832c2ec237012699c290c3f247d15535d351 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 01:09:18 -0400 Subject: make --reload work under py3 --- pyramid/scripts/pserve.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index b9d2d944f..01fb6837b 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -737,7 +737,7 @@ class _methodwrapper(object): self.type = type def __call__(self, *args, **kw): - assert not kw.has_key('self') and not kw.has_key('cls'), ( + assert not 'self' in kw and not 'cls' in kw, ( "You cannot use 'self' or 'cls' arguments to a " "classinstancemethod") return self.func(*((self.obj, self.type) + args), **kw) @@ -841,7 +841,7 @@ class Monitor(object): continue if filename.endswith('.pyc') and os.path.exists(filename[:-1]): mtime = max(os.stat(filename[:-1]).st_mtime, mtime) - if not self.module_mtimes.has_key(filename): + if not filename in self.module_mtimes: self.module_mtimes[filename] = mtime elif self.module_mtimes[filename] < mtime: print("%s changed; reloading..." % filename) -- cgit v1.2.3 From 1608656d9604bbd44fe0809d9e1f7fcb5327c267 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 01:32:22 -0400 Subject: move some common code into common; decide what to cover and what not to cover in pserve --- pyramid/scripts/pserve.py | 60 +++++++++++++++++------------------------------ pyramid/scripts/pshell.py | 20 +++------------- pyramid/testing.py | 6 ++--- 3 files changed, 27 insertions(+), 59 deletions(-) diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index 01fb6837b..de5276a9f 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -20,12 +20,10 @@ import threading import time import traceback -from logging.config import fileConfig - -from pyramid.compat import configparser - from paste.deploy import loadapp, loadserver +from pyramid.scripts.common import logging_file_config + MAXFD = 1024 def main(argv=sys.argv): @@ -285,7 +283,7 @@ class PServeCommand(object): log_fn = None if log_fn: log_fn = os.path.join(base, log_fn) - self.logging_file_config(log_fn) + logging_file_config(log_fn) server = self.loadserver(server_spec, name=server_name, relative_to=base, global_conf=vars) @@ -315,13 +313,10 @@ class PServeCommand(object): def loadserver(self, server_spec, name, relative_to, **kw): return loadserver( - server_spec, name=name, - relative_to=relative_to, **kw) + server_spec, name=name, relative_to=relative_to, **kw) def loadapp(self, app_spec, name, relative_to, **kw): - return loadapp( - app_spec, name=name, relative_to=relative_to, - **kw) + return loadapp(app_spec, name=name, relative_to=relative_to, **kw) def parse_vars(self, args): """ @@ -338,21 +333,6 @@ class PServeCommand(object): result[name] = value return result - def logging_file_config(self, config_file): - """ - Setup logging via the logging module's fileConfig function with the - specified ``config_file``, if applicable. - - ConfigParser defaults are specified for the special ``__file__`` - and ``here`` variables, similar to PasteDeploy config loading. - """ - parser = configparser.ConfigParser() - parser.read([config_file]) - if parser.has_section('loggers'): - config_file = os.path.abspath(config_file) - fileConfig(config_file, dict(__file__=config_file, - here=os.path.dirname(config_file))) - def quote_first_command_arg(self, arg): """ There's a bug in Windows when running an executable that's @@ -373,7 +353,7 @@ class PServeCommand(object): arg = win32api.GetShortPathName(arg) return arg - def daemonize(self): + def daemonize(self): # pragma: no cover (nfw) pid = live_pidfile(self.options.pid_file) if pid: raise DaemonizeException( @@ -455,7 +435,7 @@ class PServeCommand(object): else: self.out('Stale PID removed') - def record_pid(self, pid_file): + def record_pid(self, pid_file): # pragma: no cover (nfw) pid = os.getpid() if self.verbose > 1: self.out('Writing PID %s to %s' % (pid, pid_file)) @@ -464,7 +444,7 @@ class PServeCommand(object): f.close() atexit.register(self._remove_pid_file, pid, pid_file, self.verbose) - def stop_daemon(self): + def stop_daemon(self): # pragma: no cover (nfw) pid_file = self.options.pid_file or 'pyramid.pid' if not os.path.exists(pid_file): self.out('No PID file exists in %s' % pid_file) @@ -511,10 +491,10 @@ class PServeCommand(object): self.out('Server running in PID %s' % pid) return 0 - def restart_with_reloader(self): + def restart_with_reloader(self): # pragma: no cover (nfw) self.restart_with_monitor(reloader=True) - def restart_with_monitor(self, reloader=False): + def restart_with_monitor(self, reloader=False): # pragma: no cover (nfw) if self.verbose > 0: if reloader: self.out('Starting subprocess with file monitor') @@ -556,7 +536,7 @@ class PServeCommand(object): if self.verbose > 0: self.out('%s %s %s' % ('-'*20, 'Restarting', '-'*20)) - def change_user_group(self, user, group): + def change_user_group(self, user, group): # pragma: no cover (nfw) if not user and not group: return import pwd, grp @@ -629,7 +609,7 @@ class LazyWriter(object): def flush(self): self.open().flush() -def live_pidfile(pidfile): +def live_pidfile(pidfile): # pragma: no cover (nfw) """(pidfile:str) -> int | None Returns an int found in the named file, if there is one, and if there is a running process with that process id. @@ -657,7 +637,8 @@ def read_pidfile(filename): else: return None -def ensure_port_cleanup(bound_addresses, maxtries=30, sleeptime=2): +def ensure_port_cleanup( + bound_addresses, maxtries=30, sleeptime=2): # pragma: no cover (nfw) """ This makes sure any open ports are closed. @@ -669,7 +650,8 @@ def ensure_port_cleanup(bound_addresses, maxtries=30, sleeptime=2): atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries, sleeptime=sleeptime) -def _cleanup_ports(bound_addresses, maxtries=30, sleeptime=2): +def _cleanup_ports( + bound_addresses, maxtries=30, sleeptime=2): # pragma: no cover (nfw) # Wait for the server to bind to the port. import socket import errno @@ -688,7 +670,7 @@ def _cleanup_ports(bound_addresses, maxtries=30, sleeptime=2): raise SystemExit('Timeout waiting for port.') sock.close() -def _turn_sigterm_into_systemexit(): +def _turn_sigterm_into_systemexit(): # pragma: no cover (nfw) """ Attempts to turn a SIGTERM exception into a SystemExit exception. """ @@ -700,7 +682,7 @@ def _turn_sigterm_into_systemexit(): raise SystemExit signal.signal(signal.SIGTERM, handle_term) -def install_reloader(poll_interval=1): +def install_reloader(poll_interval=1): # pragma: no cover (nfw) """ Install the reloading monitor. @@ -802,9 +784,9 @@ class Monitor(object): self.instances.append(self) self.file_callbacks = list(self.global_file_callbacks) - def periodic_reload(self): + def periodic_reload(self): # pragma: no cover (nfw) while True: - if not self.check_reload(): + if not self.check_reload(): # use os._exit() here and not sys.exit() since within a # thread sys.exit() just closes the given thread and # won't kill the process; note os._exit does not call @@ -876,7 +858,7 @@ watch_file = Monitor.watch_file add_file_callback = Monitor.add_file_callback # For paste.deploy server instantiation (egg:pyramid#wsgiref) -def wsgiref_server_runner(wsgi_app, global_conf, **kw): +def wsgiref_server_runner(wsgi_app, global_conf, **kw): # pragma: no cover from wsgiref.simple_server import make_server host = kw.get('host', '0.0.0.0') port = int(kw.get('port', 8080)) diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 12215bedb..d6bf03163 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -1,13 +1,13 @@ from code import interact import optparse -import os import sys -from logging.config import fileConfig from pyramid.compat import configparser from pyramid.util import DottedNameResolver from pyramid.paster import bootstrap +from pyramid.scripts.common import logging_file_config + def main(argv=sys.argv): command = PShellCommand(argv) return command.run() @@ -81,7 +81,7 @@ class PShellCommand(object): def run(self, shell=None): config_uri = self.args[0] config_file = config_uri.split('#', 1)[0] - self.logging_file_config(config_file) + logging_file_config(config_file) self.pshell_file_config(config_file) # bootstrap the environ @@ -184,17 +184,3 @@ class PShellCommand(object): IPShell() return shell - def logging_file_config(self, config_file): - """ - Setup logging via the logging module's fileConfig function with the - specified ``config_file``, if applicable. - - ConfigParser defaults are specified for the special ``__file__`` - and ``here`` variables, similar to PasteDeploy config loading. - """ - parser = configparser.ConfigParser() - parser.read([config_file]) - if parser.has_section('loggers'): - config_file = os.path.abspath(config_file) - fileConfig(config_file, dict(__file__=config_file, - here=os.path.dirname(config_file))) diff --git a/pyramid/testing.py b/pyramid/testing.py index 148575782..e1011f5b4 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -897,14 +897,14 @@ class MockTemplate(object): self._received.update(kw) return self.response -def skip_on(*platforms): +def skip_on(*platforms): # pragma: no cover skip = False for platform in platforms: if skip_on.os_name.startswith(platform): skip = True - if platform == 'pypy' and PYPY: # pragma: no cover + if platform == 'pypy' and PYPY: skip = True - if platform == 'py3' and PY3: # pragma: no cover + if platform == 'py3' and PY3: skip = True def decorator(func): if isinstance(func, class_types): -- cgit v1.2.3 From d29151abecd85e844b170fb2880dc701b63d7f52 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 01:47:27 -0400 Subject: coverage, better error messages --- pyramid/scripts/common.py | 20 ++++++++++++++++++++ pyramid/scripts/pcreate.py | 13 +++++-------- pyramid/scripts/proutes.py | 15 ++++++++++----- pyramid/scripts/pshell.py | 14 +++++++++++--- pyramid/scripts/ptweens.py | 13 +++++++++---- pyramid/scripts/pviews.py | 13 +++++++++---- pyramid/tests/test_scripts/test_pcreate.py | 4 ++-- pyramid/tests/test_scripts/test_proutes.py | 9 +++++++++ pyramid/tests/test_scripts/test_pshell.py | 10 ++++++++++ pyramid/tests/test_scripts/test_ptweens.py | 8 ++++++++ pyramid/tests/test_scripts/test_pviews.py | 8 ++++++++ 11 files changed, 101 insertions(+), 26 deletions(-) create mode 100644 pyramid/scripts/common.py diff --git a/pyramid/scripts/common.py b/pyramid/scripts/common.py new file mode 100644 index 000000000..bbf734bd2 --- /dev/null +++ b/pyramid/scripts/common.py @@ -0,0 +1,20 @@ +import os +from pyramid.compat import configparser +from logging.config import fileConfig + +def logging_file_config(config_file, fileConfig=fileConfig): + """ + Setup logging via the logging module's fileConfig function with the + specified ``config_file``, if applicable. + + ConfigParser defaults are specified for the special ``__file__`` + and ``here`` variables, similar to PasteDeploy config loading. + """ + parser = configparser.ConfigParser() + parser.read([config_file]) + if parser.has_section('loggers'): + config_file = os.path.abspath(config_file) + fileConfig( + config_file, + dict(__file__=config_file, here=os.path.dirname(config_file)) + ) diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py index effb54734..dfd9f691b 100644 --- a/pyramid/scripts/pcreate.py +++ b/pyramid/scripts/pcreate.py @@ -10,8 +10,8 @@ import sys _bad_chars_re = re.compile('[^a-zA-Z0-9_]') -def main(argv=sys.argv): - command = PCreateCommand(argv) +def main(argv=sys.argv, quiet=False): + command = PCreateCommand(argv, quiet) return command.run() class PCreateCommand(object): @@ -37,12 +37,9 @@ class PCreateCommand(object): dest='overwrite', action='store_true', help='Always overwrite') - parser.add_option('-q', '--quiet', - dest='quiet', - action='store_true', - help='Dont emit any output') - def __init__(self, argv): + def __init__(self, argv, quiet=False): + self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) self.scaffolds = self.all_scaffolds() @@ -108,7 +105,7 @@ class PCreateCommand(object): return scaffolds def out(self, msg): # pragma: no cover - if not self.options.quiet: + if not self.quiet: print(msg) diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py index 0dab9acda..fb1cf2059 100644 --- a/pyramid/scripts/proutes.py +++ b/pyramid/scripts/proutes.py @@ -4,8 +4,8 @@ import sys from pyramid.compat import print_ from pyramid.paster import bootstrap -def main(argv=sys.argv): - command = PRoutesCommand(argv) +def main(argv=sys.argv, quiet=False): + command = PRoutesCommand(argv, quiet) command.run() class PRoutesCommand(object): @@ -33,8 +33,9 @@ class PRoutesCommand(object): parser = optparse.OptionParser() - def __init__(self, argv): + def __init__(self, argv, quiet=False): self.options, self.args = self.parser.parse_args(argv[1:]) + self.quiet = quiet def _get_mapper(self, registry): from pyramid.config import Configurator @@ -42,9 +43,13 @@ class PRoutesCommand(object): return config.get_routes_mapper() def out(self, msg): # pragma: no cover - print_(msg) + if not self.quiet: + print_(msg) - def run(self): + def run(self, quiet=False): + if not self.args: + self.out('requires a config file argument') + return from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IView diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index d6bf03163..40673906b 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -8,8 +8,8 @@ from pyramid.paster import bootstrap from pyramid.scripts.common import logging_file_config -def main(argv=sys.argv): - command = PShellCommand(argv) +def main(argv=sys.argv, quiet=False): + command = PShellCommand(argv, quiet) return command.run() class PShellCommand(object): @@ -56,7 +56,8 @@ class PShellCommand(object): object_help = {} setup = None - def __init__(self, argv): + def __init__(self, argv, quiet=False): + self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def pshell_file_config(self, filename): @@ -78,7 +79,14 @@ class PShellCommand(object): self.loaded_objects[k] = resolver.maybe_resolve(v) self.object_help[k] = v + def out(self, msg): # pragma: no cover + if not self.quiet: + print(msg) + def run(self, shell=None): + if not self.args: + self.out('Requires a config file argument') + return config_uri = self.args[0] config_file = config_uri.split('#', 1)[0] logging_file_config(config_file) diff --git a/pyramid/scripts/ptweens.py b/pyramid/scripts/ptweens.py index 56d8b422b..556b43254 100644 --- a/pyramid/scripts/ptweens.py +++ b/pyramid/scripts/ptweens.py @@ -9,8 +9,8 @@ from pyramid.paster import bootstrap from pyramid.compat import print_ -def main(argv=sys.argv): - command = PTweensCommand(argv) +def main(argv=sys.argv, quiet=False): + command = PTweensCommand(argv, quiet) command.run() class PTweensCommand(object): @@ -40,7 +40,8 @@ class PTweensCommand(object): bootstrap = (bootstrap,) # testing - def __init__(self, argv): + def __init__(self, argv, quiet=False): + self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def _get_tweens(self, registry): @@ -49,7 +50,8 @@ class PTweensCommand(object): return config.registry.queryUtility(ITweens) def out(self, msg): # pragma: no cover - print_(msg) + if not self.quiet: + print_(msg) def show_chain(self, chain): fmt = '%-10s %-65s' @@ -61,6 +63,9 @@ class PTweensCommand(object): self.out(fmt % ('-', MAIN)) def run(self): + if not self.args: + self.out('Requires a config file argument') + return config_uri = self.args[0] env = self.bootstrap[0](config_uri) registry = env['registry'] diff --git a/pyramid/scripts/pviews.py b/pyramid/scripts/pviews.py index 69baaee87..8d3048633 100644 --- a/pyramid/scripts/pviews.py +++ b/pyramid/scripts/pviews.py @@ -5,8 +5,8 @@ from pyramid.compat import print_ from pyramid.interfaces import IMultiView from pyramid.paster import bootstrap -def main(argv=sys.argv): - command = PViewsCommand(argv) +def main(argv=sys.argv, quiet=False): + command = PViewsCommand(argv, quiet) command.run() class PViewsCommand(object): @@ -37,11 +37,13 @@ class PViewsCommand(object): bootstrap = (bootstrap,) # testing - def __init__(self, argv): + def __init__(self, argv, quiet=False): + self.quiet = quiet self.options, self.args = self.parser.parse_args(argv[1:]) def out(self, msg): # pragma: no cover - print_(msg) + if not self.quiet: + print_(msg) def _find_multi_routes(self, mapper, request): infos = [] @@ -230,6 +232,9 @@ class PViewsCommand(object): self.out("%sview predicates (%s)" % (indent, predicate_text)) def run(self): + if len(self.args) < 2: + self.out('Command requires a config file arg and a url arg') + return config_uri, url = self.args if not url.startswith('/'): url = '/%s' % url diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py index 346b35fda..cdd0daf2e 100644 --- a/pyramid/tests/test_scripts/test_pcreate.py +++ b/pyramid/tests/test_scripts/test_pcreate.py @@ -97,10 +97,10 @@ class TestPCreateCommand(unittest.TestCase): class Test_main(unittest.TestCase): def _callFUT(self, argv): from pyramid.scripts.pcreate import main - return main(argv) + return main(argv, quiet=True) def test_it(self): - result = self._callFUT(['pcreate', '-q']) + result = self._callFUT(['pcreate']) self.assertEqual(result, None) class DummyScaffold(object): diff --git a/pyramid/tests/test_scripts/test_proutes.py b/pyramid/tests/test_scripts/test_proutes.py index a8b577316..af6ff19d0 100644 --- a/pyramid/tests/test_scripts/test_proutes.py +++ b/pyramid/tests/test_scripts/test_proutes.py @@ -127,3 +127,12 @@ class TestPRoutesCommand(unittest.TestCase): result = command._get_mapper(registry) self.assertEqual(result.__class__, RoutesMapper) +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.proutes import main + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['proutes']) + self.assertEqual(result, None) + diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index b0ea8c5ea..e38da2077 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -247,3 +247,13 @@ class TestPShellCommand(unittest.TestCase): self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) + +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.pshell import main + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['pshell']) + self.assertEqual(result, None) + diff --git a/pyramid/tests/test_scripts/test_ptweens.py b/pyramid/tests/test_scripts/test_ptweens.py index 4ee38dd85..4dddaa0aa 100644 --- a/pyramid/tests/test_scripts/test_ptweens.py +++ b/pyramid/tests/test_scripts/test_ptweens.py @@ -51,3 +51,11 @@ class TestPTweensCommand(unittest.TestCase): registry = dummy.DummyRegistry() self.assertEqual(command._get_tweens(registry), None) +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.ptweens import main + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['ptweens']) + self.assertEqual(result, None) diff --git a/pyramid/tests/test_scripts/test_pviews.py b/pyramid/tests/test_scripts/test_pviews.py index 473ba5598..e2c3892fa 100644 --- a/pyramid/tests/test_scripts/test_pviews.py +++ b/pyramid/tests/test_scripts/test_pviews.py @@ -465,3 +465,11 @@ class TestPViewsCommand(unittest.TestCase): ' pyramid.tests.test_scripts.dummy.view.call') self.assertEqual(L[9], ' view predicates (predicate = x)') +class Test_main(unittest.TestCase): + def _callFUT(self, argv): + from pyramid.scripts.pviews import main + return main(argv, quiet=True) + + def test_it(self): + result = self._callFUT(['pviews']) + self.assertEqual(result, None) -- cgit v1.2.3 From cfb2b5596b8ef366aeef3bce5b61eafc7a2f175d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 03:05:29 -0400 Subject: remove all reference to the paster command-line utility --- docs/glossary.rst | 4 +- docs/narr/MyProject/setup.py | 1 - docs/narr/commandline.rst | 70 ++++---- docs/narr/hooks.rst | 9 +- docs/narr/introduction.rst | 10 +- docs/narr/logging.rst | 8 +- docs/narr/paste.rst | 45 +++-- docs/narr/project.rst | 200 +++++++++------------ docs/narr/security.rst | 2 +- docs/narr/startup.rst | 41 +++-- docs/narr/templates.rst | 4 +- docs/narr/urldispatch.rst | 10 +- docs/tutorials/gae/index.rst | 2 +- docs/tutorials/modwsgi/index.rst | 2 +- docs/tutorials/wiki/basiclayout.rst | 6 +- docs/tutorials/wiki/definingmodels.rst | 2 +- docs/tutorials/wiki/definingviews.rst | 2 +- docs/tutorials/wiki/installation.rst | 16 +- .../wiki/src/authorization/development.ini | 2 +- .../wiki/src/authorization/production.ini | 2 +- docs/tutorials/wiki/src/authorization/setup.py | 1 - .../wiki/src/authorization/tutorial/__init__.py | 3 - .../tutorials/wiki/src/basiclayout/development.ini | 2 +- docs/tutorials/wiki/src/basiclayout/production.ini | 2 +- docs/tutorials/wiki/src/basiclayout/setup.py | 1 - docs/tutorials/wiki/src/models/development.ini | 2 +- docs/tutorials/wiki/src/models/production.ini | 2 +- docs/tutorials/wiki/src/models/setup.py | 1 - docs/tutorials/wiki/src/tests/development.ini | 2 +- docs/tutorials/wiki/src/tests/production.ini | 2 +- docs/tutorials/wiki/src/tests/setup.py | 1 - docs/tutorials/wiki/src/tests/tutorial/__init__.py | 3 - docs/tutorials/wiki/src/views/development.ini | 2 +- docs/tutorials/wiki/src/views/production.ini | 2 +- docs/tutorials/wiki/src/views/setup.py | 1 - docs/tutorials/wiki2/basiclayout.rst | 2 +- docs/tutorials/wiki2/definingmodels.rst | 8 +- docs/tutorials/wiki2/definingviews.rst | 2 +- docs/tutorials/wiki2/installation.rst | 8 +- .../wiki2/src/authorization/development.ini | 2 +- .../wiki2/src/authorization/production.ini | 2 +- docs/tutorials/wiki2/src/authorization/setup.py | 1 - .../wiki2/src/basiclayout/development.ini | 2 +- .../tutorials/wiki2/src/basiclayout/production.ini | 2 +- docs/tutorials/wiki2/src/basiclayout/setup.py | 1 - docs/tutorials/wiki2/src/models/development.ini | 2 +- docs/tutorials/wiki2/src/models/production.ini | 2 +- docs/tutorials/wiki2/src/models/setup.py | 1 - docs/tutorials/wiki2/src/tests/development.ini | 2 +- docs/tutorials/wiki2/src/tests/production.ini | 2 +- docs/tutorials/wiki2/src/tests/setup.py | 1 - docs/tutorials/wiki2/src/views/development.ini | 2 +- docs/tutorials/wiki2/src/views/production.ini | 2 +- docs/tutorials/wiki2/src/views/setup.py | 1 - pyramid/config/__init__.py | 2 +- pyramid/config/tweens.py | 4 +- pyramid/scaffolds/alchemy/setup.py_tmpl | 1 - pyramid/scaffolds/routesalchemy/setup.py_tmpl | 1 - pyramid/scaffolds/starter/setup.py_tmpl | 1 - pyramid/scaffolds/tests.py | 4 +- pyramid/scaffolds/zodb/setup.py_tmpl | 1 - 61 files changed, 231 insertions(+), 293 deletions(-) diff --git a/docs/glossary.rst b/docs/glossary.rst index c5452401c..472e591a9 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -930,8 +930,8 @@ Glossary scaffold A project template that helps users get started writing a Pyramid - application quickly. Scaffolds are usually used via the ``paster - create`` command. + application quickly. Scaffolds are usually used via the ``pcreate`` + command. pyramid_exclog A package which logs Pyramid application exception (error) information diff --git a/docs/narr/MyProject/setup.py b/docs/narr/MyProject/setup.py index 5203fc2a7..74879f65d 100644 --- a/docs/narr/MyProject/setup.py +++ b/docs/narr/MyProject/setup.py @@ -32,6 +32,5 @@ setup(name='MyProject', [paste.app_factory] main = myproject:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst index a8459ac27..0dc41e919 100644 --- a/docs/narr/commandline.rst +++ b/docs/narr/commandline.rst @@ -9,7 +9,7 @@ chapter. .. index:: pair: matching views; printing - single: paster pviews + single: pviews .. _displaying_matching_views: @@ -18,9 +18,9 @@ Displaying Matching Views for a Given URL For a big application with several views, it can be hard to keep the view configuration details in your head, even if you defined all the views -yourself. You can use the ``paster pviews`` command in a terminal window to +yourself. You can use the ``pviews`` command in a terminal window to print a summary of matching routes and views for a given URL in your -application. The ``paster pviews`` command accepts two arguments. The first +application. The ``pviews`` command accepts two arguments. The first argument to ``pviews`` is the path to your application's ``.ini`` file and section name inside the ``.ini`` file which points to your application. This should be of the format ``config_file#section_name``. The second argument is @@ -32,7 +32,7 @@ Here is an example for a simple view configuration using :term:`traversal`: .. code-block:: text :linenos: - $ ../bin/paster pviews development.ini#tutorial /FrontPage + $ ../bin/pviews development.ini#tutorial /FrontPage URL = /FrontPage @@ -56,7 +56,7 @@ A more complex configuration might generate something like this: .. code-block:: text :linenos: - $ ../bin/paster pviews development.ini#shootout /about + $ ../bin/pviews development.ini#shootout /about URL = /about @@ -103,14 +103,13 @@ displayed first, followed by any views that are associated with that route. As you can see from the second matching route output, a route can be associated with more than one view. -For a URL that doesn't match any views, ``paster pviews`` will simply print -out a *Not found* message. +For a URL that doesn't match any views, ``pviews`` will simply print out a +*Not found* message. .. index:: single: interactive shell single: IPython - single: paster pshell single: pshell .. _interactive_shell: @@ -121,7 +120,7 @@ The Interactive Shell Once you've installed your program for development using ``setup.py develop``, you can use an interactive Python shell to execute expressions in a Python environment exactly like the one that will be used when your -application runs "for real". To do so, use the ``paster pshell`` command. +application runs "for real". To do so, use the ``pshell`` command. The argument to ``pshell`` follows the format ``config_file#section_name`` where ``config_file`` is the path to your application's ``.ini`` file and @@ -145,7 +144,7 @@ name ``MyProject`` as a section name: .. code-block:: text - chrism@thinko env26]$ bin/paster pshell starter/development.ini#MyProject + chrism@thinko env26]$ bin/pshell starter/development.ini#MyProject Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32) [GCC 4.4.3] on linux2 Type "help" for more information. @@ -180,7 +179,7 @@ hash after the filename: .. code-block:: text - chrism@thinko env26]$ bin/paster pshell starter/development.ini + chrism@thinko env26]$ bin/pshell starter/development.ini Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows). @@ -243,7 +242,7 @@ exposed, and the request is configured to generate urls from the host .. code-block:: text - chrism@thinko env26]$ bin/paster pshell starter/development.ini + chrism@thinko env26]$ bin/pshell starter/development.ini Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32) [GCC 4.4.3] on linux2 Type "help" for more information. @@ -273,22 +272,20 @@ IPython ~~~~~~~ If you have `IPython `_ installed in -the interpreter you use to invoke the ``paster`` command, the ``pshell`` -command will use an IPython interactive shell instead of a standard Python -interpreter shell. If you don't want this to happen, even if you have -IPython installed, you can pass the ``--disable-ipython`` flag to the -``pshell`` command to use a standard Python interpreter shell -unconditionally. +the interpreter you use to invoke the ``pshell`` command, ``pshell`` will use +an IPython interactive shell instead of a standard Python interpreter shell. +If you don't want this to happen, even if you have IPython installed, you can +pass the ``--disable-ipython`` flag to the ``pshell`` command to use a +standard Python interpreter shell unconditionally. .. code-block:: text - [chrism@vitaminf shellenv]$ ../bin/paster pshell --disable-ipython \ + [chrism@vitaminf shellenv]$ ../bin/pshell --disable-ipython \ development.ini#MyProject .. index:: pair: routes; printing - single: paster proutes single: proutes .. _displaying_application_routes: @@ -296,11 +293,11 @@ unconditionally. Displaying All Application Routes --------------------------------- -You can use the ``paster proutes`` command in a terminal window to print a -summary of routes related to your application. Much like the ``paster -pshell`` command (see :ref:`interactive_shell`), the ``paster proutes`` -command accepts one argument with the format ``config_file#section_name``. -The ``config_file`` is the path to your application's ``.ini`` file, and +You can use the ``proutes`` command in a terminal window to print a summary +of routes related to your application. Much like the ``pshell`` +command (see :ref:`interactive_shell`), the ``proutes`` command +accepts one argument with the format ``config_file#section_name``. The +``config_file`` is the path to your application's ``.ini`` file, and ``section_name`` is the ``app`` section name inside the ``.ini`` file which points to your application. By default, the ``section_name`` is ``main`` and can be omitted. @@ -310,7 +307,7 @@ For example: .. code-block:: text :linenos: - [chrism@thinko MyProject]$ ../bin/paster proutes development.ini#MyProject + [chrism@thinko MyProject]$ ../bin/proutes development.ini#MyProject Name Pattern View ---- ------- ---- home / @@ -319,19 +316,18 @@ For example: static/ static/*subpath catchall /*subpath -``paster proutes`` generates a table. The table has three columns: a Name +``proutes`` generates a table. The table has three columns: a Name column, a Pattern column, and a View column. The items listed in the Name column are route names, the items listed in the Pattern column are route patterns, and the items listed in the View column are representations of the view callable that will be invoked when a request matches the associated route pattern. The view column may show ``None`` if no associated view callable could be found. If no routes are configured within your -application, nothing will be printed to the console when ``paster proutes`` +application, nothing will be printed to the console when ``proutes`` is executed. .. index:: pair: tweens; printing - single: paster ptweens single: ptweens .. _displaying_tweens: @@ -344,17 +340,17 @@ application request handler and the WSGI application which calls it. A user can get a representation of both the implicit tween ordering (the ordering specified by calls to :meth:`pyramid.config.Configurator.add_tween`) and the explicit tween ordering (specified by the ``pyramid.tweens`` configuration -setting) orderings using the ``paster ptweens`` command. Tween factories +setting) orderings using the ``ptweens`` command. Tween factories will show up represented by their standard Python dotted name in the -``paster ptweens`` output. +``ptweens`` output. -For example, here's the ``paster pwteens`` command run against a system +For example, here's the ``pwteens`` command run against a system configured without any explicit tweens: .. code-block:: text :linenos: - [chrism@thinko pyramid]$ paster ptweens development.ini + [chrism@thinko pyramid]$ ptweens development.ini "pyramid.tweens" config value NOT set (implicitly ordered tweens used) Implicit Tween Chain @@ -366,13 +362,13 @@ configured without any explicit tweens: 1 pyramid.tweens.excview_tween_factory excview - - MAIN -Here's the ``paster pwteens`` command run against a system configured *with* +Here's the ``pwteens`` command run against a system configured *with* explicit tweens defined in its ``development.ini`` file: .. code-block:: text :linenos: - [chrism@thinko pyramid]$ paster ptweens development.ini + [chrism@thinko pyramid]$ ptweens development.ini "pyramid.tweens" config value set (explicitly ordered tweens used) Explicit Tween Chain (used) @@ -395,8 +391,8 @@ explicit tweens defined in its ``development.ini`` file: - - MAIN Here's the application configuration section of the ``development.ini`` used -by the above ``paster ptweens`` command which reprorts that the explicit -tween chain is used: +by the above ``ptweens`` command which reports that the explicit tween chain +is used: .. code-block:: text :linenos: diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 7db1eca73..3c6799afb 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -727,7 +727,7 @@ A user might make use of these framework components like so: from pyramid.response import Response from pyramid.config import Configurator import pyramid_handlers - from paste.httpserver import serve + from wsgiref.simple_server import make_server class MyController(BaseController): def index(self, id): @@ -738,7 +738,8 @@ A user might make use of these framework components like so: config.include(pyramid_handlers) config.add_handler('one', '/{id}', MyController, action='index') config.add_handler('two', '/{action}/{id}', MyController) - serve(config.make_wsgi_app()) + server.make_server('0.0.0.0', 8080, config.make_wsgi_app()) + server.serve_forever() The :meth:`pyramid.config.Configurator.set_view_mapper` method can be used to set a *default* view mapper (overriding the superdefault view mapper used by @@ -1012,7 +1013,7 @@ Effectively, ``under`` means "closer to the main Pyramid application than", For example, the following call to :meth:`~pyramid.config.Configurator.add_tween` will attempt to place the tween factory represented by ``myapp.tween_factory`` directly 'above' (in -``paster ptweens`` order) the main Pyramid request handler. +``ptweens`` order) the main Pyramid request handler. .. code-block:: python :linenos: @@ -1136,6 +1137,6 @@ time. Displaying Tween Ordering ~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``paster ptweens`` command-line utility can be used to report the current +The ``ptweens`` command-line utility can be used to report the current implict and explicit tween chains used by an application. See :ref:`displaying_tweens`. diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 823c1ea13..e30a23a9a 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -202,11 +202,11 @@ turn on "debug_authorization", which lets you know why a view execution was allowed or denied by printing a message to the console. These features are useful for those WTF moments. -There are also a number of ``paster`` commands that allow you to introspect -the configuration of your system: ``paster proutes`` shows all configured -routes for an application in the order they'll be evaluated for matching; -``paster pviews`` shows all configured views for any given URL. These are -also WTF-crushers in some circumstances. +There are also a number of commands that you can invoke within a Pyramid +environment that allow you to introspect the configuration of your system: +``proutes`` shows all configured routes for an application in the order +they'll be evaluated for matching; ``pviews`` shows all configured views for +any given URL. These are also WTF-crushers in some circumstances. Examples: :ref:`debug_authorization_section` and :ref:`command_line_chapter`. diff --git a/docs/narr/logging.rst b/docs/narr/logging.rst index 3e39151a3..b18fefae0 100644 --- a/docs/narr/logging.rst +++ b/docs/narr/logging.rst @@ -32,13 +32,13 @@ format used as the Python `logging module's Configuration file format `_. The application-related and logging-related sections in the configuration file can coexist peacefully, and the logging-related sections in the file are used -from when you run ``paster serve``. +from when you run ``pserve``. -The ``paster serve`` command calls the `logging.fileConfig function +The ``pserve`` command calls the `logging.fileConfig function `_ using the specified ini file if it contains a ``[loggers]`` section (all of the scaffold-generated ``.ini`` files do). ``logging.fileConfig`` reads the -logging configuration from the ini file upon which ``paster serve`` was +logging configuration from the ini file upon which ``pserve`` was invoked. Default logging configuration is provided in both the default @@ -89,7 +89,7 @@ project. For instance, if you do: .. code-block:: text :linenos: - paster create -t pyramid_starter MyApp + pcreate -s starter MyApp The logging configuration will literally be: diff --git a/docs/narr/paste.rst b/docs/narr/paste.rst index 39ae4f373..5c7d4c3fb 100644 --- a/docs/narr/paste.rst +++ b/docs/narr/paste.rst @@ -1,26 +1,25 @@ .. _paste_chapter: -Paste -===== +PasteDeploy Configuration Files +=============================== Packages generated via a :term:`scaffold` make use of a system created by Ian -Bicking named :term:`Paste`. Paste provides the following features: - -- A way to declare :term:`WSGI` application configuration in an ``.ini`` file - (PasteDeploy). - -- A :term:`WSGI` server runner (``paster serve``) which can accept - PasteDeploy ``.ini`` file values as input. - -- A mechanism for rendering scaffolds into projects (``paster create``). - -Paste is not a particularly integral part of Pyramid. It's more or less used -directly only in projects created from scaffolds. It's possible to create a -Pyramid application which does not use Paste at all. We show a Pyramid -application that doesn't use Paste in :ref:`firstapp_chapter`. However, all -Pyramid scaffolds use the system, to provide new developers with a -standardized way of starting, stopping, and setting deployment values. This -chapter is not a replacement for documentation about Paste or PasteDeploy; it +Bicking named :term:`PasteDeploy`. PasteDeploy defines a way to declare +:term:`WSGI` application configuration in an ``.ini`` file. + +Pyramid uses this configuration file format in input to its :term:`WSGI` +server runner ``pserve``, as well as other commands such as ``pviews``, +``pshell``, ``proutes``, and ``ptweens``. + +PasteDeploy is not a particularly integral part of Pyramid. It's possible to +create a Pyramid application which does not use PasteDeploy at all. We show +a Pyramid application that doesn't use PasteDeploy in +:ref:`firstapp_chapter`. However, all Pyramid scaffolds render PasteDeploy +configuration files, to provide new developers with a standardized way of +setting deployment values, and to provide new users with a standardized way +of starting, stopping, and debugging an application. + +This chapter is not a replacement for documentation about PasteDeploy; it only contextualizes the use of Paste within Pyramid. For detailed documentation, see http://pythonpaste.org. @@ -29,7 +28,7 @@ PasteDeploy :term:`PasteDeploy` is the system that Pyramid uses to allow :term:`deployment settings` to be spelled using an ``.ini`` configuration -file format. It also allows the ``paster serve`` command to work. Its +file format. It also allows the ``pserve`` command to work. Its configuration format provides a convenient place to define application :term:`deployment settings` and WSGI server settings, and its server runner allows you to stop and start a Pyramid application easily. @@ -82,9 +81,9 @@ factory in the ``MyProject`` project which has the entry point named ``main`` where the entry point refers to a ``main`` function in the ``mypackage`` module". Indeed, if you open up the ``__init__.py`` module generated within any scaffold-generated package, you'll see a ``main`` function. This is the -function called by :term:`PasteDeploy` when the ``paster serve`` command is -invoked against our application. It accepts a global configuration object -and *returns* an instance of our application. +function called by :term:`PasteDeploy` when the ``pserve`` command is invoked +against our application. It accepts a global configuration object and +*returns* an instance of our application. ``[DEFAULTS]`` Section of a PasteDeploy ``.ini`` File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 345672204..c961b4143 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -20,15 +20,15 @@ distributed more easily than one which does not live within a package. a project. Each scaffold makes different configuration assumptions about what type of application you're trying to construct. -These scaffolds are rendered using the :term:`PasteDeploy` ``paster create`` -command. +These scaffolds are rendered using the ``pcreate`` command that is installed +as part of Pyramid. .. index:: single: scaffolds - single: pyramid_starter scaffold - single: pyramid_zodb scaffold - single: pyramid_alchemy scaffold - single: pyramid_routesalchemy scaffold + single: starter scaffold + single: zodb scaffold + single: alchemy scaffold + single: routesalchemy scaffold .. _additional_paster_scaffolds: @@ -44,36 +44,24 @@ each other on a number of axes: - the mechanism they use to map URLs to code (:term:`traversal` or :term:`URL dispatch`). -- whether or not the ``pyramid_beaker`` library is relied upon as the - sessioning implementation (as opposed to no sessioning or default - sessioning). - The included scaffolds are these: -``pyramid_starter`` +``starter`` URL mapping via :term:`traversal` and no persistence mechanism. -``pyramid_zodb`` +``zodb`` URL mapping via :term:`traversal` and persistence via :term:`ZODB`. -``pyramid_routesalchemy`` +``routesalchemy`` URL mapping via :term:`URL dispatch` and persistence via :term:`SQLAlchemy` -``pyramid_alchemy`` +``alchemy`` URL mapping via :term:`traversal` and persistence via :term:`SQLAlchemy` .. note:: - At this time, each of these scaffolds uses the :term:`Chameleon` - templating system, which is incompatible with Jython. To use scaffolds to - build applications which will run on Jython, you can try the - ``pyramid_jinja2_starter`` scaffold which ships as part of the - :term:`pyramid_jinja2` package. You can also just use any above scaffold - and replace the Chameleon template it includes with a :term:`Mako` - analogue. - Rather than use any of the above scaffolds, Pylons 1 users may feel more comfortable installing the :term:`Akhet` development environment, which provides a scaffold named ``akhet``. This scaffold configures a Pyramid @@ -91,85 +79,64 @@ Creating the Project In :ref:`installing_chapter`, you created a virtual Python environment via the ``virtualenv`` command. To start a :app:`Pyramid` :term:`project`, use -the ``paster`` facility installed within the virtualenv. In +the ``pcreate`` command installed within the virtualenv. In :ref:`installing_chapter` we called the virtualenv directory ``env``; the following command assumes that our current working directory is that -directory. We'll choose the ``pyramid_starter`` scaffold for this purpose. +directory. We'll choose the ``starter`` scaffold for this purpose. On UNIX: .. code-block:: text - $ bin/paster create -t pyramid_starter + $ bin/pcreate -s starter MyProject Or on Windows: .. code-block:: text - $ Scripts\paster.exe create -t pyramid_starter + $ Scripts\pcreate -s starter MyProject -The above command uses the ``paster create`` command to create a project with -the ``pyramid_starter`` scaffold. To use a different scaffold, such as -``pyramid_routesalchemy``, you'd just change the last argument. For example, +The above command uses the ``pcreate`` command to create a project with the +``starter`` scaffold. To use a different scaffold, such as +``routesalchemy``, you'd just change the ``-s`` argument value. For example, on UNIX: .. code-block:: text - $ bin/paster create -t pyramid_routesalchemy + $ bin/pcreate -s routesalchemy MyProject Or on Windows: .. code-block:: text - $ Scripts\paster.exe create -t pyramid_routesalchemy + $ Scripts\pcreate routesalchemy MyProject -``paster create`` will ask you a single question: the *name* of the project. -You should use a string without spaces and with only letters in it. Here's -sample output from a run of ``paster create`` on UNIX for a project we name +Here's sample output from a run of ``pcreate`` on UNIX for a project we name ``MyProject``: .. code-block:: text - $ bin/paster create -t pyramid_starter - Selected and implied templates: - pyramid#pyramid_starter pyramid starter project - - Enter project name: MyProject - Variables: - egg: MyProject - package: myproject - project: MyProject + $ bin/pcreate -s starter MyProject Creating template pyramid Creating directory ./MyProject # ... more output ... Running /Users/chrism/projects/pyramid/bin/python setup.py egg_info -.. note:: You can skip the interrogative question about a project - name during ``paster create`` by adding the project name to the - command line, e.g. ``paster create -t pyramid_starter MyProject``. - -.. note:: You may encounter an error when using ``paster create`` if a - dependent Python package is not installed. This will result in a traceback - ending in ``pkg_resources.DistributionNotFound: ``. Simply - run ``bin/easy_install`` (or ``Script\easy_install.exe`` on Windows), with - the missing package name from the error message to work around this issue. - -As a result of invoking the ``paster create`` command, a project is created -in a directory named ``MyProject``. That directory is a :term:`project` +As a result of invoking the ``pcreate`` command, a project is created in a +directory named ``MyProject``. That directory is a :term:`project` directory. The ``setup.py`` file in that directory can be used to distribute your application, or install your application for deployment or development. -A :term:`PasteDeploy` ``.ini`` file named ``development.ini`` will be created -in the project directory. You will use this ``.ini`` file to configure a -server, to run your application, and to debug your application. It sports -configuration that enables an interactive debugger and settings optimized for -development. +A ``.ini`` file named ``development.ini`` will be created in the project +directory. You will use this ``.ini`` file to configure a server, to run +your application, and to debug your application. It sports configuration +that enables an interactive debugger and settings optimized for development. -Another :term:`PasteDeploy` ``.ini`` file named ``production.ini`` will also -be created in the project directory. It sports configuration that disables -any interactive debugger (to prevent inappropriate access and disclosure), -and turns off a number of debugging settings. You can use this file to put -your application into production. +Another ``.ini`` file named ``production.ini`` will also be created in the +project directory. It sports configuration that disables any interactive +debugger (to prevent inappropriate access and disclosure), and turns off a +number of debugging settings. You can use this file to put your application +into production. The ``MyProject`` project directory contains an additional subdirectory named ``myproject`` (note the case difference) representing a Python @@ -188,7 +155,7 @@ newly created project directory and use the Python interpreter from the :term:`virtualenv` you created during :ref:`installing_chapter` to invoke the command ``python setup.py develop`` -The file named ``setup.py`` will be in the root of the paster-generated +The file named ``setup.py`` will be in the root of the pcreate-generated project directory. The ``python`` you're invoking should be the one that lives in the ``bin`` (or ``Scripts`` on Windows) directory of your virtual Python environment. Your terminal's current working directory *must* be the @@ -219,8 +186,8 @@ Elided output from a run of this command on UNIX is shown below: This will install a :term:`distribution` representing your project into the interpreter's library set so it can be found by ``import`` statements and by -:term:`PasteDeploy` commands such as ``paster serve``, ``paster pshell``, -``paster proutes`` and ``paster pviews``. +other console scripts such as ``pserve``, ``pshell``, ``proutes`` and +``pviews``. .. index:: single: running tests @@ -273,13 +240,13 @@ Here's sample output from a test run on UNIX: output to a stream of dots. If you don't pass ``-q``, you'll see more verbose test result output (which normally isn't very useful). -The tests themselves are found in the ``tests.py`` module in your ``paster -create`` -generated project. Within a project generated by the -``pyramid_starter`` scaffold, a single sample test exists. +The tests themselves are found in the ``tests.py`` module in your ``pcreate`` +generated project. Within a project generated by the ``starter`` scaffold, a +single sample test exists. .. index:: single: running an application - single: paster serve + single: pserve single: reload single: startup @@ -287,26 +254,26 @@ Running The Project Application ------------------------------- Once a project is installed for development, you can run the application it -represents using the ``paster serve`` command against the generated -configuration file. In our case, this file is named ``development.ini``. +represents using the ``pserve`` command against the generated configuration +file. In our case, this file is named ``development.ini``. On UNIX: .. code-block:: text - $ ../bin/paster serve development.ini + $ ../bin/pserve development.ini On Windows: .. code-block:: text - $ ..\Scripts\paster.exe serve development.ini + $ ..\Scripts\pserve development.ini -Here's sample output from a run of ``paster serve`` on UNIX: +Here's sample output from a run of ``pserve`` on UNIX: .. code-block:: text - $ ../bin/paster serve development.ini + $ ../bin/pserve development.ini Starting server in PID 16601. serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 @@ -314,18 +281,17 @@ By default, :app:`Pyramid` applications generated from a scaffold will listen on TCP port 6543. You can shut down a server started this way by pressing ``Ctrl-C``. -During development, it's often useful to run ``paster serve`` using its -``--reload`` option. When ``--reload`` is passed to ``paster serve``, -changes to any Python module your project uses will cause the server to -restart. This typically makes development easier, as changes to Python code -made within a :app:`Pyramid` application is not put into effect until the -server restarts. +During development, it's often useful to run ``pserve`` using its +``--reload`` option. When ``--reload`` is passed to ``pserve``, changes to +any Python module your project uses will cause the server to restart. This +typically makes development easier, as changes to Python code made within a +:app:`Pyramid` application is not put into effect until the server restarts. For example, on UNIX: .. code-block:: text - $ ../bin/paster serve development.ini --reload + $ ../bin/pserve development.ini --reload Starting subprocess with file monitor Starting server in PID 16601. serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 @@ -341,14 +307,14 @@ configuration file settings that influence startup and runtime behavior, see Viewing the Application ----------------------- -Once your application is running via ``paster serve``, you may visit +Once your application is running via ``pserve``, you may visit ``http://localhost:6543/`` in your browser. You will see something in your browser like what is displayed in the following image: .. image:: project.png -This is the page shown by default when you visit an unmodified ``paster -create`` -generated ``pyramid_starter`` application in a browser. +This is the page shown by default when you visit an unmodified ``pcreate`` +generated ``starter`` application in a browser. .. index:: single: debug toolbar @@ -403,13 +369,12 @@ Then restart the application to see that the toolbar has been turned off. The Project Structure --------------------- -The ``pyramid_starter`` scaffold generated a :term:`project` (named -``MyProject``), which contains a Python :term:`package`. The package is -*also* named ``myproject``, but it's lowercased; the scaffold -generates a project which contains a package that shares its name except for -case. +The ``starter`` scaffold generated a :term:`project` (named ``MyProject``), +which contains a Python :term:`package`. The package is *also* named +``myproject``, but it's lowercased; the scaffold generates a project which +contains a package that shares its name except for case. -All :app:`Pyramid` ``paster`` -generated projects share a similar structure. +All :app:`Pyramid` ``pcreate`` -generated projects share a similar structure. The ``MyProject`` project we've generated has the following directory structure: @@ -475,8 +440,8 @@ describe, run, and test your application. ~~~~~~~~~~~~~~~~~~~ The ``development.ini`` file is a :term:`PasteDeploy` configuration file. -Its purpose is to specify an application to run when you invoke ``paster -serve``, as well as the deployment settings provided to that application. +Its purpose is to specify an application to run when you invoke ``pserve``, +as well as the deployment settings provided to that application. The generated ``development.ini`` file looks like so: @@ -529,9 +494,9 @@ or influencing runtime behavior of a :app:`Pyramid` application. See :ref:`environment_chapter` for more information about these settings. The name ``main`` in ``[app:main]`` signifies that this is the default -application run by ``paster serve`` when it is invoked against this -configuration file. The name ``main`` is a convention used by PasteDeploy -signifying that it is the default application. +application run by ``pserve`` when it is invoked against this configuration +file. The name ``main`` is a convention used by PasteDeploy signifying that +it is the default application. The ``[server:main]`` section of the configuration file configures a WSGI server which listens on TCP port 6543. It is configured to listen on all @@ -544,7 +509,7 @@ and ``# End logging configuration`` represent Python's standard library between these two markers are passed to the `logging module's config file configuration engine `_ when the -``paster serve`` or ``paster pshell`` commands are executed. The default +``pserve`` or ``pshell`` commands are executed. The default configuration sends application logging output to the standard error output of your terminal. For more information about logging configuration, see :ref:`logging_chapter`. @@ -762,7 +727,7 @@ also informs Python that the directory which contains it is a *package*. #. Lines 4-12 define a function named ``main`` that returns a :app:`Pyramid` WSGI application. This function is meant to be called by the - :term:`PasteDeploy` framework as a result of running ``paster serve``. + :term:`PasteDeploy` framework as a result of running ``pserve``. Within this function, application configuration is performed. @@ -922,10 +887,10 @@ way you see fit. For example, the configuration method named :meth:`~pyramid.config.Configurator.add_view` requires you to pass a :term:`dotted Python name` or a direct object reference as the class or -function to be used as a view. By default, the ``pyramid_starter`` scaffold -would have you add view functions to the ``views.py`` module in your -package. However, you might be more comfortable creating a ``views`` -*directory*, and adding a single file for each view. +function to be used as a view. By default, the ``starter`` scaffold would +have you add view functions to the ``views.py`` module in your package. +However, you might be more comfortable creating a ``views`` *directory*, and +adding a single file for each view. If your project package name was ``myproject`` and you wanted to arrange all your views in a Python subpackage within the ``myproject`` :term:`package` @@ -980,33 +945,32 @@ Using the Interactive Shell It is possible to use a Python interpreter prompt loaded with a similar configuration as would be loaded if you were running your Pyramid application -via ``paster serve``. This can be a useful debugging tool. See +via ``pserve``. This can be a useful debugging tool. See :ref:`interactive_shell` for more details. Using an Alternate WSGI Server ------------------------------ The code generated by a :app:`Pyramid` scaffold assumes that you will be -using the ``paster serve`` command to start your application while you do -development. However, ``paster serve`` is by no means the only way to start -up and serve a :app:`Pyramid` application. As we saw in -:ref:`firstapp_chapter`, ``paster serve`` needn't be invoked at all to run a -:app:`Pyramid` application. The use of ``paster serve`` to run a -:app:`Pyramid` application is purely conventional based on the output of its -scaffold. +using the ``pserve`` command to start your application while you do +development. However, ``pserve`` is by no means the only way to start up and +serve a :app:`Pyramid` application. As we saw in :ref:`firstapp_chapter`, +``pserve`` needn't be invoked at all to run a :app:`Pyramid` application. +The use of ``pserve`` to run a :app:`Pyramid` application is purely +conventional based on the output of its scaffold. Any :term:`WSGI` server is capable of running a :app:`Pyramid` application. -Some WSGI servers don't require the :term:`PasteDeploy` framework's ``paster -serve`` command to do server process management at all. Each :term:`WSGI` +Some WSGI servers don't require the :term:`PasteDeploy` framework's +``pserve`` command to do server process management at all. Each :term:`WSGI` server has its own documentation about how it creates a process to run an application, and there are many of them, so we cannot provide the details for each here. But the concepts are largely the same, whatever server you happen to use. -One popular production alternative to a ``paster``-invoked server is +One popular production alternative to a ``pserve``-invoked server is :term:`mod_wsgi`. You can also use :term:`mod_wsgi` to serve your :app:`Pyramid` application using the Apache web server rather than any -"pure-Python" server that is started as a result of ``paster serve``. See +"pure-Python" server that is started as a result of ``pserve``. See :ref:`modwsgi_tutorial` for details. However, it is usually easier to -*develop* an application using a ``paster serve`` -invoked webserver, as +*develop* an application using a ``pserve`` -invoked webserver, as exception and debugging output will be sent to the console. diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 5a18ca851..1ad35b961 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -512,7 +512,7 @@ example: .. code-block:: text - $ PYRAMID_DEBUG_AUTHORIZATION=1 bin/paster serve myproject.ini + $ PYRAMID_DEBUG_AUTHORIZATION=1 bin/pserve myproject.ini When any authorization takes place during a top-level view rendering, a message will be logged to the console (to stderr) about what ACE in diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst index c66264655..f4ebef154 100644 --- a/docs/narr/startup.rst +++ b/docs/narr/startup.rst @@ -8,12 +8,12 @@ you'll see something much like this show up on the console: .. code-block:: text - $ paster serve myproject/MyProject.ini + $ pserve myproject/MyProject.ini Starting server in PID 16601. serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 This chapter explains what happens between the time you press the "Return" -key on your keyboard after typing ``paster serve myproject/MyProject.ini`` +key on your keyboard after typing ``pserve myproject/MyProject.ini`` and the time the line ``serving on 0.0.0.0:6543 ...`` is output to your console. @@ -24,22 +24,21 @@ The Startup Process ------------------- The easiest and best-documented way to start and serve a :app:`Pyramid` -application is to use the ``paster serve`` command against a +application is to use the ``pserve`` command against a :term:`PasteDeploy` ``.ini`` file. This uses the ``.ini`` file to infer settings and starts a server listening on a port. For the purposes of this discussion, we'll assume that you are using this command to run your :app:`Pyramid` application. Here's a high-level time-ordered overview of what happens when you press -``return`` after running ``paster serve development.ini``. +``return`` after running ``pserve development.ini``. -#. The :term:`PasteDeploy` ``paster`` command is invoked under your shell - with the arguments ``serve`` and ``development.ini``. As a result, the - :term:`PasteDeploy` framework recognizes that it is meant to begin to run - and serve an application using the information contained within the - ``development.ini`` file. +#. The ``pserve`` command is invoked under your shell with the argument + ``development.ini``. As a result, Pyramid recognizes that it is meant to + begin to run and serve an application using the information contained + within the ``development.ini`` file. -#. The PasteDeploy framework finds a section named either ``[app:main]``, +#. The framework finds a section named either ``[app:main]``, ``[pipeline:main]``, or ``[composite:main]`` in the ``.ini`` file. This section represents the configuration of a :term:`WSGI` application that will be served. If you're using a simple application (e.g. @@ -48,16 +47,16 @@ Here's a high-level time-ordered overview of what happens when you press configuration. If, instead of a simple application, you're using a WSGI :term:`pipeline` (e.g. a ``[pipeline:main]`` section), the application named on the "last" element will refer to your :app:`Pyramid` application. - If instead of a simple application or a pipeline, you're using a Paste + If instead of a simple application or a pipeline, you're using a "composite" (e.g. ``[composite:main]``), refer to the documentation for that particular composite to understand how to make it refer to your :app:`Pyramid` application. In most cases, a Pyramid application built from a scaffold will have a single ``[app:main]`` section in it, and this will be the application served. -#. The PasteDeploy framework finds all :mod:`logging` related configuration - in the ``.ini`` file and uses it to configure the Python standard library - logging system for this application. +#. The framework finds all :mod:`logging` related configuration in the + ``.ini`` file and uses it to configure the Python standard library logging + system for this application. #. The application's *constructor* (named by the entry point reference or dotted Python name on the ``use=`` line of the section representing your @@ -82,7 +81,7 @@ Here's a high-level time-ordered overview of what happens when you press key/value pairs received by this function in ``**settings`` will be composed of all the key/value pairs that are present in the ``[app:main]`` section (except for the ``use=`` setting) when this function is called by - the :term:`PasteDeploy` framework when you run ``paster serve``. + when you run ``pserve``. Our generated ``development.ini`` file looks like so: @@ -110,7 +109,7 @@ Here's a high-level time-ordered overview of what happens when you press The ``settings`` dictionary contains all the options in the ``[app:main]`` section of our .ini file except the ``use`` option (which is internal to - Paste) such as ``pyramid.reload_templates``, + PasteDeploy) such as ``pyramid.reload_templates``, ``pyramid.debug_authorization``, etc. #. The ``main`` function then calls various methods on the instance of the @@ -130,12 +129,12 @@ Here's a high-level time-ordered overview of what happens when you press #. Assuming there were no errors, the ``main`` function in ``myproject`` returns the router instance created by - :meth:`pyramid.config.Configurator.make_wsgi_app` back to PasteDeploy. As - far as PasteDeploy is concerned, it is "just another WSGI application". + :meth:`pyramid.config.Configurator.make_wsgi_app` back to ``pserve``. As + far as ``pserve`` is concerned, it is "just another WSGI application". -#. PasteDeploy starts the WSGI *server* defined within the ``[server:main]`` - section. In our case, this is the ``Paste#http`` server (``use = - egg:Paste#http``), and it will listen on all interfaces (``host = +#. ``pserve`` starts the WSGI *server* defined within the ``[server:main]`` + section. In our case, this is the ``egg:pyramid#wsgiref`` server (``use = + egg:pyramid#wsgiref``), and it will listen on all interfaces (``host = 0.0.0.0``), on port number 6543 (``port = 6543``). The server code itself is what prints ``serving on 0.0.0.0:6543 view at http://127.0.0.1:6543``. The server serves the application, and the application is running, waiting diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index 052843a23..fb9dd56c2 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -625,7 +625,7 @@ variable set to ``1``, For example: .. code-block:: text - $ PYRAMID_DEBUG_TEMPLATES=1 bin/paster serve myproject.ini + $ PYRAMID_DEBUG_TEMPLATES=1 bin/pserve myproject.ini To use a setting in the application ``.ini`` file for the same purpose, set the ``pyramid.debug_templates`` key to ``true`` within @@ -786,7 +786,7 @@ variable set to ``1``, For example: .. code-block:: text - $ PYRAMID_RELOAD_TEMPLATES=1 bin/paster serve myproject.ini + $ PYRAMID_RELOAD_TEMPLATES=1 bin/pserve myproject.ini To use a setting in the application ``.ini`` file for the same purpose, set the ``pyramid.reload_templates`` key to ``true`` within the diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 2fcbce507..9ceb20f21 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -390,7 +390,7 @@ lookup` subsystem takes over to find the most reasonable view callable for the matched route. Most often, there's only one view that will match (a view configured with a ``route_name`` argument matching the matched route). To gain a better understanding of how routes and views are associated in a real -application, you can use the ``paster pviews`` command, as documented in +application, you can use the ``pviews`` command, as documented in :ref:`displaying_matching_views`. If no route matches after all route patterns are exhausted, :app:`Pyramid` @@ -772,7 +772,7 @@ which you started the application from. For example: :linenos: [chrism@thinko pylonsbasic]$ PYRAMID_DEBUG_ROUTEMATCH=true \ - bin/paster serve development.ini + bin/pserve development.ini Starting server in PID 13586. serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 2010-12-16 14:45:19,956 no route matched for url \ @@ -786,7 +786,7 @@ which you started the application from. For example: See :ref:`environment_chapter` for more information about how, and where to set these values. -You can also use the ``paster proutes`` command to see a display of all the +You can also use the ``proutes`` command to see a display of all the routes configured in your application; for more information, see :ref:`displaying_application_routes`. @@ -1012,8 +1012,8 @@ The above predicate, when added to a number of route configurations ensures that the year match argument is '2010' if and only if the route name is 'ymd', 'ym', or 'y'. -You can also caption the predicates by setting the ``__text__`` attribute. This -will help you with the ``paster pviews`` command (see +You can also caption the predicates by setting the ``__text__`` +attribute. This will help you with the ``pviews`` command (see :ref:`displaying_application_routes`) and the ``pyramid_debugtoolbar``. If a predicate is a class just add __text__ property in a standard manner. diff --git a/docs/tutorials/gae/index.rst b/docs/tutorials/gae/index.rst index 9c8e8c07e..3bd739480 100644 --- a/docs/tutorials/gae/index.rst +++ b/docs/tutorials/gae/index.rst @@ -64,7 +64,7 @@ system. $ cd app $ rm -rf pyramidapp - $ bin/paster create -t pyramid_starter pyramidapp + $ bin/pcreate -s starter pyramidapp $ mv pyramidapp aside $ mv aside/pyramidapp . $ rm -rf aside diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst index 485eec169..fd6fd5884 100644 --- a/docs/tutorials/modwsgi/index.rst +++ b/docs/tutorials/modwsgi/index.rst @@ -64,7 +64,7 @@ commands and files. .. code-block:: text $ cd ~/modwsgi/env - $ bin/paster create -t pyramid_starter myapp + $ bin/pcreate -s starter myapp $ cd myapp $ ../bin/python setup.py install diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst index d3f2fb455..47cac597b 100644 --- a/docs/tutorials/wiki/basiclayout.rst +++ b/docs/tutorials/wiki/basiclayout.rst @@ -18,7 +18,7 @@ an ``__init__.py`` file. Even if empty, this marks a directory as a Python package. Our application uses ``__init__.py`` as both a package marker, as well as to contain application configuration code. -When you run the application using the ``paster`` command using the +When you run the application using the ``pserve`` command using the ``development.ini`` generated config file, the application configuration points at a Setuptools *entry point* described as ``egg:tutorial``. In our application, because the application's ``setup.py`` file says so, this entry @@ -34,7 +34,7 @@ point happens to be the ``main`` function within the file named #. *Lines 5-7* Define a root factory for our Pyramid application. #. *Line 12*. We construct a :term:`Configurator` with a :term:`root - factory` and the settings keywords parsed by PasteDeploy. The root + factory` and the settings keywords parsed by :term:`PasteDeploy`. The root factory is named ``get_root``. #. *Line 13*. Register a 'static view' which answers requests which start @@ -167,4 +167,4 @@ application. Our ZODB database settings are specified as the ``zodbconn.uri`` setting within this section. This value, and the other values within this section are passed as ``**settings`` to the ``main`` function we defined in ``__init__.py`` when the server is started via -``paster serve``. +``pserve``. diff --git a/docs/tutorials/wiki/definingmodels.rst b/docs/tutorials/wiki/definingmodels.rst index 9dd53609e..ee9c13ab2 100644 --- a/docs/tutorials/wiki/definingmodels.rst +++ b/docs/tutorials/wiki/definingmodels.rst @@ -2,7 +2,7 @@ Defining the Domain Model ========================= -The first change we'll make to our stock paster-generated application will be +The first change we'll make to our stock pcreate-generated application will be to define two :term:`resource` constructors, one representing a wiki page, and another representing the wiki as a mapping of wiki page names to page objects. We'll do this inside our ``models.py`` file. diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst index ed3a84118..c21367559 100644 --- a/docs/tutorials/wiki/definingviews.rst +++ b/docs/tutorials/wiki/definingviews.rst @@ -37,7 +37,7 @@ Declaring Dependencies in Our ``setup.py`` File The view code in our application will depend on a package which is not a dependency of the original "tutorial" application. The original "tutorial" -application was generated by the ``paster create`` command; it doesn't know +application was generated by the ``pcreate`` command; it doesn't know about our custom application requirements. We need to add a dependency on the ``docutils`` package to our ``tutorial`` package's ``setup.py`` file by assigning this dependency to the ``install_requires`` parameter in the diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst index 822e0bd22..c55c310ef 100644 --- a/docs/tutorials/wiki/installation.rst +++ b/docs/tutorials/wiki/installation.rst @@ -130,9 +130,9 @@ Preparation, Windows Making a Project ================ -Your next step is to create a project. :app:`Pyramid` supplies a -variety of scaffolds to generate sample projects. For this tutorial, -we will use the :term:`ZODB` -oriented scaffold named ``pyramid_zodb``. +Your next step is to create a project. :app:`Pyramid` supplies a variety of +scaffolds to generate sample projects. For this tutorial, we will use the +:term:`ZODB` -oriented scaffold named ``zodb``. The below instructions assume your current working directory is the "virtualenv" named "pyramidtut". @@ -141,15 +141,15 @@ On UNIX: .. code-block:: text - $ bin/paster create -t pyramid_zodb tutorial + $ bin/pcreate -s zodb tutorial On Windows: .. code-block:: text - c:\pyramidtut> Scripts\paster create -t pyramid_zodb tutorial + c:\pyramidtut> Scripts\pcreate -s zodb tutorial -.. note:: If you are using Windows, the ``pyramid_zodb`` Paster scaffold +.. note:: If you are using Windows, the ``zodb`` scaffold doesn't currently deal gracefully with installation into a location that contains spaces in the path. If you experience startup problems, try putting both the virtualenv and the project into @@ -207,13 +207,13 @@ On UNIX: .. code-block:: text - $ ../bin/paster serve development.ini --reload + $ ../bin/pserve development.ini --reload On Windows: .. code-block:: text - c:\pyramidtut\tutorial> ..\Scripts\paster serve development.ini --reload + c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload Exposing Test Coverage Information ================================== diff --git a/docs/tutorials/wiki/src/authorization/development.ini b/docs/tutorials/wiki/src/authorization/development.ini index a45860ff7..0f950e260 100644 --- a/docs/tutorials/wiki/src/authorization/development.ini +++ b/docs/tutorials/wiki/src/authorization/development.ini @@ -13,7 +13,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/authorization/production.ini b/docs/tutorials/wiki/src/authorization/production.ini index 0588f63a0..f632f89d1 100644 --- a/docs/tutorials/wiki/src/authorization/production.ini +++ b/docs/tutorials/wiki/src/authorization/production.ini @@ -12,7 +12,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py index 7dd7f2fc6..284bc216e 100644 --- a/docs/tutorials/wiki/src/authorization/setup.py +++ b/docs/tutorials/wiki/src/authorization/setup.py @@ -40,6 +40,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py index 1241cecd4..2d6eb5ecb 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py @@ -13,9 +13,6 @@ def root_factory(request): def main(global_config, **settings): """ This function returns a WSGI application. - - It is usually called by the PasteDeploy framework during - ``paster serve``. """ authn_policy = AuthTktAuthenticationPolicy(secret='sosecret', callback=groupfinder) diff --git a/docs/tutorials/wiki/src/basiclayout/development.ini b/docs/tutorials/wiki/src/basiclayout/development.ini index 87da2f84a..e296ea1a1 100644 --- a/docs/tutorials/wiki/src/basiclayout/development.ini +++ b/docs/tutorials/wiki/src/basiclayout/development.ini @@ -13,7 +13,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/basiclayout/production.ini b/docs/tutorials/wiki/src/basiclayout/production.ini index 0588f63a0..f632f89d1 100644 --- a/docs/tutorials/wiki/src/basiclayout/production.ini +++ b/docs/tutorials/wiki/src/basiclayout/production.ini @@ -12,7 +12,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/basiclayout/setup.py b/docs/tutorials/wiki/src/basiclayout/setup.py index fa641b50b..d5fd8891f 100644 --- a/docs/tutorials/wiki/src/basiclayout/setup.py +++ b/docs/tutorials/wiki/src/basiclayout/setup.py @@ -38,6 +38,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki/src/models/development.ini b/docs/tutorials/wiki/src/models/development.ini index a45860ff7..0f950e260 100644 --- a/docs/tutorials/wiki/src/models/development.ini +++ b/docs/tutorials/wiki/src/models/development.ini @@ -13,7 +13,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/models/production.ini b/docs/tutorials/wiki/src/models/production.ini index 0588f63a0..f632f89d1 100644 --- a/docs/tutorials/wiki/src/models/production.ini +++ b/docs/tutorials/wiki/src/models/production.ini @@ -12,7 +12,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/models/setup.py b/docs/tutorials/wiki/src/models/setup.py index fa641b50b..d5fd8891f 100644 --- a/docs/tutorials/wiki/src/models/setup.py +++ b/docs/tutorials/wiki/src/models/setup.py @@ -38,6 +38,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki/src/tests/development.ini b/docs/tutorials/wiki/src/tests/development.ini index a45860ff7..0f950e260 100644 --- a/docs/tutorials/wiki/src/tests/development.ini +++ b/docs/tutorials/wiki/src/tests/development.ini @@ -13,7 +13,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/tests/production.ini b/docs/tutorials/wiki/src/tests/production.ini index 0588f63a0..f632f89d1 100644 --- a/docs/tutorials/wiki/src/tests/production.ini +++ b/docs/tutorials/wiki/src/tests/production.ini @@ -12,7 +12,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py index 137b2ee42..dfecb7c5c 100644 --- a/docs/tutorials/wiki/src/tests/setup.py +++ b/docs/tutorials/wiki/src/tests/setup.py @@ -41,6 +41,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki/src/tests/tutorial/__init__.py b/docs/tutorials/wiki/src/tests/tutorial/__init__.py index 1241cecd4..2d6eb5ecb 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/tests/tutorial/__init__.py @@ -13,9 +13,6 @@ def root_factory(request): def main(global_config, **settings): """ This function returns a WSGI application. - - It is usually called by the PasteDeploy framework during - ``paster serve``. """ authn_policy = AuthTktAuthenticationPolicy(secret='sosecret', callback=groupfinder) diff --git a/docs/tutorials/wiki/src/views/development.ini b/docs/tutorials/wiki/src/views/development.ini index 87da2f84a..e296ea1a1 100644 --- a/docs/tutorials/wiki/src/views/development.ini +++ b/docs/tutorials/wiki/src/views/development.ini @@ -13,7 +13,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/views/production.ini b/docs/tutorials/wiki/src/views/production.ini index 0588f63a0..f632f89d1 100644 --- a/docs/tutorials/wiki/src/views/production.ini +++ b/docs/tutorials/wiki/src/views/production.ini @@ -12,7 +12,7 @@ tm.attempts = 3 zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki/src/views/setup.py b/docs/tutorials/wiki/src/views/setup.py index 756618072..866f12bef 100644 --- a/docs/tutorials/wiki/src/views/setup.py +++ b/docs/tutorials/wiki/src/views/setup.py @@ -40,5 +40,4 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index 2d25ace00..8dc886373 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -18,7 +18,7 @@ an ``__init__.py`` file. Even if empty, this marks a directory as a Python package. We use ``__init__.py`` both as a package marker and to contain configuration code. -The generated ``development.ini`` file is read by ``paster`` which looks for +The generated ``development.ini`` file is read by ``pserve`` which looks for the application module in the ``use`` variable of the ``app:main`` section. The *entry point* is defined in the Setuptools configuration of this module, specifically in the ``setup.py`` file. For this tutorial, the *entry diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index 7aa2214fc..083ec0aa8 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -2,9 +2,9 @@ Defining the Domain Model ========================= -The first change we'll make to our stock paster-generated application will be -to define a :term:`domain model` constructor representing a wiki page. We'll -do this inside our ``models.py`` file. +The first change we'll make to our stock pcreate-generated application will +be to define a :term:`domain model` constructor representing a wiki page. +We'll do this inside our ``models.py`` file. The source code for this tutorial stage can be browsed at `http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/models/ @@ -64,7 +64,7 @@ FrontPage object to our database at startup time. :language: python Here, we're using a slightly different binding syntax. It is otherwise -largely the same as the ``initialize_sql`` in the paster-generated +largely the same as the ``initialize_sql`` in the pcreate-generated ``models.py``. Our ``DBSession`` assignment stays the same as the original generated diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index 234f91246..21b97f7aa 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -40,7 +40,7 @@ Declaring Dependencies in Our ``setup.py`` File The view code in our application will depend on a package which is not a dependency of the original "tutorial" application. The original "tutorial" -application was generated by the ``paster create`` command; it doesn't know +application was generated by the ``pcreate`` command; it doesn't know about our custom application requirements. We need to add a dependency on the ``docutils`` package to our ``tutorial`` package's ``setup.py`` file by assigning this dependency to the ``install_requires`` parameter in the diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index bfe75d82e..f9f5c4fba 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -94,13 +94,13 @@ On UNIX: .. code-block:: text - $ bin/paster create -t pyramid_routesalchemy tutorial + $ bin/pcreate -s routesalchemy tutorial On Windows: .. code-block:: text - c:\pyramidtut> Scripts\paster create -t pyramid_routesalchemy tutorial + c:\pyramidtut> Scripts\pcreate -s routesalchemy tutorial .. note:: If you are using Windows, the ``pyramid_routesalchemy`` scaffold may not deal gracefully with installation into a @@ -160,13 +160,13 @@ On UNIX: .. code-block:: text - $ ../bin/paster serve development.ini --reload + $ ../bin/pserve development.ini --reload On Windows: .. code-block:: text - c:\pyramidtut\tutorial> ..\Scripts\paster serve development.ini --reload + c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload Exposing Test Coverage Information ================================== diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index 799ce7161..d1e262324 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -12,7 +12,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini index df91d0b49..ac02acf3f 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -11,7 +11,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py index 785d61326..439a86923 100644 --- a/docs/tutorials/wiki2/src/authorization/setup.py +++ b/docs/tutorials/wiki2/src/authorization/setup.py @@ -43,6 +43,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index 799ce7161..d1e262324 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -12,7 +12,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini index df91d0b49..ac02acf3f 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -11,7 +11,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py index 99b7cabda..3ab493912 100644 --- a/docs/tutorials/wiki2/src/basiclayout/setup.py +++ b/docs/tutorials/wiki2/src/basiclayout/setup.py @@ -42,6 +42,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index 799ce7161..d1e262324 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -12,7 +12,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini index df91d0b49..ac02acf3f 100644 --- a/docs/tutorials/wiki2/src/models/production.ini +++ b/docs/tutorials/wiki2/src/models/production.ini @@ -11,7 +11,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py index 99b7cabda..3ab493912 100644 --- a/docs/tutorials/wiki2/src/models/setup.py +++ b/docs/tutorials/wiki2/src/models/setup.py @@ -42,6 +42,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index 799ce7161..d1e262324 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/development.ini @@ -12,7 +12,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index df91d0b49..ac02acf3f 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -11,7 +11,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index ef7f1c215..6de8a1fbe 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -44,6 +44,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index 799ce7161..d1e262324 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -12,7 +12,7 @@ pyramid.includes = pyramid_debugtoolbar sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini index df91d0b49..ac02acf3f 100644 --- a/docs/tutorials/wiki2/src/views/production.ini +++ b/docs/tutorials/wiki2/src/views/production.ini @@ -11,7 +11,7 @@ pyramid.includes = pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.db [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py index 785d61326..439a86923 100644 --- a/docs/tutorials/wiki2/src/views/setup.py +++ b/docs/tutorials/wiki2/src/views/setup.py @@ -43,6 +43,5 @@ setup(name='tutorial', [paste.app_factory] main = tutorial:main """, - paster_plugins=['pyramid'], ) diff --git a/pyramid/config/__init__.py b/pyramid/config/__init__.py index 8085bbc79..b55a7a9e4 100644 --- a/pyramid/config/__init__.py +++ b/pyramid/config/__init__.py @@ -805,7 +805,7 @@ class Configurator( self.commit() app = Router(self.registry) - # Allow tools like "paster pshell development.ini" to find the 'last' + # Allow tools like "pshell development.ini" to find the 'last' # registry configured. global_registries.add(self.registry) diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py index 3c7ee384f..048309451 100644 --- a/pyramid/config/tweens.py +++ b/pyramid/config/tweens.py @@ -29,7 +29,7 @@ class TweensConfiguratorMixin(object): registry` as well as the Pyramid rendering machinery. .. note:: You can view the tween ordering configured into a given - Pyramid application by using the ``paster ptweens`` + Pyramid application by using the ``ptweens`` command. See :ref:`displaying_tweens`. The ``tween_factory`` argument must be a :term:`dotted Python name` @@ -61,7 +61,7 @@ class TweensConfiguratorMixin(object): For example, calling ``add_tween('myapp.tfactory', over=pyramid.tweens.MAIN)`` will attempt to place the tween factory represented by the dotted name ``myapp.tfactory`` directly 'above' - (in ``paster ptweens`` order) the main Pyramid request handler. + (in ``ptweens`` order) the main Pyramid request handler. Likewise, calling ``add_tween('myapp.tfactory', over=pyramid.tweens.MAIN, under='mypkg.someothertween')`` will attempt to place this tween factory 'above' the main handler but diff --git a/pyramid/scaffolds/alchemy/setup.py_tmpl b/pyramid/scaffolds/alchemy/setup.py_tmpl index 3a643c8b8..68f8e6245 100644 --- a/pyramid/scaffolds/alchemy/setup.py_tmpl +++ b/pyramid/scaffolds/alchemy/setup.py_tmpl @@ -42,6 +42,5 @@ setup(name='{{project}}', [paste.app_factory] main = {{package}}:main """, - paster_plugins=['pyramid'], ) diff --git a/pyramid/scaffolds/routesalchemy/setup.py_tmpl b/pyramid/scaffolds/routesalchemy/setup.py_tmpl index e62717742..a2cdaac60 100644 --- a/pyramid/scaffolds/routesalchemy/setup.py_tmpl +++ b/pyramid/scaffolds/routesalchemy/setup.py_tmpl @@ -42,6 +42,5 @@ setup(name='{{project}}', [paste.app_factory] main = {{package}}:main """, - paster_plugins=['pyramid'], ) diff --git a/pyramid/scaffolds/starter/setup.py_tmpl b/pyramid/scaffolds/starter/setup.py_tmpl index 44bd66bd3..e63579d50 100644 --- a/pyramid/scaffolds/starter/setup.py_tmpl +++ b/pyramid/scaffolds/starter/setup.py_tmpl @@ -32,6 +32,5 @@ setup(name='{{project}}', [paste.app_factory] main = {{package}}:main """, - paster_plugins=['pyramid'], ) diff --git a/pyramid/scaffolds/tests.py b/pyramid/scaffolds/tests.py index 9b8b975a3..c0b60b61b 100644 --- a/pyramid/scaffolds/tests.py +++ b/pyramid/scaffolds/tests.py @@ -39,10 +39,10 @@ class TemplateTest(object): py = os.path.join(self.directory, 'bin', 'python') subprocess.check_call([py, 'setup.py', 'install']) subprocess.check_call([py, 'setup.py', 'test']) - paster = os.path.join(self.directory, 'bin', 'paster') + pserve = os.path.join(self.directory, 'bin', 'pserve') for ininame, hastoolbar in (('development.ini', True), ('production.ini', False)): - proc = subprocess.Popen([paster, 'serve', ininame]) + proc = subprocess.Popen([pserve, ininame]) try: time.sleep(5) proc.poll() diff --git a/pyramid/scaffolds/zodb/setup.py_tmpl b/pyramid/scaffolds/zodb/setup.py_tmpl index 6bcd7cbbd..812b85e83 100644 --- a/pyramid/scaffolds/zodb/setup.py_tmpl +++ b/pyramid/scaffolds/zodb/setup.py_tmpl @@ -38,6 +38,5 @@ setup(name='{{project}}', [paste.app_factory] main = {{package}}:main """, - paster_plugins=['pyramid'], ) -- cgit v1.2.3 From f8869cb0664506204b22aa791003a6d5f8ded58c Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 03:22:35 -0400 Subject: remove stray references to Paste --- docs/designdefense.rst | 2 +- docs/glossary.rst | 10 +++------- docs/narr/MyProject/development.ini | 2 +- docs/narr/MyProject/production.ini | 2 +- docs/narr/environment.rst | 4 ++-- docs/narr/firstapp.rst | 2 +- docs/narr/helloworld.py | 6 ++++-- docs/narr/i18n.rst | 16 ++++++++-------- docs/narr/install.rst | 9 ++++----- docs/narr/paste.rst | 18 +++++++++--------- docs/narr/project.rst | 5 +++-- docs/narr/vhosting.rst | 10 +++++----- docs/narr/zca.rst | 13 ++++++------- docs/tutorials/modwsgi/index.rst | 2 +- 14 files changed, 49 insertions(+), 52 deletions(-) diff --git a/docs/designdefense.rst b/docs/designdefense.rst index d71f9af7f..80675ce83 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -720,7 +720,7 @@ microframeworks and Django boast. The :mod:`zope.component`, package on which :app:`Pyramid` depends has transitive dependencies on several other packages (:mod:`zope.event`, and :mod:`zope.interface`). :app:`Pyramid` also has its own direct dependencies, -such as :term:`Paste`, :term:`Chameleon`, :term:`Mako` :term:`WebOb`, +such as :term:`PasteDeploy`, :term:`Chameleon`, :term:`Mako` :term:`WebOb`, :mod:`zope.deprecation` and some of these in turn have their own transitive dependencies. diff --git a/docs/glossary.rst b/docs/glossary.rst index 472e591a9..fc282b2da 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -95,7 +95,7 @@ Glossary dotted Python name A reference to a Python object by name using a string, in the form - ``path.to.modulename:attributename``. Often used in Paste and + ``path.to.modulename:attributename``. Often used in Pyramid and setuptools configurations. A variant is used in dotted names within configurator method arguments that name objects (such as the "add_view" method's "view" and "context" attributes): the colon (``:``) is not @@ -304,7 +304,7 @@ Glossary application. pipeline - The :term:`Paste` term for a single configuration of a WSGI + The :term:`PasteDeploy` term for a single configuration of a WSGI server, a WSGI application, with a set of middleware in-between. Zope @@ -335,15 +335,11 @@ Glossary `WebOb `_ is a WSGI request/response library created by Ian Bicking. - Paste - `Paste `_ is a WSGI development and - deployment system developed by Ian Bicking. - PasteDeploy `PasteDeploy `_ is a library used by :app:`Pyramid` which makes it possible to configure :term:`WSGI` components together declaratively within an ``.ini`` - file. It was developed by Ian Bicking as part of :term:`Paste`. + file. It was developed by Ian Bicking. Chameleon `chameleon `_ is an attribute diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini index e93266bab..3a4758c44 100644 --- a/docs/narr/MyProject/development.ini +++ b/docs/narr/MyProject/development.ini @@ -10,7 +10,7 @@ pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/narr/MyProject/production.ini b/docs/narr/MyProject/production.ini index 83bce1ef2..9d025715d 100644 --- a/docs/narr/MyProject/production.ini +++ b/docs/narr/MyProject/production.ini @@ -9,7 +9,7 @@ pyramid.debug_templates = false pyramid.default_locale_name = en [server:main] -use = egg:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst index 2ac094d47..8206e0bcb 100644 --- a/docs/narr/environment.rst +++ b/docs/narr/environment.rst @@ -344,8 +344,8 @@ sequence can take several different forms. Each value in the sequence should be a :term:`dotted Python name`. -Paste Configuration vs. Plain-Python Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +PasteDeploy Configuration vs. Plain-Python Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Using the following ``pyramid.tweens`` setting in the PasteDeploy ``.ini`` file in your application: diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst index 55829bef0..45d65402c 100644 --- a/docs/narr/firstapp.rst +++ b/docs/narr/firstapp.rst @@ -209,7 +209,7 @@ Finally, we actually serve the application to requestors by starting up a WSGI server. We happen to use the :func:`paste.httpserver.serve` WSGI server runner, passing it the ``app`` object (a :term:`router`) as the application we wish to serve. We also pass in an argument ``host=='0.0.0.0'``, meaning -"listen on all TCP interfaces." By default, the Paste HTTP server listens +"listen on all TCP interfaces." By default, the HTTP server listens only on the ``127.0.0.1`` interface, which is problematic if you're running the server on a remote system and you wish to access it with a web browser from a local system. We don't specify a TCP port number to listen on; this diff --git a/docs/narr/helloworld.py b/docs/narr/helloworld.py index 5f121d48d..93a403a13 100644 --- a/docs/narr/helloworld.py +++ b/docs/narr/helloworld.py @@ -1,4 +1,4 @@ -from paste.httpserver import serve +from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response @@ -10,4 +10,6 @@ if __name__ == '__main__': config.add_route('hello', '/hello/{name}') config.add_view(hello_world, route_name='hello') app = config.make_wsgi_app() - serve(app, host='0.0.0.0') + server = make_server('0.0.0.0', 8080) + server.serve_forever() + diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst index bac86e982..c2ecba9bb 100644 --- a/docs/narr/i18n.rst +++ b/docs/narr/i18n.rst @@ -365,10 +365,10 @@ be performed to localize your application. By default, the translation domain is the :term:`project` name of your :app:`Pyramid` application. -To change the translation domain of the extracted messages in your -project, edit the ``setup.cfg`` file of your application, The default -``setup.cfg`` file of a Paster-generated :app:`Pyramid` application -has stanzas in it that look something like the following: +To change the translation domain of the extracted messages in your project, +edit the ``setup.cfg`` file of your application, The default ``setup.cfg`` +file of a ``pcreate`` -generated :app:`Pyramid` application has stanzas in it +that look something like the following: .. code-block:: ini :linenos: @@ -785,7 +785,7 @@ time: config = Configurator(settings={'pyramid.default_locale_name':'de'}) You may alternately supply a ``pyramid.default_locale_name`` via an -application's Paster ``.ini`` file: +application's ``.ini`` file: .. code-block:: ini :linenos: @@ -797,8 +797,8 @@ application's Paster ``.ini`` file: pyramid.debug_notfound = false pyramid.default_locale_name = de -If this value is not supplied via the Configurator constructor or via -a Paste config file, it will default to ``en``. +If this value is not supplied via the Configurator constructor or via a +config file, it will default to ``en``. If this setting is supplied within the :app:`Pyramid` application ``.ini`` file, it will be available as a settings key: @@ -845,7 +845,7 @@ You can set up a system to allow a deployer to select available languages based on convention by using the :mod:`pyramid.settings` mechanism: -Allow a deployer to modify your application's PasteDeploy .ini file: +Allow a deployer to modify your application's ``.ini`` file: .. code-block:: ini :linenos: diff --git a/docs/narr/install.rst b/docs/narr/install.rst index e1b5eb208..66bcea706 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -347,10 +347,9 @@ Jython; use it instead. What Gets Installed ------------------- -When you ``easy_install`` :app:`Pyramid`, various Zope libraries, -various Chameleon libraries, WebOb, Paste, PasteScript, and -PasteDeploy libraries are installed. +When you ``easy_install`` :app:`Pyramid`, various other libraries such as +WebOb, PasteDeploy, and others are installed. -Additionally, as chronicled in :ref:`project_narr`, scaffolds will be registered, -which make it easy to start a new :app:`Pyramid` project. +Additionally, as chronicled in :ref:`project_narr`, scaffolds will be +registered, which make it easy to start a new :app:`Pyramid` project. diff --git a/docs/narr/paste.rst b/docs/narr/paste.rst index 5c7d4c3fb..cf8f96c8a 100644 --- a/docs/narr/paste.rst +++ b/docs/narr/paste.rst @@ -20,7 +20,7 @@ setting deployment values, and to provide new users with a standardized way of starting, stopping, and debugging an application. This chapter is not a replacement for documentation about PasteDeploy; it -only contextualizes the use of Paste within Pyramid. For detailed +only contextualizes the use of PasteDeploy within Pyramid. For detailed documentation, see http://pythonpaste.org. PasteDeploy @@ -76,14 +76,14 @@ The ``egg:`` prefix in ``egg:MyProject`` indicates that this is an entry point *URI* specifier, where the "scheme" is "egg". An "egg" is created when you run ``setup.py install`` or ``setup.py develop`` within your project. -In English, this entry point can thus be referred to as a "Paste application -factory in the ``MyProject`` project which has the entry point named ``main`` -where the entry point refers to a ``main`` function in the ``mypackage`` -module". Indeed, if you open up the ``__init__.py`` module generated within -any scaffold-generated package, you'll see a ``main`` function. This is the -function called by :term:`PasteDeploy` when the ``pserve`` command is invoked -against our application. It accepts a global configuration object and -*returns* an instance of our application. +In English, this entry point can thus be referred to as a "PasteDeploy +application factory in the ``MyProject`` project which has the entry point +named ``main`` where the entry point refers to a ``main`` function in the +``mypackage`` module". Indeed, if you open up the ``__init__.py`` module +generated within any scaffold-generated package, you'll see a ``main`` +function. This is the function called by :term:`PasteDeploy` when the +``pserve`` command is invoked against our application. It accepts a global +configuration object and *returns* an instance of our application. ``[DEFAULTS]`` Section of a PasteDeploy ``.ini`` File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/narr/project.rst b/docs/narr/project.rst index c961b4143..4f96448af 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -683,7 +683,8 @@ The ``myproject`` :term:`package` lives inside the ``MyProject`` #. An ``__init__.py`` file signifies that this is a Python :term:`package`. It also contains code that helps users run the application, including a - ``main`` function which is used as a Paste entry point. + ``main`` function which is used as a entry point for commands such as + ``pserve``, ``pshell``, ``pviews``, and others. #. A ``resources.py`` module, which contains :term:`resource` code. @@ -750,7 +751,7 @@ also informs Python that the directory which contains it is a *package*. directory of the ``mypackage`` package). Line 12 returns a :term:`WSGI` application to the caller of the function - (Paste). + (Pyramid's pserve). .. index:: single: views.py diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst index 8697df6a0..d37518052 100644 --- a/docs/narr/vhosting.rst +++ b/docs/narr/vhosting.rst @@ -25,11 +25,11 @@ can host a :app:`Pyramid` application as a "subset" of some other site (e.g. under ``http://example.com/mypyramidapplication/`` as opposed to under ``http://example.com/``). -If you use a "pure Python" environment, this functionality is provided -by Paste's `urlmap `_ -"composite" WSGI application. Alternately, you can use -:term:`mod_wsgi` to serve your application, which handles this virtual -hosting translation for you "under the hood". +If you use a "pure Python" environment, this functionality can be provided by +Paste's `urlmap `_ "composite" +WSGI application. Alternately, you can use :term:`mod_wsgi` to serve your +application, which handles this virtual hosting translation for you "under +the hood". If you use the ``urlmap`` composite application "in front" of a :app:`Pyramid` application or if you use :term:`mod_wsgi` to serve diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst index 96aac6a80..f7707ea29 100644 --- a/docs/narr/zca.rst +++ b/docs/narr/zca.rst @@ -61,14 +61,13 @@ Using the ZCA Global API in a :app:`Pyramid` Application effectively making it impossible to run more than one Zope application in a single process. -However, for ease of deployment, it's often useful to be able to run -more than a single application per process. For example, use of a -:term:`Paste` "composite" allows you to run separate individual WSGI +However, for ease of deployment, it's often useful to be able to run more +than a single application per process. For example, use of a +:term:`PasteDeploy` "composite" allows you to run separate individual WSGI applications in the same process, each answering requests for some URL -prefix. This makes it possible to run, for example, a TurboGears -application at ``/turbogears`` and a :app:`Pyramid` application at -``/pyramid``, both served up using the same :term:`WSGI` server -within a single Python process. +prefix. This makes it possible to run, for example, a TurboGears application +at ``/turbogears`` and a :app:`Pyramid` application at ``/pyramid``, both +served up using the same :term:`WSGI` server within a single Python process. Most production Zope applications are relatively large, making it impractical due to memory constraints to run more than one Zope diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst index fd6fd5884..c2baa5bd8 100644 --- a/docs/tutorials/modwsgi/index.rst +++ b/docs/tutorials/modwsgi/index.rst @@ -77,7 +77,7 @@ commands and files. application = get_app( '/Users/chrism/modwsgi/env/myapp/production.ini', 'main') - The first argument to ``get_app`` is the project Paste configuration file + The first argument to ``get_app`` is the project configuration file name. It's best to use the ``production.ini`` file provided by your scaffold, as it contains settings appropriate for production. The second is the name of the section within the .ini file -- cgit v1.2.3 From 71260a1b55f07c44f5a6275f34b6601cf66158db Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 05:11:28 -0400 Subject: add tests for template.py --- pyramid/scaffolds/template.py | 43 +++++--- pyramid/tests/test_scaffolds/__init__.py | 1 + pyramid/tests/test_scaffolds/test_template.py | 137 ++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 pyramid/tests/test_scaffolds/__init__.py create mode 100644 pyramid/tests/test_scaffolds/test_template.py diff --git a/pyramid/scaffolds/template.py b/pyramid/scaffolds/template.py index f9af8010a..4ffa946d3 100644 --- a/pyramid/scaffolds/template.py +++ b/pyramid/scaffolds/template.py @@ -16,6 +16,8 @@ from pyramid.scaffolds import copydir fsenc = sys.getfilesystemencoding() class Template(object): + copydir = copydir # for testing + _template_dir = None def __init__(self, name): self.name = name @@ -47,13 +49,13 @@ class Template(object): self.write_files(command, output_dir, vars) self.post(command, output_dir, vars) - def pre(self, command, output_dir, vars): + def pre(self, command, output_dir, vars): # pragma: no cover """ Called before template is applied. """ pass - def post(self, command, output_dir, vars): + def post(self, command, output_dir, vars): # pragma: no cover """ Called after template is applied. """ @@ -61,21 +63,32 @@ class Template(object): def write_files(self, command, output_dir, vars): template_dir = self.template_dir() - if not os.path.exists(output_dir): - print("Creating directory %s" % output_dir) + if not self.exists(output_dir): + self.out("Creating directory %s" % output_dir) if not command.simulate: # Don't let copydir create this top-level directory, # since copydir will svn add it sometimes: - os.makedirs(output_dir) - copydir.copy_dir(template_dir, output_dir, - vars, - verbosity=command.verbose, - simulate=command.options.simulate, - interactive=command.interactive, - overwrite=command.options.overwrite, - indent=1, - template_renderer=self.template_renderer) - + self.makedirs(output_dir) + self.copydir.copy_dir( + template_dir, + output_dir, + vars, + verbosity=command.verbose, + simulate=command.options.simulate, + interactive=command.interactive, + overwrite=command.options.overwrite, + indent=1, + template_renderer=self.template_renderer + ) + + def makedirs(self, dir): # pragma: no cover + return os.makedirs(dir) + + def exists(self, path): # pragma: no cover + return os.path.exists(path) + + def out(self, msg): # pragma: no cover + print(msg) class TypeMapper(dict): @@ -109,7 +122,7 @@ def substitute_double_braces(content, values): return values[value] return double_brace_pattern.sub(double_bracerepl, content) -def _add_except(exc, info): +def _add_except(exc, info): # pragma: no cover if not hasattr(exc, 'args') or exc.args is None: return args = list(exc.args) diff --git a/pyramid/tests/test_scaffolds/__init__.py b/pyramid/tests/test_scaffolds/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/tests/test_scaffolds/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/tests/test_scaffolds/test_template.py b/pyramid/tests/test_scaffolds/test_template.py new file mode 100644 index 000000000..2943a0057 --- /dev/null +++ b/pyramid/tests/test_scaffolds/test_template.py @@ -0,0 +1,137 @@ +import unittest + +from pyramid.compat import bytes_ + +class TestTemplate(unittest.TestCase): + def _makeOne(self, name='whatever'): + from pyramid.scaffolds.template import Template + return Template(name) + + def test_template_renderer_success(self): + inst = self._makeOne() + result = inst.template_renderer('{{a}} {{b}}', {'a':'1', 'b':'2'}) + self.assertEqual(result, bytes_('1 2')) + + def test_template_renderer_expr_failure(self): + inst = self._makeOne() + self.assertRaises(AttributeError, inst.template_renderer, + '{{a.foo}}', {'a':'1', 'b':'2'}) + + def test_template_renderer_expr_success(self): + inst = self._makeOne() + result = inst.template_renderer('{{a.lower()}}', {'a':'A'}) + self.assertEqual(result, 'a') + + def test_template_renderer_expr_success_via_pipe(self): + inst = self._makeOne() + result = inst.template_renderer('{{b|c|a.lower()}}', {'a':'A'}) + self.assertEqual(result, 'a') + + def test_template_renderer_expr_success_via_pipe2(self): + inst = self._makeOne() + result = inst.template_renderer('{{b|a.lower()|c}}', {'a':'A'}) + self.assertEqual(result, 'a') + + def test_template_renderer_expr_value_is_None(self): + inst = self._makeOne() + result = inst.template_renderer('{{a}}', {'a':None}) + self.assertEqual(result, '') + + def test_module_dir(self): + import sys + import pkg_resources + package = sys.modules['pyramid.scaffolds.template'] + path = pkg_resources.resource_filename(package.__name__, '') + inst = self._makeOne() + result = inst.module_dir() + self.assertEqual(result, path) + + def test_template_dir__template_dir_is_None(self): + inst = self._makeOne() + self.assertRaises(AssertionError, inst.template_dir) + + def test_template_dir__template_dir_is_tuple(self): + inst = self._makeOne() + inst._template_dir = ('a', 'b') + self.assertEqual(inst.template_dir(), ('a', 'b')) + + def test_template_dir__template_dir_is_not_None(self): + import os + import sys + import pkg_resources + package = sys.modules['pyramid.scaffolds.template'] + path = pkg_resources.resource_filename(package.__name__, '') + inst = self._makeOne() + inst._template_dir ='foo' + result = inst.template_dir() + self.assertEqual(result, os.path.join(path, 'foo')) + + def test_write_files_path_exists(self): + import os + import sys + import pkg_resources + package = sys.modules['pyramid.scaffolds.template'] + path = pkg_resources.resource_filename(package.__name__, '') + inst = self._makeOne() + inst._template_dir = 'foo' + inst.exists = lambda *arg: True + copydir = DummyCopydir() + inst.copydir = copydir + command = DummyCommand() + inst.write_files(command, 'output dir', {'a':1}) + self.assertEqual(copydir.template_dir, os.path.join(path, 'foo')) + self.assertEqual(copydir.output_dir, 'output dir') + self.assertEqual(copydir.vars, {'a':1}) + self.assertEqual(copydir.kw, + {'template_renderer':inst.template_renderer, + 'indent':1, + 'verbosity':True, + 'simulate':True, + 'overwrite':False, + 'interactive':False, + }) + + def test_write_files_path_missing(self): + L = [] + inst = self._makeOne() + inst._template_dir = 'foo' + inst.exists = lambda *arg: False + inst.out = lambda *arg: None + inst.makedirs = lambda dir: L.append(dir) + copydir = DummyCopydir() + inst.copydir = copydir + command = DummyCommand() + inst.write_files(command, 'output dir', {'a':1}) + self.assertEqual(L, ['output dir']) + + def test_run(self): + L = [] + inst = self._makeOne() + inst._template_dir = 'foo' + inst.exists = lambda *arg: False + inst.out = lambda *arg: None + inst.makedirs = lambda dir: L.append(dir) + copydir = DummyCopydir() + inst.copydir = copydir + command = DummyCommand() + inst.run(command, 'output dir', {'a':1}) + self.assertEqual(L, ['output dir']) + +class DummyCopydir(object): + def copy_dir(self, template_dir, output_dir, vars, **kw): + self.template_dir = template_dir + self.output_dir = output_dir + self.vars = vars + self.kw = kw + +class DummyOptions(object): + simulate = True + overwrite = False + +class DummyCommand(object): + options = DummyOptions() + verbose = True + interactive = False + simulate = False + + -- cgit v1.2.3 From d206187ac822274e4583a8d5514e7b28f91a319e Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 05:12:51 -0400 Subject: make tests pass on py3 --- pyramid/tests/test_scaffolds/test_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyramid/tests/test_scaffolds/test_template.py b/pyramid/tests/test_scaffolds/test_template.py index 2943a0057..88a31e802 100644 --- a/pyramid/tests/test_scaffolds/test_template.py +++ b/pyramid/tests/test_scaffolds/test_template.py @@ -20,22 +20,22 @@ class TestTemplate(unittest.TestCase): def test_template_renderer_expr_success(self): inst = self._makeOne() result = inst.template_renderer('{{a.lower()}}', {'a':'A'}) - self.assertEqual(result, 'a') + self.assertEqual(result, b'a') def test_template_renderer_expr_success_via_pipe(self): inst = self._makeOne() result = inst.template_renderer('{{b|c|a.lower()}}', {'a':'A'}) - self.assertEqual(result, 'a') + self.assertEqual(result, b'a') def test_template_renderer_expr_success_via_pipe2(self): inst = self._makeOne() result = inst.template_renderer('{{b|a.lower()|c}}', {'a':'A'}) - self.assertEqual(result, 'a') + self.assertEqual(result, b'a') def test_template_renderer_expr_value_is_None(self): inst = self._makeOne() result = inst.template_renderer('{{a}}', {'a':None}) - self.assertEqual(result, '') + self.assertEqual(result, b'') def test_module_dir(self): import sys -- cgit v1.2.3 From fe219cdea55e40782c2ee8f825812530b4018ca4 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 05:22:14 -0400 Subject: add tests for scaffold init stuff --- pyramid/scaffolds/__init__.py | 9 +++------ pyramid/tests/test_scaffolds/test_init.py | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 pyramid/tests/test_scaffolds/test_init.py diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py index 93a5db12a..a42e18ab7 100644 --- a/pyramid/scaffolds/__init__.py +++ b/pyramid/scaffolds/__init__.py @@ -1,10 +1,7 @@ import binascii import os -from pyramid.compat import ( - print_, - native_ - ) +from pyramid.compat import native_ from pyramid.scaffolds.template import Template @@ -18,12 +15,12 @@ class PyramidTemplate(Template): vars['package_logger'] = package_logger return Template.pre(self, command, output_dir, vars) - def post(self, command, output_dir, vars): + def post(self, command, output_dir, vars): # pragma: no cover self.out('Welcome to Pyramid. Sorry for the convenience.') return Template.post(self, command, output_dir, vars) def out(self, msg): # pragma: no cover (replaceable testing hook) - print_(msg) + print(msg) class StarterProjectTemplate(PyramidTemplate): _template_dir = 'starter' diff --git a/pyramid/tests/test_scaffolds/test_init.py b/pyramid/tests/test_scaffolds/test_init.py new file mode 100644 index 000000000..6b038914a --- /dev/null +++ b/pyramid/tests/test_scaffolds/test_init.py @@ -0,0 +1,21 @@ +import unittest + +class TestPyramidTemplate(unittest.TestCase): + def _makeOne(self): + from pyramid.scaffolds import PyramidTemplate + return PyramidTemplate('name') + + def test_pre(self): + inst = self._makeOne() + vars = {'package':'one'} + inst.pre('command', 'output dir', vars) + self.assertTrue(vars['random_string']) + self.assertEqual(vars['package_logger'], 'one') + + def test_pre_root(self): + inst = self._makeOne() + vars = {'package':'root'} + inst.pre('command', 'output dir', vars) + self.assertTrue(vars['random_string']) + self.assertEqual(vars['package_logger'], 'app') + -- cgit v1.2.3 From eb1fc1689705c9d93149abf2f108f8fb15d72c4b Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 05:31:21 -0400 Subject: test logging_file_config --- pyramid/scripts/common.py | 5 +++-- pyramid/tests/test_scripts/test_common.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 pyramid/tests/test_scripts/test_common.py diff --git a/pyramid/scripts/common.py b/pyramid/scripts/common.py index bbf734bd2..dfff26449 100644 --- a/pyramid/scripts/common.py +++ b/pyramid/scripts/common.py @@ -2,7 +2,8 @@ import os from pyramid.compat import configparser from logging.config import fileConfig -def logging_file_config(config_file, fileConfig=fileConfig): +def logging_file_config(config_file, fileConfig=fileConfig, + configparser=configparser): """ Setup logging via the logging module's fileConfig function with the specified ``config_file``, if applicable. @@ -14,7 +15,7 @@ def logging_file_config(config_file, fileConfig=fileConfig): parser.read([config_file]) if parser.has_section('loggers'): config_file = os.path.abspath(config_file) - fileConfig( + return fileConfig( config_file, dict(__file__=config_file, here=os.path.dirname(config_file)) ) diff --git a/pyramid/tests/test_scripts/test_common.py b/pyramid/tests/test_scripts/test_common.py new file mode 100644 index 000000000..c62483fdc --- /dev/null +++ b/pyramid/tests/test_scripts/test_common.py @@ -0,0 +1,28 @@ +import unittest + +class Test_logging_file_config(unittest.TestCase): + def _callFUT(self, config_file): + from pyramid.scripts.common import logging_file_config + dummy_cp = DummyConfigParserModule + return logging_file_config(config_file, self.fileConfig, dummy_cp) + + def test_it(self): + config_file, dict = self._callFUT('/abc') + self.assertEqual(config_file, '/abc') + self.assertEqual(dict['__file__'], '/abc') + self.assertEqual(dict['here'], '/') + + def fileConfig(self, config_file, dict): + return config_file, dict + +class DummyConfigParser(object): + def read(self, x): + pass + + def has_section(self, name): + return True + +class DummyConfigParserModule(object): + ConfigParser = DummyConfigParser + + -- cgit v1.2.3 From 75f05a60f8754c9c5c234b9731dbc08646ba89a9 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 6 Oct 2011 05:40:47 -0400 Subject: document glue changes --- CHANGES.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 047db6472..2e2b2291d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,6 +16,20 @@ Backwards Incompatibilities - Pyramid no longer runs on Python 2.5 (which includes the most recent release of Jython, and the current version of GAE). +- The ``paster`` command is no longer the documented way to create projects, + start the server, or run debugging commands. To create projects from + scaffolds, ``paster create`` is replaced by the ``pcreate`` console script. + To serve up a project, ``paster serve`` is replaced by the ``pserve`` + console script. New console scripts named ``pshell``, ``pviews``, + ``proutes``, and ``ptweens`` do what their ``paster `` + equivalents used to do. Rationale: the Paste and PasteScript packages do + not run under Python 3. + +- The default WSGI server run as the result of ``pserve`` from newly rendered + scaffolding is now the ``wsgiref`` WSGI server instead of the + ``paste.httpserver`` server. Rationale: Rationale: the Paste and + PasteScript packages do not run under Python 3. + Dependencies ------------ @@ -27,3 +41,6 @@ Dependencies Python 3 compatibility purposes). It also, as a testing dependency, depends on WebTest>=1.3.1 for the same reason. +- Pyramid no longer depends on the Paste or PasteScript packages. + + -- cgit v1.2.3