diff options
Diffstat (limited to 'docs/narr/commandline.rst')
| -rw-r--r-- | docs/narr/commandline.rst | 1057 |
1 files changed, 1057 insertions, 0 deletions
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst new file mode 100644 index 000000000..6cd90d42f --- /dev/null +++ b/docs/narr/commandline.rst @@ -0,0 +1,1057 @@ +.. _command_line_chapter: + +Command-Line Pyramid +==================== + +Your :app:`Pyramid` application can be controlled and inspected using a variety +of command-line utilities. These utilities are documented in this chapter. + + +.. index:: + pair: matching views; printing + single: pviews + +.. _displaying_matching_views: + +Displaying Matching Views for a Given URL +----------------------------------------- + +.. seealso:: See also the output of :ref:`pviews --help <pviews_script>`. + +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 ``pviews`` command in a terminal window to print a summary of +matching routes and views for a given URL in your 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 the URL to test for +matching views. The ``section_name`` may be omitted; if it is, it's considered +to be ``main``. + +Here is an example for a simple view configuration using :term:`traversal`: + +.. code-block:: text + :linenos: + + $ $VENV/bin/pviews development.ini#tutorial /FrontPage + + URL = /FrontPage + + context: <tutorial.models.Page object at 0xa12536c> + view name: + + View: + ----- + tutorial.views.view_page + required permission = view + +The output always has the requested URL at the top and below that all the views +that matched with their view configuration details. In this example only one +view matches, so there is just a single *View* section. For each matching view, +the full code path to the associated view callable is shown, along with any +permissions and predicates that are part of that view configuration. + +A more complex configuration might generate something like this: + +.. code-block:: text + :linenos: + + $ $VENV/bin/pviews development.ini#shootout /about + + URL = /about + + context: <shootout.models.RootFactory object at 0xa56668c> + view name: about + + Route: + ------ + route name: about + route pattern: /about + route path: /about + subpath: + route predicates (request method = GET) + + View: + ----- + shootout.views.about_view + required permission = view + view predicates (request_param testing, header X/header) + + Route: + ------ + route name: about_post + route pattern: /about + route path: /about + subpath: + route predicates (request method = POST) + + View: + ----- + shootout.views.about_view_post + required permission = view + view predicates (request_param test) + + View: + ----- + shootout.views.about_view_post2 + required permission = view + view predicates (request_param test2) + +In this case, we are dealing with a :term:`URL dispatch` application. This +specific URL has two matching routes. The matching route information is +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, ``pviews`` will simply print out a *Not +found* message. + + +.. index:: + single: interactive shell + single: pshell + +.. _interactive_shell: + +The Interactive Shell +--------------------- + +.. seealso:: See also the output of :ref:`pshell --help <pshell_script>`. + +Once you've installed your program for development using ``pip install -e .``, +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 ``pshell`` command line utility. + +The argument to ``pshell`` follows the format ``config_file#section_name`` +where ``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. For example, your application ``.ini`` file might +have an ``[app:main]`` section that looks like so: + +.. code-block:: ini + :linenos: + + [app:main] + use = egg:MyProject + pyramid.reload_templates = true + pyramid.debug_authorization = false + pyramid.debug_notfound = false + pyramid.debug_templates = true + pyramid.default_locale_name = en + +If so, you can use the following command to invoke a debug shell using the name +``main`` as a section name: + +.. code-block:: text + + $ $VENV/bin/pshell starter/development.ini#main + Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32) + [GCC 4.4.3] on linux2 + Type "help" for more information. + + Environment: + app The WSGI application. + registry Active Pyramid registry. + request Active request object. + root Root of the default resource tree. + root_factory Default root factory used to create `root`. + + >>> root + <myproject.resources.MyResource object at 0x445270> + >>> registry + <Registry myproject> + >>> registry.settings['pyramid.debug_notfound'] + False + >>> from myproject.views import my_view + >>> from pyramid.request import Request + >>> r = Request.blank('/') + >>> my_view(r) + {'project': 'myproject'} + +The WSGI application that is loaded will be available in the shell as the +``app`` global. Also, if the application that is loaded is the :app:`Pyramid` +app with no surrounding :term:`middleware`, the ``root`` object returned by the +default :term:`root factory`, ``registry``, and ``request`` will be available. + +You can also simply rely on the ``main`` default section name by omitting any +hash after the filename: + +.. code-block:: text + + $ $VENV/bin/pshell starter/development.ini + +Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows). + + +.. index:: + pair: pshell; extending + +.. _extending_pshell: + +Extending the Shell +~~~~~~~~~~~~~~~~~~~ + +It is convenient when using the interactive shell often to have some variables +significant to your application already loaded as globals when you start the +``pshell``. To facilitate this, ``pshell`` will look for a special ``[pshell]`` +section in your INI file and expose the subsequent key/value pairs to the +shell. Each key is a variable name that will be global within the pshell +session; each value is a :term:`dotted Python name`. If specified, the special +key ``setup`` should be a :term:`dotted Python name` pointing to a callable +that accepts the dictionary of globals that will be loaded into the shell. This +allows for some custom initializing code to be executed each time the +``pshell`` is run. The ``setup`` callable can also be specified from the +commandline using the ``--setup`` option which will override the key in the INI +file. + +For example, you want to expose your model to the shell along with the database +session so that you can mutate the model on an actual database. Here, we'll +assume your model is stored in the ``myapp.models`` package. + +.. code-block:: ini + :linenos: + + [pshell] + setup = myapp.lib.pshell.setup + m = myapp.models + session = myapp.models.DBSession + t = transaction + +By defining the ``setup`` callable, we will create the module +``myapp.lib.pshell`` containing a callable named ``setup`` that will receive +the global environment before it is exposed to the shell. Here we mutate the +environment's request as well as add a new value containing a WebTest version +of the application to which we can easily submit requests. + +.. code-block:: python + :linenos: + + # myapp/lib/pshell.py + from webtest import TestApp + + def setup(env): + env['request'].host = 'www.example.com' + env['request'].scheme = 'https' + env['testapp'] = TestApp(env['app']) + +When this INI file is loaded, the extra variables ``m``, ``session`` and ``t`` +will be available for use immediately. Since a ``setup`` callable was also +specified, it is executed and a new variable ``testapp`` is exposed, and the +request is configured to generate urls from the host +``http://www.example.com``. For example: + +.. code-block:: text + + $ $VENV/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. + + Environment: + app The WSGI application. + registry Active Pyramid registry. + request Active request object. + root Root of the default resource tree. + root_factory Default root factory used to create `root`. + testapp <webtest.TestApp object at ...> + + Custom Variables: + m myapp.models + session myapp.models.DBSession + t transaction + + >>> testapp.get('/') + <200 OK text/html body='<!DOCTYPE...l>\n'/3337> + >>> request.route_url('home') + 'https://www.example.com/' + + +.. _ipython_or_bpython: + +Alternative Shells +~~~~~~~~~~~~~~~~~~ + +The ``pshell`` command can be easily extended with alternate REPLs if the +default python REPL is not satisfactory. Assuming you have a binding +installed such as ``pyramid_ipython`` it will normally be auto-selected and +used. You may also specifically invoke your choice with the ``-p choice`` or +``--python-shell choice`` option. + +.. code-block:: text + + $ $VENV/bin/pshell -p ipython development.ini#MyProject + +You may use the ``--list-shells`` option to see the available shells. + +.. code-block:: text + + $ $VENV/bin/pshell --list-shells + Available shells: + bpython + ipython + python + +If you want to use a shell that isn't supported out of the box, you can +introduce a new shell by registering an entry point in your ``setup.py``: + +.. code-block:: python + + setup( + entry_points={ + 'pyramid.pshell_runner': [ + 'myshell=my_app:ptpython_shell_factory', + ], + }, + ) + +And then your shell factory should return a function that accepts two +arguments, ``env`` and ``help``, which would look like this: + +.. code-block:: python + + from ptpython.repl import embed + + def ptpython_shell_runner(env, help): + print(help) + return embed(locals=env) + +.. versionchanged:: 1.6 + User-defined shells may be registered using entry points. Prior to this + the only supported shells were ``ipython``, ``bpython`` and ``python``. + + ``ipython`` and ``bpython`` have been moved into their respective + packages ``pyramid_ipython`` and ``pyramid_bpython``. + + +Setting a Default Shell +~~~~~~~~~~~~~~~~~~~~~~~ + +You may use the ``default_shell`` option in your ``[pshell]`` ini section to +specify a list of preferred shells. + +.. code-block:: ini + :linenos: + + [pshell] + default_shell = ptpython ipython bpython + +.. versionadded:: 1.6 + + +.. index:: + pair: routes; printing + single: proutes + +.. _displaying_application_routes: + +Displaying All Application Routes +--------------------------------- + +.. seealso:: See also the output of :ref:`proutes --help <proutes_script>`. + +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. + +For example: + +.. code-block:: text + :linenos: + + $ $VENV/bin/proutes development.ini + Name Pattern View Method + ---- ------- ---- ------ + debugtoolbar /_debug_toolbar/*subpath <wsgiapp> * + __static/ /static/*subpath dummy_starter:static/ * + __static2/ /static2/*subpath /var/www/static/ * + __pdt_images/ /pdt_images/*subpath pyramid_debugtoolbar:static/img/ * + a / <unknown> * + no_view_attached / <unknown> * + route_and_view_attached / app1.standard_views.route_and_view_attached * + method_conflicts /conflicts app1.standard_conflicts <route mismatch> + multiview /multiview app1.standard_views.multiview GET,PATCH + not_post /not_post app1.standard_views.multview !POST,* + +``proutes`` generates a table with four columns: *Name*, *Pattern*, *View*, and +*Method*. The items listed in the Name column are route names, the items +listed in the Pattern column are route patterns, 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, and the items listed in the +Method column are the request methods that are associated with the route name. +The View column may show ``<unknown>`` if no associated view callable could be +found. The Method column, for the route name, may show either ``<route +mismatch>`` if the view callable does not accept any of the route's request +methods, or ``*`` if the view callable will accept any of the route's request +methods. If no routes are configured within your application, nothing will be +printed to the console when ``proutes`` is executed. + +It is convenient when using the ``proutes`` command often to configure which +columns and the order you would like to view them. To facilitate this, +``proutes`` will look for a special ``[proutes]`` section in your ``.ini`` file +and use those as defaults. + +For example you may remove the request method and place the view first: + +.. code-block:: text + :linenos: + + [proutes] + format = view + name + pattern + +You can also separate the formats with commas or spaces: + +.. code-block:: text + :linenos: + + [proutes] + format = view name pattern + + [proutes] + format = view, name, pattern + +If you want to temporarily configure the columns and order, there is the +argument ``--format``, which is a comma separated list of columns you want to +include. The current available formats are ``name``, ``pattern``, ``view``, and +``method``. + + +.. index:: + pair: tweens; printing + single: ptweens + +.. _displaying_tweens: + +Displaying "Tweens" +------------------- + +.. seealso:: See also the output of :ref:`ptweens --help <ptweens_script>`. + +A :term:`tween` is a bit of code that sits between the main Pyramid 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) using the +``ptweens`` command. Tween factories will show up represented by their +standard Python dotted name in the ``ptweens`` output. + +For example, here's the ``ptweens`` command run against a system configured +without any explicit tweens: + +.. code-block:: text + :linenos: + + $ $VENV/bin/ptweens development.ini + "pyramid.tweens" config value NOT set (implicitly ordered tweens used) + + Implicit Tween Chain + + Position Name Alias + -------- ---- ----- + - - INGRESS + 0 pyramid_debugtoolbar.toolbar.toolbar_tween_factory pdbt + 1 pyramid.tweens.excview_tween_factory excview + - - MAIN + +Here's the ``ptweens`` command run against a system configured *with* explicit +tweens defined in its ``development.ini`` file: + +.. code-block:: text + :linenos: + + $ ptweens development.ini + "pyramid.tweens" config value set (explicitly ordered tweens used) + + Explicit Tween Chain (used) + + Position Name + -------- ---- + - INGRESS + 0 starter.tween_factory2 + 1 starter.tween_factory1 + 2 pyramid.tweens.excview_tween_factory + - MAIN + + Implicit Tween Chain (not used) + + Position Name + -------- ---- + - INGRESS + 0 pyramid_debugtoolbar.toolbar.toolbar_tween_factory + 1 pyramid.tweens.excview_tween_factory + - MAIN + +Here's the application configuration section of the ``development.ini`` used by +the above ``ptweens`` command which reports that the explicit tween chain is +used: + +.. code-block:: ini + :linenos: + + [app:main] + use = egg:starter + reload_templates = true + debug_authorization = false + debug_notfound = false + debug_routematch = false + debug_templates = true + default_locale_name = en + pyramid.include = pyramid_debugtoolbar + pyramid.tweens = starter.tween_factory2 + starter.tween_factory1 + pyramid.tweens.excview_tween_factory + +See :ref:`registering_tweens` for more information about tweens. + + +.. index:: + single: invoking a request + single: prequest + +.. _invoking_a_request: + +Invoking a Request +------------------ + +.. seealso:: See also the output of :ref:`prequest --help <prequest_script>`. + +You can use the ``prequest`` command-line utility to send a request to your +application and see the response body without starting a server. + +There are two required arguments to ``prequest``: + +- The config file/section: follows the format ``config_file#section_name``, + where ``config_file`` is the path to your application's ``.ini`` file and + ``section_name`` is the ``app`` section name inside the ``.ini`` file. The + ``section_name`` is optional; it defaults to ``main``. For example: + ``development.ini``. + +- The path: this should be the non-URL-quoted path element of the URL to the + resource you'd like to be rendered on the server. For example, ``/``. + +For example:: + + $ $VENV/bin/prequest development.ini / + +This will print the body of the response to the console on which it was +invoked. + +Several options are supported by ``prequest``. These should precede any config +file name or URL. + +``prequest`` has a ``-d`` (i.e., ``--display-headers``) option which prints the +status and headers returned by the server before the output:: + + $ $VENV/bin/prequest -d development.ini / + +This will print the status, headers, and the body of the response to the +console. + +You can add request header values by using the ``--header`` option:: + + $ $VENV/bin/prequest --header=Host:example.com development.ini / + +Headers are added to the WSGI environment by converting them to their CGI/WSGI +equivalents (e.g., ``Host=example.com`` will insert the ``HTTP_HOST`` header +variable as the value ``example.com``). Multiple ``--header`` options can be +supplied. The special header value ``content-type`` sets the ``CONTENT_TYPE`` +in the WSGI environment. + +By default, ``prequest`` sends a ``GET`` request. You can change this by using +the ``-m`` (aka ``--method``) option. ``GET``, ``HEAD``, ``POST``, and +``DELETE`` are currently supported. When you use ``POST``, the standard input +of the ``prequest`` process is used as the ``POST`` body:: + + $ $VENV/bin/prequest -mPOST development.ini / < somefile + + +Using Custom Arguments to Python when Running ``p*`` Scripts +------------------------------------------------------------ + +.. versionadded:: 1.5 + +Each of Pyramid's console scripts (``pserve``, ``pviews``, etc.) can be run +directly using ``python3 -m``, allowing custom arguments to be sent to the +Python interpreter at runtime. For example:: + + python3 -m pyramid.scripts.pserve development.ini + + +.. index:: + single: pdistreport + single: distributions, showing installed + single: showing installed distributions + +.. _showing_distributions: + +Showing All Installed Distributions and Their Versions +------------------------------------------------------ + +.. versionadded:: 1.5 + +.. seealso:: See also the output of :ref:`pdistreport --help + <pdistreport_script>`. + +You can use the ``pdistreport`` command to show the :app:`Pyramid` version in +use, the Python version in use, and all installed versions of Python +distributions in your Python environment:: + + $ $VENV/bin/pdistreport + Pyramid version: 1.5dev + Platform Linux-3.2.0-51-generic-x86_64-with-debian-wheezy-sid + Packages: + authapp 0.0 + /home/chrism/projects/foo/src/authapp + beautifulsoup4 4.1.3 + /home/chrism/projects/foo/lib/python2.7/site-packages/beautifulsoup4-4.1.3-py2.7.egg + ... more output ... + +``pdistreport`` takes no options. Its output is useful to paste into a +pastebin when you are having problems and need someone with more familiarity +with Python packaging and distribution than you have to look at your +environment. + + +.. _writing_a_script: + +Writing a Script +---------------- + +All web applications are, at their hearts, systems which accept a request and +return a response. When a request is accepted by a :app:`Pyramid` application, +the system receives state from the request which is later relied on by your +application code. For example, one :term:`view callable` may assume it's +working against a request that has a ``request.matchdict`` of a particular +composition, while another assumes a different composition of the matchdict. + +In the meantime, it's convenient to be able to write a Python script that can +work "in a Pyramid environment", for instance to update database tables used by +your :app:`Pyramid` application. But a "real" Pyramid environment doesn't have +a completely static state independent of a request; your application (and +Pyramid itself) is almost always reliant on being able to obtain information +from a request. When you run a Python script that simply imports code from +your application and tries to run it, there just is no request data, because +there isn't any real web request. Therefore some parts of your application and +some Pyramid APIs will not work. + +For this reason, :app:`Pyramid` makes it possible to run a script in an +environment much like the environment produced when a particular +:term:`request` reaches your :app:`Pyramid` application. This is achieved by +using the :func:`pyramid.paster.bootstrap` command in the body of your script. + +.. versionadded:: 1.1 + :func:`pyramid.paster.bootstrap` + +In the simplest case, :func:`pyramid.paster.bootstrap` can be used with a +single argument, which accepts the :term:`PasteDeploy` ``.ini`` file +representing your Pyramid application's configuration as a single argument: + +.. code-block:: python + + from pyramid.paster import bootstrap + env = bootstrap('/path/to/my/development.ini') + print(env['request'].route_url('home')) + +:func:`pyramid.paster.bootstrap` returns a dictionary containing +framework-related information. This dictionary will always contain a +:term:`request` object as its ``request`` key. + +The following keys are available in the ``env`` dictionary returned by +:func:`pyramid.paster.bootstrap`: + +request + + A :class:`pyramid.request.Request` object implying the current request + state for your script. + +app + + The :term:`WSGI` application object generated by bootstrapping. + +root + + The :term:`resource` root of your :app:`Pyramid` application. This is an + object generated by the :term:`root factory` configured in your + application. + +registry + + The :term:`application registry` of your :app:`Pyramid` application. + +closer + + A parameterless callable that can be used to pop an internal :app:`Pyramid` + threadlocal stack (used by :func:`pyramid.threadlocal.get_current_registry` + and :func:`pyramid.threadlocal.get_current_request`) when your scripting + job is finished. + +Let's assume that the ``/path/to/my/development.ini`` file used in the example +above looks like so: + +.. code-block:: ini + + [pipeline:main] + pipeline = translogger + another + + [filter:translogger] + filter_app_factory = egg:Paste#translogger + setup_console_handler = False + logger_name = wsgi + + [app:another] + use = egg:MyProject + +The configuration loaded by the above bootstrap example will use the +configuration implied by the ``[pipeline:main]`` section of your configuration +file by default. Specifying ``/path/to/my/development.ini`` is logically +equivalent to specifying ``/path/to/my/development.ini#main``. In this case, +we'll be using a configuration that includes an ``app`` object which is wrapped +in the Paste "translogger" :term:`middleware` (which logs requests to the +console). + +You can also specify a particular *section* of the PasteDeploy ``.ini`` file to +load instead of ``main``: + +.. code-block:: python + + from pyramid.paster import bootstrap + env = bootstrap('/path/to/my/development.ini#another') + print(env['request'].route_url('home')) + +The above example specifies the ``another`` ``app``, ``pipeline``, or +``composite`` section of your PasteDeploy configuration file. The ``app`` +object present in the ``env`` dictionary returned by +:func:`pyramid.paster.bootstrap` will be a :app:`Pyramid` :term:`router`. + + +Changing the Request +~~~~~~~~~~~~~~~~~~~~ + +By default, Pyramid will generate a request object in the ``env`` dictionary +for the URL ``http://localhost:80/``. This means that any URLs generated by +Pyramid during the execution of your script will be anchored here. This is +generally not what you want. + +So how do we make Pyramid generate the correct URLs? + +Assuming that you have a route configured in your application like so: + +.. code-block:: python + + config.add_route('verify', '/verify/{code}') + +You need to inform the Pyramid environment that the WSGI application is +handling requests from a certain base. For example, we want to simulate +mounting our application at `https://example.com/prefix`, to ensure that the +generated URLs are correct for our deployment. This can be done by either +mutating the resulting request object, or more simply by constructing the +desired request and passing it into :func:`~pyramid.paster.bootstrap`: + +.. code-block:: python + + from pyramid.paster import bootstrap + from pyramid.request import Request + + request = Request.blank('/', base_url='https://example.com/prefix') + env = bootstrap('/path/to/my/development.ini#another', request=request) + print(env['request'].application_url) + # will print 'https://example.com/prefix' + +Now you can readily use Pyramid's APIs for generating URLs: + +.. code-block:: python + + env['request'].route_url('verify', code='1337') + # will return 'https://example.com/prefix/verify/1337' + + +Cleanup +~~~~~~~ + +When your scripting logic finishes, it's good manners to call the ``closer`` +callback: + +.. code-block:: python + + from pyramid.paster import bootstrap + env = bootstrap('/path/to/my/development.ini') + + # .. do stuff ... + + env['closer']() + + +Setting Up Logging +~~~~~~~~~~~~~~~~~~ + +By default, :func:`pyramid.paster.bootstrap` does not configure logging +parameters present in the configuration file. If you'd like to configure +logging based on ``[logger]`` and related sections in the configuration file, +use the following command: + +.. code-block:: python + + import pyramid.paster + pyramid.paster.setup_logging('/path/to/my/development.ini') + +See :ref:`logging_chapter` for more information on logging within +:app:`Pyramid`. + + +.. index:: + single: console script + +.. _making_a_console_script: + +Making Your Script into a Console Script +---------------------------------------- + +A "console script" is :term:`setuptools` terminology for a script that gets +installed into the ``bin`` directory of a Python :term:`virtual environment` +(or "base" Python environment) when a :term:`distribution` which houses that +script is installed. Because it's installed into the ``bin`` directory of a +virtual environment when the distribution is installed, it's a convenient way +to package and distribute functionality that you can call from the +command-line. It's often more convenient to create a console script than it is +to create a ``.py`` script and instruct people to call it with the "right" +Python interpreter. A console script generates a file that lives in ``bin``, +and when it's invoked it will always use the "right" Python environment, which +means it will always be invoked in an environment where all the libraries it +needs (such as Pyramid) are available. + +In general, you can make your script into a console script by doing the +following: + +- Use an existing distribution (such as one you've already created via + ``pcreate``) or create a new distribution that possesses at least one package + or module. It should, within any module within the distribution, house a + callable (usually a function) that takes no arguments and which runs any of + the code you wish to run. + +- Add a ``[console_scripts]`` section to the ``entry_points`` argument of the + distribution which creates a mapping between a script name and a dotted name + representing the callable you added to your distribution. + +- Run ``pip install -e .`` or ``pip install .`` to get your distribution + reinstalled. When you reinstall your distribution, a file representing the + script that you named in the last step will be in the ``bin`` directory of + the virtual environment in which you installed the distribution. It will be + executable. Invoking it from a terminal will execute your callable. + +As an example, let's create some code that can be invoked by a console script +that prints the deployment settings of a Pyramid application. To do so, we'll +pretend you have a distribution with a package in it named ``myproject``. +Within this package, we'll pretend you've added a ``scripts.py`` module which +contains the following code: + +.. code-block:: python + :linenos: + + # myproject.scripts module + + import optparse + import sys + import textwrap + + from pyramid.paster import bootstrap + + def settings_show(): + description = """\ + Print the deployment settings for a Pyramid application. Example: + 'show_settings deployment.ini' + """ + usage = "usage: %prog config_uri" + parser = optparse.OptionParser( + usage=usage, + description=textwrap.dedent(description) + ) + parser.add_option( + '-o', '--omit', + dest='omit', + metavar='PREFIX', + type='string', + action='append', + help=("Omit settings which start with PREFIX (you can use this " + "option multiple times)") + ) + + options, args = parser.parse_args(sys.argv[1:]) + if not len(args) >= 1: + print('You must provide at least one argument') + return 2 + config_uri = args[0] + omit = options.omit + if omit is None: + omit = [] + env = bootstrap(config_uri) + settings, closer = env['registry'].settings, env['closer'] + try: + for k, v in settings.items(): + if any([k.startswith(x) for x in omit]): + continue + print('%-40s %-20s' % (k, v)) + finally: + closer() + +This script uses the Python ``optparse`` module to allow us to make sense out +of extra arguments passed to the script. It uses the +:func:`pyramid.paster.bootstrap` function to get information about the +application defined by a config file, and prints the deployment settings +defined in that config file. + +After adding this script to the package, you'll need to tell your +distribution's ``setup.py`` about its existence. Within your distribution's +top-level directory, your ``setup.py`` file will look something like this: + +.. code-block:: python + :linenos: + + import os + + from setuptools import setup, find_packages + + here = os.path.abspath(os.path.dirname(__file__)) + with open(os.path.join(here, 'README.txt')) as f: + README = f.read() + with open(os.path.join(here, 'CHANGES.txt')) as f: + CHANGES = f.read() + + requires = ['pyramid', 'pyramid_debugtoolbar'] + + tests_require = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest', # includes virtualenv + 'pytest-cov', + ] + + setup(name='MyProject', + version='0.0', + description='My project', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + "Programming Language :: Python", + "Framework :: Pyramid", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], + author='', + author_email='', + url='', + keywords='web pyramid pylons', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=requires, + extras_require={ + 'testing': tests_require, + }, + entry_points = """\ + [paste.app_factory] + main = myproject:main + """, + ) + +We're going to change the ``setup.py`` file to add a ``[console_scripts]`` +section within the ``entry_points`` string. Within this section, you should +specify a ``scriptname = dotted.path.to:yourfunction`` line. For example: + +.. code-block:: ini + + [console_scripts] + show_settings = myproject.scripts:settings_show + +The ``show_settings`` name will be the name of the script that is installed +into ``bin``. The colon (``:``) between ``myproject.scripts`` and +``settings_show`` above indicates that ``myproject.scripts`` is a Python +module, and ``settings_show`` is the function in that module which contains the +code you'd like to run as the result of someone invoking the ``show_settings`` +script from their command line. + +The result will be something like: + +.. code-block:: python + :linenos: + :emphasize-lines: 43-44 + + import os + + from setuptools import setup, find_packages + + here = os.path.abspath(os.path.dirname(__file__)) + with open(os.path.join(here, 'README.txt')) as f: + README = f.read() + with open(os.path.join(here, 'CHANGES.txt')) as f: + CHANGES = f.read() + + requires = ['pyramid', 'pyramid_debugtoolbar'] + + tests_require = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest', # includes virtualenv + 'pytest-cov', + ] + + setup(name='MyProject', + version='0.0', + description='My project', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + "Programming Language :: Python", + "Framework :: Pyramid", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], + author='', + author_email='', + url='', + keywords='web pyramid pylons', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=requires, + extras_require={ + 'testing': tests_require, + }, + entry_points = """\ + [paste.app_factory] + main = myproject:main + [console_scripts] + show_settings = myproject.scripts:settings_show + """, + ) + +Once you've done this, invoking ``$VENV/bin/pip install -e .`` will install a +file named ``show_settings`` into the ``$somevenv/bin`` directory with a +small bit of Python code that points to your entry point. It will be +executable. Running it without any arguments will print an error and exit. +Running it with a single argument that is the path of a config file will print +the settings. Running it with an ``--omit=foo`` argument will omit the settings +that have keys that start with ``foo``. Running it with two "omit" options +(e.g., ``--omit=foo --omit=bar``) will omit all settings that have keys that +start with either ``foo`` or ``bar``: + +.. code-block:: bash + + $ $VENV/bin/show_settings development.ini --omit=pyramid --omit=debugtoolbar + debug_routematch False + debug_templates True + reload_templates True + mako.directories [] + debug_notfound False + default_locale_name en + reload_resources False + debug_authorization False + reload_assets False + prevent_http_cache False + +Pyramid's ``pserve``, ``pcreate``, ``pshell``, ``prequest``, ``ptweens``, and +other ``p*`` scripts are implemented as console scripts. When you invoke one +of those, you are using a console script. |
