diff options
| author | Chris McDonough <chrism@plope.com> | 2011-10-06 05:40:58 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-10-06 05:40:58 -0400 |
| commit | 87dbfa4cd26171414d971d4f56f2aceaceb9a6eb (patch) | |
| tree | 38876d7fe78e1b32763fa185f0dc9546e113671a | |
| parent | 206a7f6eb776ff91043ce9cf4627dad20f68b937 (diff) | |
| parent | 75f05a60f8754c9c5c234b9731dbc08646ba89a9 (diff) | |
| download | pyramid-87dbfa4cd26171414d971d4f56f2aceaceb9a6eb.tar.gz pyramid-87dbfa4cd26171414d971d4f56f2aceaceb9a6eb.tar.bz2 pyramid-87dbfa4cd26171414d971d4f56f2aceaceb9a6eb.zip | |
Merge branch 'feature.glue'
114 files changed, 3822 insertions, 1992 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 06eace14a..9859fbf14 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -25,6 +25,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 <commandname>`` + 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 ------------ @@ -36,3 +50,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. + + 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 c5452401c..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 <http://webob.org>`_ is a WSGI request/response library created by Ian Bicking. - Paste - `Paste <http://pythonpaste.org>`_ is a WSGI development and - deployment system developed by Ian Bicking. - PasteDeploy `PasteDeploy <http://pythonpaste.org>`_ 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 <http://chameleon.repoze.org>`_ is an attribute @@ -930,8 +926,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/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/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 <http://en.wikipedia.org/wiki/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 / <function my_view> @@ -319,19 +316,18 @@ For example: static/ static/*subpath <static_view object> catchall /*subpath <function static_view> -``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/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/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/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/introduction.rst b/docs/narr/introduction.rst index 8f0533997..547f88ef3 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 <http://docs.python.org/lib/logging-config-fileformat.html>`_. 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 <http://docs.python.org/lib/logging-config-api.html>`_ 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..cf8f96c8a 100644 --- a/docs/narr/paste.rst +++ b/docs/narr/paste.rst @@ -1,27 +1,26 @@ .. _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 -only contextualizes the use of Paste within Pyramid. For detailed +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 PasteDeploy within Pyramid. For detailed documentation, see http://pythonpaste.org. PasteDeploy @@ -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. @@ -77,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 ``paster serve`` 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 345672204..4f96448af 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: <package name>``. 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 <http://docs.python.org/howto/logging.html#configuring-logging>`_ 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`. @@ -718,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. @@ -762,7 +728,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. @@ -785,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 @@ -922,10 +888,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 +946,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/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 <http://pythonpaste.org/modules/urlmap.html>`_ -"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 <http://pythonpaste.org/modules/urlmap.html>`_ "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/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..c2baa5bd8 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 @@ -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 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/compat.py b/pyramid/compat.py index e686be27d..3ac235b0f 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 @@ -224,3 +223,15 @@ if PY3: # pragma: no cover from html import escape else: from cgi import escape + +try: # pragma: no cover + input_ = raw_input +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/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/paster.py b/pyramid/paster.py index c9c356a92..3fec6c556 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -1,30 +1,9 @@ import os -import sys -from code import interact import zope.deprecation +from paste.deploy import loadapp -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 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 @@ -100,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:: - - $ paster 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:: - - $ paster 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, '<unknown>')) - 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:: - - $ paster 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:: - - $ paster 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/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/__init__.py b/pyramid/scaffolds/__init__.py index 673f22e21..a42e18ab7 100644 --- a/pyramid/scaffolds/__init__.py +++ b/pyramid/scaffolds/__init__.py @@ -1,22 +1,13 @@ +import binascii import os -from pyramid.compat import print_ +from pyramid.compat import 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 pyramid.scaffolds.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' @@ -24,30 +15,26 @@ 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' 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/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..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:Paste#http +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 fad5baa30..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:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 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/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='<string>', + 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/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..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:Paste#http +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 fad5baa30..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:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 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/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl index 2d2f6e354..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:Paste#http +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 b04f37ab3..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:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 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/template.py b/pyramid/scaffolds/template.py new file mode 100644 index 000000000..4ffa946d3 --- /dev/null +++ b/pyramid/scaffolds/template.py @@ -0,0 +1,136 @@ +# (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): + copydir = copydir # for testing + _template_dir = None + + 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): # pragma: no cover + """ + Called before template is applied. + """ + pass + + def post(self, command, output_dir, vars): # pragma: no cover + """ + Called after template is applied. + """ + pass + + def write_files(self, command, output_dir, vars): + template_dir = self.template_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: + 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): + + 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<braced>.*?)}}') + +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): # pragma: no cover + 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..c0b60b61b 100644 --- a/pyramid/scaffolds/tests.py +++ b/pyramid/scaffolds/tests.py @@ -34,16 +34,15 @@ 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']) 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() @@ -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/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl index 29da8ee38..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:Paste#http +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 0275a33a7..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:Paste#http +use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 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'], ) 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/common.py b/pyramid/scripts/common.py new file mode 100644 index 000000000..dfff26449 --- /dev/null +++ b/pyramid/scripts/common.py @@ -0,0 +1,21 @@ +import os +from pyramid.compat import configparser +from logging.config import 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. + + 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) + return 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 new file mode 100644 index 000000000..dfd9f691b --- /dev/null +++ b/pyramid/scripts/pcreate.py @@ -0,0 +1,111 @@ +# (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 +import re +import sys + +_bad_chars_re = re.compile('[^a-zA-Z0-9_]') + +def main(argv=sys.argv, quiet=False): + command = PCreateCommand(argv, quiet) + return command.run() + +class PCreateCommand(object): + verbose = True + interactive = False + simulate = False + usage = "usage: %prog [options] distribution_name" + 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, quiet=False): + self.quiet = quiet + self.options, self.args = self.parser.parse_args(argv[1:]) + self.scaffolds = self.all_scaffolds() + + def run(self): + if self.options.list: + return self.show_scaffolds() + if not self.options.scaffold_name: + self.out('You must provide at least one scaffold name') + return + if not self.args: + self.out('You must provide a project name') + return + available = [x.name for x in self.scaffolds] + diff = set(self.options.scaffold_name).difference(available) + if diff: + self.out('Unavailable scaffolds: %s' % list(diff)) + return + return self.render_scaffolds() + + def render_scaffolds(self): + options = self.options + args = self.args + 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': project_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) + return True + + def show_scaffolds(self): + 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.quiet: + print(msg) + + diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py new file mode 100644 index 000000000..fb1cf2059 --- /dev/null +++ b/pyramid/scripts/proutes.py @@ -0,0 +1,80 @@ +import optparse +import sys + +from pyramid.compat import print_ +from pyramid.paster import bootstrap + +def main(argv=sys.argv, quiet=False): + command = PRoutesCommand(argv, quiet) + 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, 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 + config = Configurator(registry = registry) + return config.get_routes_mapper() + + def out(self, msg): # pragma: no cover + if not self.quiet: + print_(msg) + + 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 + 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, '<unknown>')) + 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/pserve.py b/pyramid/scripts/pserve.py new file mode 100644 index 000000000..de5276a9f --- /dev/null +++ b/pyramid/scripts/pserve.py @@ -0,0 +1,867 @@ +# (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 +# +# 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 atexit +import errno +import logging +import optparse +import os +import re +import subprocess +import sys +import threading +import time +import traceback + +from paste.deploy import loadapp, loadserver + +from pyramid.scripts.common import logging_file_config + +MAXFD = 1024 + +def main(argv=sys.argv): + command = PServeCommand(argv) + return command.run() + +class DaemonizeException(Exception): + pass + +class PServeCommand(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") + 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 + 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)')) + + _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 out(self, msg): + if not self.options.quiet: + print(msg) + + 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: + 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): + 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[:] + + if self.options.reload: + 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'): + self.out( + 'Error: must give start|stop|restart (not %s)' % cmd) + return + + 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': + self.out("Could not stop daemon; aborting") + else: + self.out("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: + self.out(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) + 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.' + self.out(msg) + + def serve(): + try: + server(app) + except (SystemExit, KeyboardInterrupt) as e: + if self.verbose > 1: + raise + if str(e): + msg = ' '+str(e) + else: + msg = '' + self.out('Exiting%s (-v to see traceback)' % msg) + + 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 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): # pragma: no cover (nfw) + 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: + self.out('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 _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): # pragma: no cover (nfw) + pid = os.getpid() + if self.verbose > 1: + self.out('Writing PID %s to %s' % (pid, pid_file)) + f = open(pid_file, 'w') + f.write(str(pid)) + f.close() + atexit.register(self._remove_pid_file, pid, pid_file, self.verbose) + + 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) + return 1 + pid = read_pidfile(pid_file) + if not pid: + self.out("Not a valid PID file in %s" % pid_file) + return 1 + pid = live_pidfile(pid_file) + if not pid: + self.out("PID in %s is not valid (deleting)" % pid_file) + try: + os.unlink(pid_file) + except (OSError, IOError) as e: + self.out("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: + self.out("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): + self.out('No PID file %s' % pid_file) + return 1 + pid = read_pidfile(pid_file) + if not pid: + self.out('No PID in file %s' % pid_file) + return 1 + pid = live_pidfile(pid_file) + if not pid: + self.out('PID %s in %s is not running' % (pid, pid_file)) + return 1 + self.out('Server running in PID %s' % pid) + return 0 + + def restart_with_reloader(self): # pragma: no cover (nfw) + self.restart_with_monitor(reloader=True) + + def restart_with_monitor(self, reloader=False): # pragma: no cover (nfw) + if self.verbose > 0: + if reloader: + self.out('Starting subprocess with file monitor') + else: + self.out('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: + self.out('^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: + self.out('%s %s %s' % ('-'*20, 'Restarting', '-'*20)) + + def change_user_group(self, user, group): # pragma: no cover (nfw) + 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: + self.out('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): # 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. + 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 ensure_port_cleanup( + bound_addresses, maxtries=30, sleeptime=2): # pragma: no cover (nfw) + """ + This makes sure any open ports are closed. + + Does this by connecting to them until they give connection + refused. Servers should call like:: + + ensure_port_cleanup([80, 443]) + """ + atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries, + sleeptime=sleeptime) + +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 + 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(): # pragma: no cover (nfw) + """ + 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) + +def install_reloader(poll_interval=1): # pragma: no cover (nfw) + """ + 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 '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) + + def __repr__(self): + if self.obj is None: + return ('<bound class method %s.%s>' + % (self.type.__name__, self.func.func_name)) + else: + return ('<bound method %s.%s of %r>' + % (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): # pragma: no cover (nfw) + 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 filename in self.module_mtimes: + 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): # pragma: no cover + 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/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py new file mode 100644 index 000000000..40673906b --- /dev/null +++ b/pyramid/scripts/pshell.py @@ -0,0 +1,194 @@ +from code import interact +import optparse +import sys + +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, quiet=False): + command = PShellCommand(argv, quiet) + 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, quiet=False): + self.quiet = quiet + 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 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) + 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.items(): + 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 + diff --git a/pyramid/scripts/ptweens.py b/pyramid/scripts/ptweens.py new file mode 100644 index 000000000..556b43254 --- /dev/null +++ b/pyramid/scripts/ptweens.py @@ -0,0 +1,92 @@ +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, quiet=False): + command = PTweensCommand(argv, quiet) + 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, quiet=False): + self.quiet = quiet + 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 + if not self.quiet: + 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): + 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'] + 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..8d3048633 --- /dev/null +++ b/pyramid/scripts/pviews.py @@ -0,0 +1,261 @@ +import optparse +import sys + +from pyramid.compat import print_ +from pyramid.interfaces import IMultiView +from pyramid.paster import bootstrap + +def main(argv=sys.argv, quiet=False): + command = PViewsCommand(argv, quiet) + 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, quiet=False): + self.quiet = quiet + self.options, self.args = self.parser.parse_args(argv[1:]) + + def out(self, msg): # pragma: no cover + if not self.quiet: + 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): + 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 + 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/testing.py b/pyramid/testing.py index b24a6cac7..e1011f5b4 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 @@ -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): diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py index a697b5691..d23c156c2 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', '<unknown>']) - - 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', '<function', 'view']) - - def test_single_route_one_view_registered_with_factory(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 IMyRoot(Interface): - pass - class IMyRoute(Interface): - pass - registry.registerAdapter(view, - (IViewClassifier, IMyRoute, IMyRoot), - IView, '') - registry.registerUtility(IMyRoute, IRouteRequest, name='a') - command = self._makeOne() - def factory(request): pass - route = DummyRoute('a', '/a', factory=factory) - 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', '<unknown>']) - - 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 @@ -902,108 +72,16 @@ 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) - -@skip_on('py3') -class TestPTweensCommand(unittest.TestCase): - def _getTargetClass(self): - from pyramid.paster import PTweensCommand - return PTweensCommand + self.assertTrue('closer' in result) - 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=''): - 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 @@ -1018,28 +96,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 +103,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_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 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_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') + diff --git a/pyramid/tests/test_scaffolds/test_template.py b/pyramid/tests/test_scaffolds/test_template.py new file mode 100644 index 000000000..88a31e802 --- /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, 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, 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, b'a') + + def test_template_renderer_expr_value_is_None(self): + inst = self._makeOne() + result = inst.template_renderer('{{a}}', {'a':None}) + self.assertEqual(result, b'') + + 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 + + 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_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 + + diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py new file mode 100644 index 000000000..cdd0daf2e --- /dev/null +++ b/pyramid/tests/test_scripts/test_pcreate.py @@ -0,0 +1,114 @@ +import unittest + +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.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, quiet=True) + + def test_it(self): + result = self._callFUT(['pcreate']) + 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 + diff --git a/pyramid/tests/test_scripts/test_proutes.py b/pyramid/tests/test_scripts/test_proutes.py new file mode 100644 index 000000000..af6ff19d0 --- /dev/null +++ b/pyramid/tests/test_scripts/test_proutes.py @@ -0,0 +1,138 @@ +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', '<unknown>']) + + 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', '<function', 'view']) + + def test_single_route_one_view_registered_with_factory(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 IMyRoot(Interface): + pass + class IMyRoute(Interface): + pass + registry.registerAdapter(view, + (IViewClassifier, IMyRoute, IMyRoot), + IView, '') + registry.registerUtility(IMyRoute, IRouteRequest, name='a') + command = self._makeOne() + def factory(request): pass + route = dummy.DummyRoute('a', '/a', factory=factory) + 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', '<unknown>']) + + 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 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_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') + diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py new file mode 100644 index 000000000..e38da2077 --- /dev/null +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -0,0 +1,259 @@ +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) + + +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 new file mode 100644 index 000000000..4dddaa0aa --- /dev/null +++ b/pyramid/tests/test_scripts/test_ptweens.py @@ -0,0 +1,61 @@ +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) + +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 new file mode 100644 index 000000000..e2c3892fa --- /dev/null +++ b/pyramid/tests/test_scripts/test_pviews.py @@ -0,0 +1,475 @@ +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)') + +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) 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') @@ -39,15 +39,9 @@ install_requires=[ 'zope.deprecation >= 3.5.0', # py3 compat 'venusian >= 1.0a1', # ``onerror`` 'translationstring >= 0.4', # py3 compat + 'PasteDeploy >= 1.5.0', # py3 compat ] -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,18 +84,21 @@ setup(name='pyramid', tests_require = tests_require, test_suite="pyramid.tests", entry_points = """\ - [paste.paster_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] - pshell=pyramid.paster:PShellCommand - proutes=pyramid.paster:PRoutesCommand - pviews=pyramid.paster:PViewsCommand - ptweens=pyramid.paster:PTweensCommand + [pyramid.scaffold] + starter=pyramid.scaffolds:StarterProjectTemplate + zodb=pyramid.scaffolds:ZODBProjectTemplate + routesalchemy=pyramid.scaffolds:RoutesAlchemyProjectTemplate + alchemy=pyramid.scaffolds:AlchemyProjectTemplate [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 """ ) |
