Michael Merickel
2016-12-15 5b5b20be6d248beb40b33269306b61ea40f22ab8
Merge pull request #2864 from stevepiercy/scripts-optparse-to-argparse

Scripts optparse to argparse
25 files modified
697 ■■■■■ changed files
docs/conf.py 8 ●●●● patch | view | raw | blame | history
docs/pscripts/pcreate.rst 10 ●●●●● patch | view | raw | blame | history
docs/pscripts/pdistreport.rst 10 ●●●●● patch | view | raw | blame | history
docs/pscripts/prequest.rst 10 ●●●●● patch | view | raw | blame | history
docs/pscripts/proutes.rst 8 ●●●● patch | view | raw | blame | history
docs/pscripts/pserve.rst 8 ●●●● patch | view | raw | blame | history
docs/pscripts/pshell.rst 8 ●●●● patch | view | raw | blame | history
docs/pscripts/ptweens.rst 8 ●●●● patch | view | raw | blame | history
docs/pscripts/pviews.rst 8 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/template.py 8 ●●●● patch | view | raw | blame | history
pyramid/scripts/pcreate.py 168 ●●●● patch | view | raw | blame | history
pyramid/scripts/pdistreport.py 11 ●●●●● patch | view | raw | blame | history
pyramid/scripts/prequest.py 69 ●●●●● patch | view | raw | blame | history
pyramid/scripts/proutes.py 63 ●●●●● patch | view | raw | blame | history
pyramid/scripts/pserve.py 75 ●●●●● patch | view | raw | blame | history
pyramid/scripts/pshell.py 70 ●●●●● patch | view | raw | blame | history
pyramid/scripts/ptweens.py 27 ●●●● patch | view | raw | blame | history
pyramid/scripts/pviews.py 35 ●●●● patch | view | raw | blame | history
pyramid/tests/test_scaffolds/test_template.py 4 ●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_proutes.py 18 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_pserve.py 7 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_pshell.py 22 ●●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_ptweens.py 2 ●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_pviews.py 38 ●●●●● patch | view | raw | blame | history
setup.py 2 ●●● patch | view | raw | blame | history
docs/conf.py
@@ -51,11 +51,11 @@
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.doctest',
    'sphinx.ext.intersphinx',
    'sphinx.ext.todo',
    'sphinx.ext.viewcode',
    'repoze.sphinx.autointerface',
    'sphinxcontrib.programoutput',
    'sphinx.ext.viewcode',
    'sphinx.ext.intersphinx',
    'sphinxcontrib.autoprogram',
    'sphinx.ext.todo',
    # enable pylons_sphinx_latesturl when this branch is no longer "latest"
    # 'pylons_sphinx_latesturl',
    ]
docs/pscripts/pcreate.rst
@@ -1,13 +1,9 @@
.. index::
   single: pcreate; --help
    single: pcreate; --help
.. _pcreate_script:
``pcreate``
-----------
.. program-output:: pcreate --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.pcreate:PCreateCommand.parser
    :prog: pcreate
.. seealso:: :ref:`creating_a_project`
docs/pscripts/pdistreport.rst
@@ -1,13 +1,9 @@
.. index::
   single: pdistreport; --help
    single: pdistreport; --help
.. _pdistreport_script:
``pdistreport``
---------------
.. program-output:: pdistreport --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.pdistreport:get_parser()
    :prog: pdistreport
.. seealso:: :ref:`showing_distributions`
docs/pscripts/prequest.rst
@@ -1,13 +1,9 @@
.. index::
   single: prequest; --help
    single: prequest; --help
.. _prequest_script:
``prequest``
------------
.. program-output:: prequest --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.prequest:PRequestCommand.parser
    :prog: prequest
.. seealso:: :ref:`invoking_a_request`
docs/pscripts/proutes.rst
@@ -3,11 +3,7 @@
.. _proutes_script:
``proutes``
-----------
.. program-output:: proutes --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.proutes:PRoutesCommand.parser
    :prog: proutes
.. seealso:: :ref:`displaying_application_routes`
docs/pscripts/pserve.rst
@@ -3,11 +3,7 @@
.. _pserve_script:
``pserve``
----------
.. program-output:: pserve --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.pserve:PServeCommand.parser
    :prog: pserve
.. seealso:: :ref:`running_the_project_application`
docs/pscripts/pshell.rst
@@ -3,11 +3,7 @@
.. _pshell_script:
``pshell``
----------
.. program-output:: pshell --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.pshell:PShellCommand.parser
    :prog: pshell
.. seealso:: :ref:`interactive_shell`
docs/pscripts/ptweens.rst
@@ -3,11 +3,7 @@
.. _ptweens_script:
``ptweens``
-----------
.. program-output:: ptweens --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.ptweens:PTweensCommand.parser
    :prog: ptweens
.. seealso:: :ref:`displaying_tweens`
docs/pscripts/pviews.rst
@@ -3,11 +3,7 @@
.. _pviews_script:
``pviews``
----------
.. program-output:: pviews --help
   :prompt:
   :shell:
.. autoprogram:: pyramid.scripts.pviews:PViewsCommand.parser
    :prog: pviews
.. seealso:: :ref:`displaying_matching_views`
pyramid/scaffolds/template.py
@@ -81,7 +81,7 @@
        template_dir = self.template_dir()
        if not self.exists(output_dir):
            self.out("Creating directory %s" % output_dir)
            if not command.options.simulate:
            if not command.args.simulate:
                # Don't let copydir create this top-level directory,
                # since copydir will svn add it sometimes:
                self.makedirs(output_dir)
@@ -90,9 +90,9 @@
            output_dir,
            vars,
            verbosity=command.verbosity,
            simulate=command.options.simulate,
            interactive=command.options.interactive,
            overwrite=command.options.overwrite,
            simulate=command.args.simulate,
            interactive=command.args.interactive,
            overwrite=command.args.overwrite,
            indent=1,
            template_renderer=self.render_template,
            )
pyramid/scripts/pcreate.py
@@ -2,7 +2,7 @@
# (http://pythonpaste.org) Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
import optparse
import argparse
import os
import os.path
import pkg_resources
@@ -12,90 +12,93 @@
_bad_chars_re = re.compile('[^a-zA-Z0-9_]')
def main(argv=sys.argv, quiet=False):
    command = PCreateCommand(argv, quiet)
    try:
        return command.run()
    except KeyboardInterrupt: # pragma: no cover
    except KeyboardInterrupt:  # pragma: no cover
        return 1
class PCreateCommand(object):
    verbosity = 1 # required
    description = """\
    verbosity = 1  # required
    parser = argparse.ArgumentParser(description="""\
Render Pyramid scaffolding to an output directory.
Note: As of Pyramid 1.8, this command is deprecated. Use a specific
cookiecutter instead:
https://github.com/Pylons/?q=cookiecutter
"""
    usage = "usage: %prog [options] -s <scaffold> output_directory"
    parser = optparse.OptionParser(usage, description=description)
    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('-t', '--template',
                      dest='scaffold_name',
                      action='append',
                      help=('A backwards compatibility alias for '
                            '-s/--scaffold.  Add a scaffold to the '
                            'create process (multiple -t args accepted)'))
    parser.add_option('-l', '--list',
                      dest='list',
                      action='store_true',
                      help="List all available scaffold names")
    parser.add_option('--list-templates',
                      dest='list',
                      action='store_true',
                      help=("A backwards compatibility alias for -l/--list.  "
                            "List all available scaffold names."))
    parser.add_option('--package-name',
                      dest='package_name',
                      action='store',
                      type='string',
                      help='Package name to use. The name provided is assumed '
                           'to be a valid Python package name, and will not '
                           'be validated. By default the package name is '
                           'derived from the value of output_directory.')
    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')
    parser.add_option('--interactive',
                      dest='interactive',
                      action='store_true',
                      help='When a file would be overwritten, interrogate '
                           '(this is the default, but you may specify it to '
                           'override --overwrite)')
    parser.add_option('--ignore-conflicting-name',
                      dest='force_bad_name',
                      action='store_true',
                      default=False,
                      help='Do create a project even if the chosen name '
                           'is the name of an already existing / importable '
                           'package.')
""")
    parser.add_argument('-s', '--scaffold',
                        dest='scaffold_name',
                        action='append',
                        help=("Add a scaffold to the create process "
                              "(multiple -s args accepted)"))
    parser.add_argument('-t', '--template',
                        dest='scaffold_name',
                        action='append',
                        help=('A backwards compatibility alias for '
                              '-s/--scaffold.  Add a scaffold to the '
                              'create process (multiple -t args accepted)'))
    parser.add_argument('-l', '--list',
                        dest='list',
                        action='store_true',
                        help="List all available scaffold names")
    parser.add_argument('--list-templates',
                        dest='list',
                        action='store_true',
                        help=("A backwards compatibility alias for -l/--list. "
                              "List all available scaffold names."))
    parser.add_argument('--package-name',
                        dest='package_name',
                        action='store',
                        help='Package name to use. The name provided is '
                             'assumed to be a valid Python package name, and '
                             'will not be validated. By default the package '
                             'name is derived from the value of '
                             'output_directory.')
    parser.add_argument('--simulate',
                        dest='simulate',
                        action='store_true',
                        help='Simulate but do no work')
    parser.add_argument('--overwrite',
                        dest='overwrite',
                        action='store_true',
                        help='Always overwrite')
    parser.add_argument('--interactive',
                        dest='interactive',
                        action='store_true',
                        help='When a file would be overwritten, interrogate '
                             '(this is the default, but you may specify it to '
                             'override --overwrite)')
    parser.add_argument('--ignore-conflicting-name',
                        dest='force_bad_name',
                        action='store_true',
                        default=False,
                        help='Do create a project even if the chosen name '
                             'is the name of an already existing / importable '
                             'package.')
    parser.add_argument('output_directory',
                        nargs='?',
                        default=None,
                        help='The directory where the project will be '
                             'created.')
    pyramid_dist = pkg_resources.get_distribution("pyramid")
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
        self.options, self.args = self.parser.parse_args(argv[1:])
        if not self.options.interactive and not self.options.overwrite:
            self.options.interactive = True
        self.args = self.parser.parse_args(argv[1:])
        if not self.args.interactive and not self.args.overwrite:
            self.args.interactive = True
        self.scaffolds = self.all_scaffolds()
    def run(self):
        self._warn_pcreate_deprecated()
        if self.options.list:
        if self.args.list:
            return self.show_scaffolds()
        if not self.options.scaffold_name and not self.args:
            if not self.quiet: # pragma: no cover
        if not self.args.scaffold_name and not self.args.output_directory:
            if not self.quiet:  # pragma: no cover
                self.parser.print_help()
                self.out('')
                self.show_scaffolds()
@@ -103,30 +106,31 @@
        if not self.validate_input():
            return 2
        self._warn_pcreate_deprecated()
        return self.render_scaffolds()
    @property
    def output_path(self):
        return os.path.abspath(os.path.normpath(self.args[0]))
        return os.path.abspath(os.path.normpath(self.args.output_directory))
    @property
    def project_vars(self):
        output_dir = self.output_path
        project_name = os.path.basename(os.path.split(output_dir)[1])
        if self.options.package_name is None:
        if self.args.package_name is None:
            pkg_name = _bad_chars_re.sub(
                '', project_name.lower().replace('-', '_'))
            safe_name = pkg_resources.safe_name(project_name)
        else:
            pkg_name = self.options.package_name
            pkg_name = self.args.package_name
            safe_name = pkg_name
        egg_name = pkg_resources.to_filename(safe_name)
        # get pyramid package version
        pyramid_version = self.pyramid_dist.version
        ## map pyramid package version of the documentation branch ##
        # map pyramid package version of the documentation branch ##
        # if version ends with 'dev' then docs version is 'master'
        if self.pyramid_dist.version[-3:] == 'dev':
            pyramid_docs_branch = 'master'
@@ -148,11 +152,10 @@
            'pyramid_docs_branch': pyramid_docs_branch,
        }
    def render_scaffolds(self):
        props = self.project_vars
        output_dir = self.output_path
        for scaffold_name in self.options.scaffold_name:
        for scaffold_name in self.args.scaffold_name:
            for scaffold in self.scaffolds:
                if scaffold.name == scaffold_name:
                    scaffold.run(self, output_dir, props)
@@ -179,55 +182,58 @@
                scaffold_class = entry.load()
                scaffold = scaffold_class(entry.name)
                scaffolds.append(scaffold)
            except Exception as e: # pragma: no cover
            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
    def out(self, msg):  # pragma: no cover
        if not self.quiet:
            print(msg)
    def validate_input(self):
        if not self.options.scaffold_name:
            self.out('You must provide at least one scaffold name: -s <scaffold name>')
        if not self.args.scaffold_name:
            self.out('You must provide at least one scaffold name: '
                     '-s <scaffold name>')
            self.out('')
            self.show_scaffolds()
            return False
        if not self.args:
        if not self.args.output_directory:
            self.out('You must provide a project name')
            return False
        available = [x.name for x in self.scaffolds]
        diff = set(self.options.scaffold_name).difference(available)
        diff = set(self.args.scaffold_name).difference(available)
        if diff:
            self.out('Unavailable scaffolds: %s' % ", ".join(sorted(diff)))
            return False
        pkg_name = self.project_vars['package']
        if pkg_name == 'site' and not self.options.force_bad_name:
        if pkg_name == 'site' and not self.args.force_bad_name:
            self.out('The package name "site" has a special meaning in '
                     'Python. Are you sure you want to use it as your '
                     'project\'s name?')
            return self.confirm_bad_name('Really use "{0}"?: '.format(pkg_name))
            return self.confirm_bad_name('Really use "{0}"?: '.format(
                pkg_name))
        # check if pkg_name can be imported (i.e. already exists in current
        # $PYTHON_PATH, if so - let the user confirm
        pkg_exists = True
        try:
            __import__(pkg_name, globals(), locals(), [], 0) # use absolute imports
            # use absolute imports
            __import__(pkg_name, globals(), locals(), [], 0)
        except ImportError as error:
            pkg_exists = False
        if not pkg_exists:
            return True
        if self.options.force_bad_name:
        if self.args.force_bad_name:
            return True
        self.out('A package named "{0}" already exists, are you sure you want '
                 'to use it as your project\'s name?'.format(pkg_name))
        return self.confirm_bad_name('Really use "{0}"?: '.format(pkg_name))
    def confirm_bad_name(self, prompt): # pragma: no cover
    def confirm_bad_name(self, prompt):  # pragma: no cover
        answer = input_('{0} [y|N]: '.format(prompt))
        return answer.strip().lower() == 'y'
@@ -238,5 +244,5 @@
https://github.com/pylons/?query=cookiecutter
''')
if __name__ == '__main__': # pragma: no cover
if __name__ == '__main__':  # pragma: no cover
    sys.exit(main() or 0)
pyramid/scripts/pdistreport.py
@@ -1,7 +1,7 @@
import sys
import platform
import pkg_resources
import optparse
import argparse
from operator import itemgetter
def out(*args): # pragma: no cover
@@ -10,12 +10,15 @@
        sys.stdout.write(' ')
    sys.stdout.write('\n')
def get_parser():
    parser = argparse.ArgumentParser(
        description="Show Python distribution versions and locations in use")
    return parser
def main(argv=sys.argv, pkg_resources=pkg_resources, platform=platform.platform,
         out=out):
    # all args except argv are for unit testing purposes only
    description = "Show Python distribution versions and locations in use"
    usage = "usage: %prog"
    parser = optparse.OptionParser(usage, description=description)
    parser = get_parser()
    parser.parse_args(argv[1:])
    packages = []
    for distribution in pkg_resources.working_set:
pyramid/scripts/prequest.py
@@ -1,5 +1,5 @@
import base64
import optparse
import argparse
import sys
import textwrap
@@ -39,64 +39,81 @@
    If the path is relative (doesn't begin with "/") it is interpreted as
    relative to "/".  The path passed to this script should be URL-quoted.
    The path can be succeeded with a query string (e.g. `/path?a=1&=b2').
    The path can be succeeded with a query string (e.g. '/path?a=1&=b2').
    The variable "environ['paste.command_request']" will be set to "True" in
    the request's WSGI environment, so your application can distinguish these
    calls from normal requests.
    """
    usage = "usage: %prog config_uri path_info [args/options]"
    parser = optparse.OptionParser(
        usage=usage,
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description)
        )
    parser.add_option(
    parser.add_argument(
        '-n', '--app-name',
        dest='app_name',
        metavar='NAME',
        help=(
            "Load the named application from the config file (default 'main')"
        ),
        type="string",
        )
    parser.add_option(
    parser.add_argument(
        '--header',
        dest='headers',
        metavar='NAME:VALUE',
        type='string',
        action='append',
        help=(
            "Header to add to request (you can use this option multiple times)"
        ),
    )
    parser.add_option(
    parser.add_argument(
        '-d', '--display-headers',
        dest='display_headers',
        action='store_true',
        help='Display status and headers before the response body'
        )
    parser.add_option(
    parser.add_argument(
        '-m', '--method',
        dest='method',
        choices=['GET', 'HEAD', 'POST', 'PUT', 'PATCH','DELETE',
                 'PROPFIND', 'OPTIONS'],
        type='choice',
        help='Request method type (GET, POST, PUT, PATCH, DELETE, '
             'PROPFIND, OPTIONS)',
        )
    parser.add_option(
    parser.add_argument(
        '-l', '--login',
        dest='login',
        type='string',
        help='HTTP basic auth username:password pair',
        )
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
        )
    parser.add_argument(
        'path_info',
        nargs='?',
        default=None,
        help='The path of the request.',
        )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help='Arbitrary options to override those in the [app:main] section '
             'of the configuration file.',
    )
    get_app = staticmethod(get_app)
    stdin = sys.stdin
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
        self.options, self.args = self.parser.parse_args(argv[1:])
        self.args = self.parser.parse_args(argv[1:])
    def out(self, msg): # pragma: no cover
        if not self.quiet:
@@ -106,11 +123,11 @@
        setup_logging(app_spec)
    def run(self):
        if not len(self.args) >= 2:
        if not self.args.config_uri or not self.args.path_info:
            self.out('You must provide at least two arguments')
            return 2
        app_spec = self.args[0]
        path = self.args[1]
        app_spec = self.args.config_uri
        path = self.args.path_info
        self.configure_logging(app_spec)
@@ -125,12 +142,12 @@
        path = url_unquote(path)
        headers = {}
        if self.options.login:
            enc = base64.b64encode(self.options.login.encode('ascii'))
        if self.args.login:
            enc = base64.b64encode(self.args.login.encode('ascii'))
            headers['Authorization'] = 'Basic ' + enc.decode('ascii')
        if self.options.headers:
            for item in self.options.headers:
        if self.args.headers:
            for item in self.args.headers:
                if ':' not in item:
                    self.out(
                        "Bad --header=%s option, value must be in the form "
@@ -139,10 +156,10 @@
                name, value = item.split(':', 1)
                headers[name] = value.strip()
        app = self.get_app(app_spec, self.options.app_name,
                options=parse_vars(self.args[2:]))
        app = self.get_app(app_spec, self.args.app_name,
                options=parse_vars(self.args.config_vars))
        request_method = (self.options.method or 'GET').upper()
        request_method = (self.args.method or 'GET').upper()
        environ = {
            'REQUEST_METHOD': request_method,
@@ -177,7 +194,7 @@
        request = Request.blank(path, environ=environ)
        response = request.get_response(app)
        if self.options.display_headers:
        if self.args.display_headers:
            self.out(response.status)
            for name, value in response.headerlist:
                self.out('%s: %s' % (name, value))
pyramid/scripts/proutes.py
@@ -1,5 +1,5 @@
import fnmatch
import optparse
import argparse
import sys
import textwrap
import re
@@ -247,24 +247,41 @@
    """
    bootstrap = (bootstrap,)
    stdout = sys.stdout
    usage = '%prog config_uri'
    ConfigParser = configparser.ConfigParser # testing
    parser = optparse.OptionParser(
        usage,
    ConfigParser = configparser.ConfigParser  # testing
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description)
    )
    parser.add_option('-g', '--glob',
                      action='store', type='string', dest='glob',
                      default='', help='Display routes matching glob pattern')
        )
    parser.add_argument('-g', '--glob',
                        action='store',
                        dest='glob',
                        default='',
                        help='Display routes matching glob pattern')
    parser.add_option('-f', '--format',
                      action='store', type='string', dest='format',
                      default='', help=('Choose which columns to display, this '
                                        'will override the format key in the '
                                        '[proutes] ini section'))
    parser.add_argument('-f', '--format',
                        action='store',
                        dest='format',
                        default='',
                        help=('Choose which columns to display, this will '
                              'override the format key in the [proutes] ini '
                              'section'))
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
        )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help='Arbitrary options to override those in the [app:main] section '
             'of the configuration file.',
    )
    def __init__(self, argv, quiet=False):
        self.options, self.args = self.parser.parse_args(argv[1:])
        self.args = self.parser.parse_args(argv[1:])
        self.quiet = quiet
        self.available_formats = [
            'name', 'pattern', 'view', 'method'
@@ -312,19 +329,19 @@
        return config.get_routes_mapper()
    def run(self, quiet=False):
        if not self.args:
        if not self.args.config_uri:
            self.out('requires a config file argument')
            return 2
        config_uri = self.args[0]
        env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:]))
        config_uri = self.args.config_uri
        env = self.bootstrap[0](config_uri, options=parse_vars(self.args.config_vars))
        registry = env['registry']
        mapper = self._get_mapper(registry)
        self.proutes_file_config(config_uri)
        if self.options.format:
            columns = self.options.format.split(',')
        if self.args.format:
            columns = self.args.format.split(',')
            self.column_format = [x.strip() for x in columns]
        is_valid = self.validate_formats(self.column_format)
@@ -361,9 +378,9 @@
            route_data = get_route_data(route, registry)
            for name, pattern, view, method in route_data:
                if self.options.glob:
                    match = (fnmatch.fnmatch(name, self.options.glob) or
                             fnmatch.fnmatch(pattern, self.options.glob))
                if self.args.glob:
                    match = (fnmatch.fnmatch(name, self.args.glob) or
                             fnmatch.fnmatch(pattern, self.args.glob))
                    if not match:
                        continue
pyramid/scripts/pserve.py
@@ -8,7 +8,7 @@
# Code taken also from QP: http://www.mems-exchange.org/software/qp/ From
# lib/site.py
import optparse
import argparse
import os
import re
import sys
@@ -41,7 +41,6 @@
class PServeCommand(object):
    usage = '%prog config_uri [var=value]'
    description = """\
    This command serves a web application that uses a PasteDeploy
    configuration file for the server and application.
@@ -51,54 +50,68 @@
    """
    default_verbosity = 1
    parser = optparse.OptionParser(
        usage,
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description)
        )
    parser.add_option(
    parser.add_argument(
        '-n', '--app-name',
        dest='app_name',
        metavar='NAME',
        help="Load the named application (default main)")
    parser.add_option(
    parser.add_argument(
        '-s', '--server',
        dest='server',
        metavar='SERVER_TYPE',
        help="Use the named server.")
    parser.add_option(
    parser.add_argument(
        '--server-name',
        dest='server_name',
        metavar='SECTION_NAME',
        help=("Use the named server as defined in the configuration file "
              "(default: main)"))
    parser.add_option(
    parser.add_argument(
        '--reload',
        dest='reload',
        action='store_true',
        help="Use auto-restart file monitor")
    parser.add_option(
    parser.add_argument(
        '--reload-interval',
        dest='reload_interval',
        default=1,
        help=("Seconds between checking files (low number can cause "
              "significant CPU usage)"))
    parser.add_option(
    parser.add_argument(
        '-b', '--browser',
        dest='browser',
        action='store_true',
        help="Open a web browser to server url")
    parser.add_option(
    parser.add_argument(
        '-v', '--verbose',
        default=default_verbosity,
        dest='verbose',
        action='count',
        help="Set verbose level (default " + str(default_verbosity) + ")")
    parser.add_option(
    parser.add_argument(
        '-q', '--quiet',
        action='store_const',
        const=0,
        dest='verbose',
        help="Suppress verbose output")
    parser.add_argument(
        'config_uri',
        nargs='?',
        default=None,
        help='The URI to the configuration file.',
        )
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
    ConfigParser = configparser.ConfigParser  # testing
    loadapp = staticmethod(loadapp)  # testing
@@ -107,17 +120,17 @@
    _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I)
    def __init__(self, argv, quiet=False):
        self.options, self.args = self.parser.parse_args(argv[1:])
        self.args = self.parser.parse_args(argv[1:])
        if quiet:
            self.options.verbose = 0
            self.args.verbose = 0
        self.watch_files = []
    def out(self, msg): # pragma: no cover
        if self.options.verbose > 0:
        if self.args.verbose > 0:
            print(msg)
    def get_options(self):
        restvars = self.args[1:]
    def get_config_vars(self):
        restvars = self.args.config_vars
        return parse_vars(restvars)
    def pserve_file_config(self, filename, global_conf=None):
@@ -147,13 +160,13 @@
            self.watch_files.append(os.path.abspath(file))
    def run(self):  # pragma: no cover
        if not self.args:
        if not self.args.config_uri:
            self.out('You must give a config file')
            return 2
        app_spec = self.args[0]
        app_spec = self.args.config_uri
        vars = self.get_options()
        app_name = self.options.app_name
        vars = self.get_config_vars()
        app_name = self.args.app_name
        base = os.getcwd()
        if not self._scheme_re.search(app_spec):
@@ -161,16 +174,16 @@
            app_spec = 'config:' + app_spec
        else:
            config_path = None
        server_name = self.options.server_name
        if self.options.server:
        server_name = self.args.server_name
        if self.args.server:
            server_spec = 'egg:pyramid'
            assert server_name is None
            server_name = self.options.server
            server_name = self.args.server
        else:
            server_spec = app_spec
        # do not open the browser on each reload so check hupper first
        if self.options.browser and not hupper.is_active():
        if self.args.browser and not hupper.is_active():
            def open_browser():
                context = loadcontext(
                    SERVER, app_spec, name=server_name, relative_to=base,
@@ -182,13 +195,13 @@
            t.setDaemon(True)
            t.start()
        if self.options.reload and not hupper.is_active():
            if self.options.verbose > 1:
        if self.args.reload and not hupper.is_active():
            if self.args.verbose > 1:
                self.out('Running reloading file monitor')
            hupper.start_reloader(
                'pyramid.scripts.pserve.main',
                reload_interval=int(self.options.reload_interval),
                verbose=self.options.verbose,
                reload_interval=int(self.args.reload_interval),
                verbose=self.args.verbose,
            )
            return 0
@@ -207,7 +220,7 @@
        app = self.loadapp(
            app_spec, name=app_name, relative_to=base, global_conf=vars)
        if self.options.verbose > 0:
        if self.args.verbose > 0:
            if hasattr(os, 'getpid'):
                msg = 'Starting server in PID %i.' % os.getpid()
            else:
@@ -217,7 +230,7 @@
        try:
            server(app)
        except (SystemExit, KeyboardInterrupt) as e:
            if self.options.verbose > 1:
            if self.args.verbose > 1:
                raise
            if str(e):
                msg = ' ' + str(e)
pyramid/scripts/pshell.py
@@ -1,5 +1,5 @@
from code import interact
import optparse
import argparse
import os
import sys
import textwrap
@@ -28,13 +28,12 @@
class PShellCommand(object):
    usage = '%prog config_uri'
    description = """\
    Open an interactive shell with a Pyramid app loaded.  This command
    accepts one positional argument named "config_uri" which specifies the
    PasteDeploy config file to use for the interactive shell. The format is
    "inifile#name". If the name is left off, the Pyramid default application
    will be assumed.  Example: "pshell myapp.ini#main"
    will be assumed.  Example: "pshell myapp.ini#main".
    If you do not point the loader directly at the section of the ini file
    containing your Pyramid application, the command will attempt to
@@ -45,26 +44,38 @@
    bootstrap = (bootstrap,)  # for testing
    pkg_resources = pkg_resources  # for testing
    parser = optparse.OptionParser(
        usage,
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description)
        )
    parser.add_option('-p', '--python-shell',
                      action='store', type='string', dest='python_shell',
                      default='',
                      help=('Select the shell to use. A list of possible '
                            'shells is available using the --list-shells '
                            'option.'))
    parser.add_option('-l', '--list-shells',
                      dest='list',
                      action='store_true',
                      help='List all available shells.')
    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."))
    parser.add_argument('-p', '--python-shell',
                        action='store',
                        dest='python_shell',
                        default='',
                        help=('Select the shell to use. A list of possible '
                              'shells is available using the --list-shells '
                              'option.'))
    parser.add_argument('-l', '--list-shells',
                        dest='list',
                        action='store_true',
                        help='List all available shells.')
    parser.add_argument('--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."))
    parser.add_argument('config_uri',
                        nargs='?',
                        default=None,
                        help='The URI to the configuration file.')
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
    ConfigParser = configparser.ConfigParser # testing
    default_runner = python_shell_runner # testing
@@ -77,7 +88,7 @@
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
        self.options, self.args = self.parser.parse_args(argv[1:])
        self.args = self.parser.parse_args(argv[1:])
    def pshell_file_config(self, filename):
        config = self.ConfigParser()
@@ -106,18 +117,19 @@
            print(msg)
    def run(self, shell=None):
        if self.options.list:
        if self.args.list:
            return self.show_shells()
        if not self.args:
        if not self.args.config_uri:
            self.out('Requires a config file argument')
            return 2
        config_uri = self.args[0]
        config_uri = self.args.config_uri
        config_file = config_uri.split('#', 1)[0]
        setup_logging(config_file)
        self.pshell_file_config(config_file)
        # bootstrap the environ
        env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:]))
        env = self.bootstrap[0](config_uri,
                                options=parse_vars(self.args.config_vars))
        # remove the closer from the env
        self.closer = env.pop('closer')
@@ -132,8 +144,8 @@
            '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.args.setup:
            self.setup = self.args.setup
        if self.setup:
            # store the env before muddling it with the script
@@ -214,7 +226,7 @@
        shells = self.find_all_shells()
        shell = None
        user_shell = self.options.python_shell.lower()
        user_shell = self.args.python_shell.lower()
        if not user_shell:
            preferred_shells = self.preferred_shells
pyramid/scripts/ptweens.py
@@ -1,4 +1,4 @@
import optparse
import argparse
import sys
import textwrap
@@ -14,7 +14,6 @@
    return command.run()
class PTweensCommand(object):
    usage = '%prog config_uri'
    description = """\
    Print all implicit and explicit tween objects used by a Pyramid
    application.  The handler output includes whether the system is using an
@@ -28,9 +27,21 @@
    will be assumed.  Example: "ptweens myapp.ini#main".
    """
    parser = optparse.OptionParser(
        usage,
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description),
        )
    parser.add_argument('config_uri',
                        nargs='?',
                        default=None,
                        help='The URI to the configuration file.')
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help='Arbitrary options to override those in the [app:main] section '
             'of the configuration file.',
        )
    stdout = sys.stdout
@@ -38,7 +49,7 @@
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
        self.options, self.args = self.parser.parse_args(argv[1:])
        self.args = self.parser.parse_args(argv[1:])
    def _get_tweens(self, registry):
        from pyramid.config import Configurator
@@ -59,11 +70,11 @@
        self.out(fmt % ('-', MAIN))
    def run(self):
        if not self.args:
        if not self.args.config_uri:
            self.out('Requires a config file argument')
            return 2
        config_uri = self.args[0]
        env = self.bootstrap[0](config_uri, options=parse_vars(self.args[1:]))
        config_uri = self.args.config_uri
        env = self.bootstrap[0](config_uri, options=parse_vars(self.args.config_vars))
        registry = env['registry']
        tweens = self._get_tweens(registry)
        if tweens is not None:
pyramid/scripts/pviews.py
@@ -1,4 +1,4 @@
import optparse
import argparse
import sys
import textwrap
@@ -13,7 +13,6 @@
    return command.run()
class PViewsCommand(object):
    usage = '%prog config_uri url'
    description = """\
    Print, for a given URL, the views that might match. Underneath each
    potentially matching route, list the predicates required. Underneath
@@ -28,16 +27,34 @@
    """
    stdout = sys.stdout
    parser = optparse.OptionParser(
        usage,
    parser = argparse.ArgumentParser(
        description=textwrap.dedent(description)
        )
    parser.add_argument('config_uri',
                        nargs='?',
                        default=None,
                        help='The URI to the configuration file.')
    parser.add_argument('url',
                        nargs='?',
                        default=None,
                        help='The path info portion of the URL.')
    parser.add_argument(
        'config_vars',
        nargs='*',
        default=(),
        help="Variables required by the config file. For example, "
             "`http_port=%%(http_port)s` would expect `http_port=8080` to be "
             "passed here.",
        )
    bootstrap = (bootstrap,) # testing
    def __init__(self, argv, quiet=False):
        self.quiet = quiet
        self.options, self.args = self.parser.parse_args(argv[1:])
        self.args = self.parser.parse_args(argv[1:])
    def out(self, msg): # pragma: no cover
        if not self.quiet:
@@ -230,16 +247,16 @@
                self.out("%sview predicates (%s)" % (indent, predicate_text))
    def run(self):
        if len(self.args) < 2:
        if not self.args.config_uri or not self.args.url:
            self.out('Command requires a config file arg and a url arg')
            return 2
        config_uri = self.args[0]
        url = self.args[1]
        config_uri = self.args.config_uri
        url = self.args.url
        if not url.startswith('/'):
            url = '/%s' % url
        request = Request.blank(url)
        env = self.bootstrap[0](config_uri, options=parse_vars(self.args[2:]),
        env = self.bootstrap[0](config_uri, options=parse_vars(self.args.config_vars),
                                request=request)
        view = self._find_view(request)
        self.out('')
pyramid/tests/test_scaffolds/test_template.py
@@ -143,13 +143,13 @@
        self.vars = vars
        self.kw = kw
class DummyOptions(object):
class DummyArgs(object):
    simulate = False
    overwrite = False
    interactive = False
class DummyCommand(object):
    options = DummyOptions()
    args = DummyArgs()
    verbosity = 1
pyramid/tests/test_scripts/test_proutes.py
@@ -20,7 +20,7 @@
    def _makeOne(self):
        cmd = self._getTargetClass()([])
        cmd.bootstrap = (dummy.DummyBootstrap(),)
        cmd.args = ('/foo/bar/myapp.ini#myapp',)
        cmd.args.config_uri = '/foo/bar/myapp.ini#myapp'
        return cmd
@@ -38,7 +38,8 @@
    def test_good_args(self):
        cmd = self._getTargetClass()([])
        cmd.bootstrap = (dummy.DummyBootstrap(),)
        cmd.args = ('/foo/bar/myapp.ini#myapp', 'a=1')
        cmd.args.config_uri = '/foo/bar/myapp.ini#myapp'
        cmd.args.config_args = ('a=1',)
        route = dummy.DummyRoute('a', '/a')
        mapper = dummy.DummyMapper(route)
        cmd._get_mapper = lambda *arg: mapper
@@ -52,7 +53,8 @@
    def test_bad_args(self):
        cmd = self._getTargetClass()([])
        cmd.bootstrap = (dummy.DummyBootstrap(),)
        cmd.args = ('/foo/bar/myapp.ini#myapp', 'a')
        cmd.args.config_uri = '/foo/bar/myapp.ini#myapp'
        cmd.args.config_vars = ('a',)
        route = dummy.DummyRoute('a', '/a')
        mapper = dummy.DummyMapper(route)
        cmd._get_mapper = lambda *arg: mapper
@@ -586,7 +588,7 @@
        )
        command = self._makeOne()
        command.options.glob = '*foo*'
        command.args.glob = '*foo*'
        L = []
        command.out = L.append
@@ -618,8 +620,8 @@
        )
        command = self._makeOne()
        command.options.glob = '*foo*'
        command.options.format = 'method,name'
        command.args.glob = '*foo*'
        command.args.format = 'method,name'
        L = []
        command.out = L.append
        command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),)
@@ -648,8 +650,8 @@
        )
        command = self._makeOne()
        command.options.glob = '*foo*'
        command.options.format = 'predicates,name,pattern'
        command.args.glob = '*foo*'
        command.args.format = 'predicates,name,pattern'
        L = []
        command.out = L.append
        command.bootstrap = (dummy.DummyBootstrap(registry=config.registry),)
pyramid/tests/test_scripts/test_pserve.py
@@ -36,10 +36,11 @@
        self.assertEqual(result, 2)
        self.assertEqual(self.out_.getvalue(), 'You must give a config file')
    def test_get_options_no_command(self):
    def test_config_vars_no_command(self):
        inst = self._makeOne()
        inst.args = ['foo', 'a=1', 'b=2']
        result = inst.get_options()
        inst.args.config_uri = 'foo'
        inst.args.config_vars = ['a=1', 'b=2']
        result = inst.get_config_vars()
        self.assertEqual(result, {'a': '1', 'b': '2'})
    def test_parse_vars_good(self):
pyramid/tests/test_scripts/test_pshell.py
@@ -19,8 +19,10 @@
            self.config_factory = dummy.DummyConfigParserFactory()
            cmd.ConfigParser = self.config_factory
        if patch_args:
            self.args = ('/foo/bar/myapp.ini#myapp',)
            cmd.args = self.args
            class Args(object): pass
            self.args = Args()
            self.args.config_uri = '/foo/bar/myapp.ini#myapp'
            cmd.args.config_uri = self.args.config_uri
        if patch_options:
            class Options(object): pass
            self.options = Options()
@@ -71,7 +73,7 @@
        self._makeEntryPoints(command, {})
        command.default_runner = shell
        command.options.python_shell = 'unknown_python_shell'
        command.args.python_shell = 'unknown_python_shell'
        result = command.run()
        self.assertEqual(result, 1)
        self.assertEqual(
@@ -95,7 +97,7 @@
            }
        )
        command.options.python_shell = 'ipython'
        command.args.python_shell = 'ipython'
        command.run()
        self.assertTrue(self.config_factory.parser)
@@ -140,7 +142,7 @@
        shell = command.make_shell()
        self.assertEqual(shell, dshell)
        command.options.python_shell = 'ipython'
        command.args.python_shell = 'ipython'
        self.assertRaises(ValueError, command.make_shell)
        self._makeEntryPoints(
@@ -152,15 +154,15 @@
            }
        )
        command.options.python_shell = 'ipython'
        command.args.python_shell = 'ipython'
        shell = command.make_shell()
        self.assertEqual(shell, ipshell)
        command.options.python_shell = 'bpython'
        command.args.python_shell = 'bpython'
        shell = command.make_shell()
        self.assertEqual(shell, bpshell)
        command.options.python_shell = 'python'
        command.args.python_shell = 'python'
        shell = command.make_shell()
        self.assertEqual(shell, dshell)
@@ -291,7 +293,7 @@
        model = dummy.Dummy()
        self.config_factory.items = [('setup', 'abc'),
                                     ('m', model)]
        command.options.setup = setup
        command.args.setup = setup
        shell = dummy.DummyShell()
        command.run(shell)
        self.assertTrue(self.config_factory.parser)
@@ -365,7 +367,7 @@
            }
        )
        command.options.list = True
        command.args.list = True
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(out_calls, [
pyramid/tests/test_scripts/test_ptweens.py
@@ -9,7 +9,7 @@
    def _makeOne(self):
        cmd = self._getTargetClass()([])
        cmd.bootstrap = (dummy.DummyBootstrap(),)
        cmd.args = ('/foo/bar/myapp.ini#myapp',)
        cmd.args.config_uri = '/foo/bar/myapp.ini#myapp'
        return cmd
    def test_command_no_tweens(self):
pyramid/tests/test_scripts/test_pviews.py
@@ -9,7 +9,7 @@
    def _makeOne(self, registry=None):
        cmd = self._getTargetClass()([])
        cmd.bootstrap = (dummy.DummyBootstrap(registry=registry),)
        cmd.args = ('/foo/bar/myapp.ini#myapp',)
        cmd.args.config_uri = '/foo/bar/myapp.ini#myapp'
        return cmd
    def _makeRequest(self, url, registry):
@@ -242,7 +242,8 @@
        L = []
        command.out = L.append
        command._find_view = lambda arg1: None
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -255,7 +256,8 @@
        L = []
        command.out = L.append
        command._find_view = lambda arg1: None
        command.args = ('/foo/bar/myapp.ini#myapp', 'a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = 'a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -269,7 +271,8 @@
        command.out = L.append
        view = dummy.DummyView(context='context', view_name='a')
        command._find_view = lambda arg1: view
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -287,7 +290,8 @@
        def view(): pass
        view.__request_attrs__ = {'context': 'context', 'view_name': 'a'}
        command._find_view = lambda arg1: view
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -305,7 +309,8 @@
        view = dummy.DummyView(context='context', view_name='a')
        view.__permission__ = 'test'
        command._find_view = lambda arg1: view
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -326,7 +331,8 @@
        view = dummy.DummyView(context='context', view_name='a')
        view.__predicates__ = [predicate]
        command._find_view = lambda arg1: view
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -346,7 +352,8 @@
        view = dummy.DummyView(context='context', view_name='a',
                         matched_route=route, subpath='')
        command._find_view = lambda arg1: view
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -374,7 +381,8 @@
        multiview2 = dummy.DummyMultiView(multiview1, context='context',
                                    view_name='a')
        command._find_view = lambda arg1: multiview2
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -397,7 +405,8 @@
        view = dummy.DummyView(context='context', view_name='a',
                         matched_route=route, subpath='')
        command._find_view = lambda arg1: view
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -423,7 +432,8 @@
        view.__view_attr__ = 'call'
        multiview = dummy.DummyMultiView(view, context='context', view_name='a')
        command._find_view = lambda arg1: multiview
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -444,7 +454,8 @@
        view.__permission__ = 'test'
        multiview = dummy.DummyMultiView(view, context='context', view_name='a')
        command._find_view = lambda arg1: multiview
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
@@ -468,7 +479,8 @@
        view.__predicates__ = [predicate]
        multiview = dummy.DummyMultiView(view, context='context', view_name='a')
        command._find_view = lambda arg1: multiview
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        command.args.config_uri = '/foo/bar/myapp.ini#myapp'
        command.args.url = '/a'
        result = command.run()
        self.assertEqual(result, 0)
        self.assertEqual(L[1], 'URL = /a')
setup.py
@@ -64,7 +64,7 @@
    'repoze.sphinx.autointerface',
    'pylons_sphinx_latesturl',
    'pylons-sphinx-themes',
    'sphinxcontrib-programoutput',
    'sphinxcontrib-autoprogram',
    ]
testing_extras = tests_require + [