diff options
| -rw-r--r-- | CHANGES.txt | 9 | ||||
| -rw-r--r-- | docs/glossary.rst | 26 | ||||
| -rw-r--r-- | docs/index.rst | 1 | ||||
| -rw-r--r-- | docs/narr/MyProject/myproject/configure.zcml | 2 | ||||
| -rw-r--r-- | docs/narr/MyProject/myproject/run.py | 12 | ||||
| -rw-r--r-- | docs/narr/project.rst | 23 | ||||
| -rw-r--r-- | docs/narr/startup.rst | 169 | ||||
| -rw-r--r-- | docs/narr/traversal.rst | 138 | ||||
| -rw-r--r-- | docs/notes.txt | 7 |
9 files changed, 302 insertions, 85 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index e8a7b1e71..c4ae68324 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,16 @@ Next Release + Features + + - Add startup process docs. + Bug Fixes + - Move core repoze.bfg ZCML into a ``repoze.bfg.includes`` package so we can + use repoze.bfg better as a namespace package. Adjust the code generator to + use it. We've left around the older configure.zcml in the repoze.bfg + package directly so as not to break older apps. + - When a zcml application registry cache was unpickled, and it contained a reference to an object that no longer existed (such as a view), bfg would not start properly. diff --git a/docs/glossary.rst b/docs/glossary.rst index 439b39424..1dd5fa821 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -31,6 +31,15 @@ Glossary files that have the suffix of ``.egg``, ``.tar.gz``, or ``.zip``. Distributions are the target of Setuptools commands such as ``easy_install``. + Entry Point + A :term:`setuptools` indirection, defined within a setuptools + :term:`distribution` setup.py. It is usually a name which refers + to a function somewhere in a package which is held by the + distribution. + 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 + setuptools configurations. View A "view" is a callable which returns a response object. It should accept two values: context and request. @@ -52,6 +61,11 @@ Glossary model in order to find a :term:`context`. The :mod:`repoze.bfg` *router* performs traversal of model objects. See the :ref:`traversal_chapter` chapter for more information. + Router + The :term:`WSGI` application created when you start a + :mod:`repoze.bfg` application. The router intercepts requests, + invokes traversal, calls view functions, and returns responses to + the WSGI server. URL dispatch An alternative to graph traversal as a mechanism for locating a :term:`context` for a :term:`view`. When you use :term:`Routes` @@ -156,6 +170,12 @@ Glossary encoding, and other functions. See `WSGI.org <http://wsgi.org>`_ or `PyPI <http://python.org/pypi>`_ to find middleware for your application. + Pipeline + The :term:`Paste` term for a single configuration of a WSGI + server, a WSGI application, with a set of middleware in-between. + mod_wsgi + An `Apache module <http://code.google.com/p/modwsgi/>`_ for hosting + Python WSGI applications. Zope `The Z Object Publishing Framework <http://zope.org>`_. The granddaddy of Python web frameworks. @@ -220,4 +240,8 @@ Glossary A `plain text format <http://docutils.sourceforge.net/rst.html>`_ that is the defacto standard for descriptive text shipped in :term:`distribution` files, and Python docstrings. - + Subpath + A list of element "left over" after the :term:`router` has + performed a successful traversal to a view. The subpath is a + sequence of strings, e.g. ``['left', 'over', 'names']``. + diff --git a/docs/index.rst b/docs/index.rst index 3d18c115f..5362b9681 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,7 @@ Narrative documentation in chapter form explaining how to use narr/introduction narr/install narr/project + narr/startup narr/urlmapping narr/traversal narr/urldispatch diff --git a/docs/narr/MyProject/myproject/configure.zcml b/docs/narr/MyProject/myproject/configure.zcml index 174b27354..c9bb92565 100644 --- a/docs/narr/MyProject/myproject/configure.zcml +++ b/docs/narr/MyProject/myproject/configure.zcml @@ -3,7 +3,7 @@ i18n_domain="repoze.bfg"> <!-- this must be included for the view declarations to work --> - <include package="repoze.bfg" /> + <include package="repoze.bfg.includes" /> <bfg:view for=".models.IMyModel" diff --git a/docs/narr/MyProject/myproject/run.py b/docs/narr/MyProject/myproject/run.py index 07241ccc7..33d534f2d 100644 --- a/docs/narr/MyProject/myproject/run.py +++ b/docs/narr/MyProject/myproject/run.py @@ -2,12 +2,10 @@ from repoze.bfg.router import make_app from repoze.bfg.registry import get_options def app(global_config, **kw): - # paster app config callback + """ This function returns a repoze.bfg.router.Router object. It + is usually called by the PasteDeploy framework during ``paster + serve``""" from myproject.models import get_root import myproject - return make_app(get_root, myproject, options=get_options(kw)) - -if __name__ == '__main__': - from paste import httpserver - httpserver.serve(app(None), host='0.0.0.0', port='6543') - + options = get_options(kw) + return make_app(get_root, myproject, options=options) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 8daac5e32..1dca10991 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -1,6 +1,6 @@ .. _project_narr: -Starting a :mod:`repoze.bfg` Project +Creating a :mod:`repoze.bfg` Project ==================================== You can use :mod:`repoze.bfg` 's sample application generator to get @@ -224,6 +224,8 @@ wants to install your package does not have :term:`Setuptools` already installed. It is only imported by and used by ``setup.py``, so we won't describe it here. +.. _MyProject_ini: + ``MyProject.ini`` ~~~~~~~~~~~~~~~~~ @@ -256,9 +258,11 @@ invoked against this configuration file. The name ``main`` is a convention signifying that it the default application. The ``use`` setting is required in the ``[app:main]`` section. The -``use`` setting points at a :term:`setuptools` "entry point" named -``MyProject#app`` (the ``egg:`` prefix in ``egg:MyProject#app`` -indicates that this is an entry point specifier). +``use`` setting points at a :term:`setuptools` :term:`entry point` +named ``MyProject#app`` (the ``egg:`` prefix in ``egg:MyProject#app`` +indicates that this is an entry point *URI* specifier, where the +"scheme" is "egg"; there are no other schemes currently, so the +``egg:`` prefix is arguably not very useful). .. note:: @@ -442,7 +446,10 @@ registry`. It looks like so: :mod:`repoze.bfg` -specific configuration directives. #. Line 6 initializes :mod:`repoze.bfg`-specific configuration - directives by including it as a package. + directives by including the ``repoze.bfg.includes`` package. This + causes all of the ZCML within the ``configure.zcml`` of the + ``repoze.bfg.includes`` package (which can be found in the main + :mod:`repoze.bfg` sources). #. Lines 8-11 register a single view. It is ``for`` model objects that support the IMyModel interface. The ``view`` attribute points @@ -552,15 +559,11 @@ without the PasteDeploy configuration file: #. Lines 1 - 2 import functions from :mod:`repoze.bfg` that we use later. -#. Lines 4-9 define a function that returns a :mod:`repoze.bfg` Router +#. Lines 4-12 define a function that returns a :mod:`repoze.bfg` Router application from :ref:`router_module` . This is meant to be called by the :term:`PasteDeploy` framework as a result of running ``paster serve``. -#. Lines 11 - 13 allow this file to serve optionally as a shortcut for - executing our program if the ``run.py`` file is executed directly. - It starts our application under a web server on port 6543. - ``templates/mytemplate.pt`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst new file mode 100644 index 000000000..a0ca8354f --- /dev/null +++ b/docs/narr/startup.rst @@ -0,0 +1,169 @@ +.. _startup_chapter: + +Startup +======= + +When you cause :mod:`repoze.bfg` to start up in the foreground, you'll +see something much like this show up on your console:: + + $ paster serve 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`` and the resulting output of the line +``serving on 0.0.0:6543 ...`` to your console. + +The Startup Process +------------------- + +The easiest and best-documented way to start and serve a +:mod:`repoze.bfg` application is to use the ``paster serve`` 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 :mod:`repoze.bfg` application. + +.. note:: ``paster serve`` is by no means the only way to start up and + serve a :mod:`repoze.bfg` application. Any :term:`WSGI` server is + capable of running a :mod:`repoze.bfg` application, and some WSGI + servers (such as :term:`mod_wsgi`) don't require the + :term:`PasteDeploy` framework's ``paster serve`` command to do + server process management. 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. + +Here's a high-level time-ordered overview of what happens when you +press ``return`` after running ``paster serve MyProject.ini``. + +#. The :term:`PasteDeploy` ``paster`` command is invoked under your + shell with the arguments ``serve`` and ``MyProject.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 ``MyProject.ini`` file. + +#. The PasteDeploy framework finds a section named either + ``[app:main]`` or ``[pipeline: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. an + ``[app:main]`` section of a default-generated :mod:`repoze.bfg` + project), the application :term:`entry point` or :term:`dotted + Python name` will be named on the ``use=`` line within the + section's 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 :mod:`repoze.bfg` application. + +#. The application's *constructor* (named by the entry point reference + or dotted Python name on the ``use=`` line) is passed the key/value + parameters mentioned within the section in which it's defined. The + constructor is meant to return :term:`router` instance. + + For ``repoze.bfg`` applications, the constructor will be a function + named ``app`` in the ``run.py`` file within the :term:`package` in + which your application lives. If this function succeeds, it will + return a :mod:`repoze.bfg` :term:`router` instance. Here's the + contents of an example ``run.py`` module: + + .. literalinclude:: MyProject/myproject/run.py + :linenos: + + Note that the constructor function accepts a ``global_config`` + argument (which are the key/value pairs mentioned in the + ``[DEFAULT]`` section of the configuration file. It also accepts a + ``**kw`` argument, which collects arbitrary key/value pairs. The + arbitrary key/value pairs received by this function in ``**kw`` + 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``. + + Our generated ``MyProject.ini`` file looks like so: + + .. literalinclude:: MyProject/MyProject.ini + :linenos: + + In this case, the ``myproject.run:app`` function referred to by the + entry point URI ``egg:MyProject#app`` (see :ref:`MyProject_ini` for + more information about entry point URIs, and how they relate to + callables), will receive the key/value pairs + ``{'reload_templates':'true'}``. + +#. The constructor itself is invoked. A generated :mod:`repoze.bfg` + ``app`` function will look like the below. + + .. literalinclude:: MyProject/myproject/run.py + :linenos: + + Note that the app function imports the ``get_root`` function from + the ``myproject.models`` Python module. It then also imports the + "bare" ``myproject`` package, and passes ``get_root``, + ``myproject``, and the result of ``get_options(kw)`` as the + ``options`` keyword to the ``make_app`` function of the + ``repoze.bfg.router`` module. + + ``get_options`` is a function imported from a :mod:`repoze.bfg` + package which allows the user to pass framework-related (as opposed + to application-related) options to an application constructor. It + picks off framework-related options from the ``*kw`` dict passed in + to the constructor. We actually use a framework option named + ``reload_templates`` in our configuration. Note that we make no + use of this option in our application, but the fact that we use + ``get_options`` to parse the ``*kw`` dict, and subsequently pass + along the result as the ``options`` argument to ``make_app``. + + ``get_root`` is the first argument to ``make_app``, and it is a + callable that is invoked on every request to retrieve the + application root. It is not called during startup, only when a + request is handled. + + We pass in the bare ``myproject`` package so that the ``make_app`` + callback knows where to look for the :term:`application registry` + file (conventially named ``configure.zcml``). ``make_app`` will + use the package's path and look for ``configure.zcml`` within that + package's filesystem directory. If you for some reason need or + want to load a different application registry filename for your + application, you can pass an optional ``filename=`` paramter to + make_app (e.g. ``make_app(get_root, myproject, + filename='meta.zcml', options=options``). If the filename is + absolute, the package is ignored. + +#. The ``make_app`` function does its work. It parses the ZCML + represented by the application registry file (or may obtain the + application registry from a previously cached pickle file, + e.g. ``configure.zcml.cache``). If it fails to parse one or more + ZCML files, a ``XMLConfigurationError`` is raised. If it succeeds, + the :term:`application registry` is created, a :term:`router` + instance is created, and the router is associated with the + application registry. The router represents your application; the + settings in this application registry will be used for your + application. + +#. A ``WSGIApplicationCreatedEvent`` event is emitted (see + :ref:`events_chapter` for more informations about events). + +#. Assuming there were no errors, our ``myproject`` ``app`` function + returns the router instance created by ``make_app`` back to + PasteDeploy. As far as PasteDeploy 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 "CherryPy" + server (``use = egg:PasteScript#cherrypy``), and it will listen on + all interfaces (``host = 0.0.0.0``), on port number 6543 (``port = + 6543``). It will serve up the application using 4 simultaneous + threads (``numthreads = 4``), which means it will handle four + simultaneous requests before needing to put a request in a wait + queue. The server code itself is what prints `serving on + 0.0.0.0:6543 view at http://127.0.0.1:6543``. + +#. The application is running. + + + + + + diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index f03a22c9e..d57cf8f02 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -62,72 +62,74 @@ When a user requests a page from your :mod:`repoze.bfg` -powered application, the system uses this algorithm to determine which Python code to execute: - 1. The request for the page is presented to :mod:`repoze.bfg`'s +#. The request for the page is presented to :mod:`repoze.bfg`'s "router" in terms of a standard :term:`WSGI` request, which is represented by a WSGI environment and a ``start_response`` callable. - 2. The router creates a :term:`WebOb` request object based on the - WSGI environment. - - 3. The router uses the WSGI environment's ``PATH_INFO`` variable to - determine the path segments to traverse. The leading slash is - stripped off ``PATH_INFO``, and the remaining path segments are - split on the slash character to form a traversal sequence, so a - request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the - traversal sequence ``['a', 'b', 'c']``. - - 4. :term:`Traversal` begins at the root object. For the traversal - sequence ``['a', 'b', 'c']``, the root object's ``__getitem__`` - is called with the name ``a``. Traversal continues through the - sequence. In our example, if the root object's ``__getitem__`` - called with the name ``a`` returns an object (aka "object A"), - that object's ``__getitem__`` is called with the name ``b``. If - object A returns an object when asked for ``b``, object B's - ``__getitem__`` is then asked for the name ``c``, and may return - object C. - - 5. Traversal ends when a) the entire path is exhausted or b) when - any graph element raises a ``KeyError`` from its ``__getitem__`` - or c) when any non-final path element traversal does not have a - ``__getitem__`` method (resulting in a ``NameError``) or d) when - any path element is prefixed with the set of characters ``@@`` - (indicating that the characters following the ``@@`` token should - be treated as a "view name"). - - 6. When traversal ends for any of the reasons in the previous step, - the the last object found during traversal is deemed to be the - :term:`context`. If the path has been exhausted when traversal - ends, the "view name" is deemed to be the empty string (``''``). - However, if the path was *not* exhausted before traversal - terminated, the first remaining path element is treated as the - view name. - - Any subseqent path elements after the view name are deemed the - *subpath*. For instance, if ``PATH_INFO`` was ``/a/b`` and the - root returned an "A" object, and the "A" object returned a "B" - object, the router deems that the context is "object B", the view - name is the empty string, and the subpath is the empty sequence. - On the other hand, if ``PATH_INFO`` was ``/a/b/c`` and "object A" - was found but raised a ``KeyError`` for the name ``b``, the - router deems that the context is object A, the view name is ``b`` - and the subpath is ``['c']``. - - 7. If a :term:`security policy` is configured, the router performs a - permission lookup. If a permission declaration is found for the - view name and context implied by the current request, the - security policy is consulted to see if the "current user" (also - determined by the security policy) can perform the action. If he - can, processing continues. If he cannot, an ``HTTPUnauthorized`` - error is raised. - - 8. Armed with the context, the view name, and the subpath, the - router performs a view lookup. It attemtps to look up a view - from the :mod:`repoze.bfg` :term:`application registry` using the - view name and the context. If a view function is found, it is - called with the context and the request. It returns a response, - which is fed back upstream. If a view is not found, a generic - WSGI ``NotFound`` application is constructed. +#. The router creates a :term:`WebOb` request object based on the + WSGI environment. + +#. The router uses the WSGI environment's ``PATH_INFO`` variable to + determine the path segments to traverse. The leading slash is + stripped off ``PATH_INFO``, and the remaining path segments are + split on the slash character to form a traversal sequence, so a + request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the + traversal sequence ``['a', 'b', 'c']``. + +#. :term:`Traversal` begins at the root object. For the traversal + sequence ``['a', 'b', 'c']``, the root object's ``__getitem__`` is + called with the name ``a``. Traversal continues through the + sequence. In our example, if the root object's ``__getitem__`` + called with the name ``a`` returns an object (aka "object ``a``"), + that object's ``__getitem__`` is called with the name ``b``. If + object A returns an object when asked for ``b``, "object ``b``"'s + ``__getitem__`` is then asked for the name ``c``, and may return + "object ``c``". + +#. Traversal ends when a) the entire path is exhausted or b) when any + graph element raises a ``KeyError`` from its ``__getitem__`` or c) + when any non-final path element traversal does not have a + ``__getitem__`` method (resulting in a ``NameError``) or d) when + any path element is prefixed with the set of characters ``@@`` + (indicating that the characters following the ``@@`` token should + be treated as a "view name"). + +#. When traversal ends for any of the reasons in the previous step, + the the last object found during traversal is deemed to be the + :term:`context`. If the path has been exhausted when traversal + ends, the "view name" is deemed to be the empty string (``''``). + However, if the path was *not* exhausted before traversal + terminated, the first remaining path element is treated as the + view name. + + Any subseqent path elements after the view name are deemed the + :term:`subpath`. The subpath is always a sequence of strings that + come from ``PATH_INFO`` that are "left over" after traversal has + completed. For instance, if ``PATH_INFO`` was ``/a/b`` and the + root returned an "object ``a``", and "object ``a``" subsequently + returned an "object ``b``", the router deems that the context is + "object ``b``", the view name is the empty string, and the subpath + is the empty sequence. On the other hand, if ``PATH_INFO`` was + ``/a/b/c`` and "object ``a``" was found but raised a ``KeyError`` + for the name ``b``, the router deems that the context is "object + ``a``", the view name is ``b`` and the subpath is ``['c']``. + +#. If a :term:`security policy` is configured, the router performs a + permission lookup. If a permission declaration is found for the + view name and context implied by the current request, the security + policy is consulted to see if the "current user" (also determined + by the security policy) can perform the action. If he can, + processing continues. If he cannot, an ``HTTPUnauthorized`` error + is raised. + +#. Armed with the context, the view name, and the subpath, the router + performs a view lookup. It attemtps to look up a view from the + :mod:`repoze.bfg` :term:`application registry` using the view name + and the context. If a view function is found, it is called with + the context and the request. It returns a response, which is fed + back upstream. If a view is not found, a generic WSGI + ``NotFound`` application is constructed. In either case, the result is returned upstream via the :term:`WSGI` protocol. @@ -244,3 +246,15 @@ There are two special cases: to address views that may have the same names as model instance names in the graph unambiguously. +Traversal-Related Side Effects +------------------------------ + +The :term:`subpath` will always be available to a view as a the +``subpath`` attribute of the :term:`request` object. It will be a +list containing zero or more elements (which will be strings). + +The :term:`view name` will always be available to a view as the +``view_name`` attribute of the :term:`request` object. It will be a +single string (possibly the empty string if we're rendering a default +view). + diff --git a/docs/notes.txt b/docs/notes.txt index 4e641234c..d92c95564 100644 --- a/docs/notes.txt +++ b/docs/notes.txt @@ -1,9 +1,5 @@ - Document z3c.pt -- Spaces in project names (allow for separate project / package names?) - -- Subpath and view name in request - - WebOb Request basics - "push" style templating @@ -15,3 +11,6 @@ - Show usage of, e.g. HTTPFound(location=url). - Document ``make_app`` better; particularly how zcml is found. + +- What is ``global_conf`` passed to the app constructor? How is it + composed? |
