diff options
| author | Christoph Zwerschke <cito@online.de> | 2016-04-19 20:07:12 +0200 |
|---|---|---|
| committer | Christoph Zwerschke <cito@online.de> | 2016-04-19 20:07:12 +0200 |
| commit | 3629c49e46207ff5162a82883c14937e6ef4c186 (patch) | |
| tree | 1306181202cb8313f16080789f5b9ab1eeb61d53 /docs/narr/firstapp.rst | |
| parent | 804ba0b2f434781e77d2b5191f1cd76a490f6610 (diff) | |
| parent | 6c16fb020027fac47e4d2e335cd9e264dba8aa3b (diff) | |
| download | pyramid-3629c49e46207ff5162a82883c14937e6ef4c186.tar.gz pyramid-3629c49e46207ff5162a82883c14937e6ef4c186.tar.bz2 pyramid-3629c49e46207ff5162a82883c14937e6ef4c186.zip | |
Merge remote-tracking branch 'refs/remotes/Pylons/master'
Diffstat (limited to 'docs/narr/firstapp.rst')
| -rw-r--r-- | docs/narr/firstapp.rst | 341 |
1 files changed, 133 insertions, 208 deletions
diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst index f5adad905..6a952dec9 100644 --- a/docs/narr/firstapp.rst +++ b/docs/narr/firstapp.rst @@ -1,111 +1,101 @@ +.. index:: + single: hello world program + .. _firstapp_chapter: Creating Your First :app:`Pyramid` Application -================================================= +============================================== In this chapter, we will walk through the creation of a tiny :app:`Pyramid` application. After we're finished creating the application, we'll explain in -more detail how it works. +more detail how it works. It assumes you already have :app:`Pyramid` installed. +If you do not, head over to the :ref:`installing_chapter` section. .. _helloworld_imperative: -Hello World, Goodbye World --------------------------- +Hello World +----------- -Here's one of the very simplest :app:`Pyramid` applications, configured -imperatively: +Here's one of the very simplest :app:`Pyramid` applications: -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: - from pyramid.config import Configurator - from pyramid.response import Response - from paste.httpserver import serve +When this code is inserted into a Python script named ``helloworld.py`` and +executed by a Python interpreter which has the :app:`Pyramid` software +installed, an HTTP server is started on TCP port 8080. - def hello_world(request): - return Response('Hello world!') +On UNIX: - def goodbye_world(request): - return Response('Goodbye world!') +.. code-block:: text - if __name__ == '__main__': - config = Configurator() - config.add_view(hello_world) - config.add_view(goodbye_world, name='goodbye') - app = config.make_wsgi_app() - serve(app, host='0.0.0.0') + $ $VENV/bin/python helloworld.py -When this code is inserted into a Python script named ``helloworld.py`` and -executed by a Python interpreter which has the :app:`Pyramid` software -installed, an HTTP server is started on TCP port 8080: +On Windows: .. code-block:: text - $ python helloworld.py - serving on 0.0.0.0:8080 view at http://127.0.0.1:8080 + C:\> %VENV%\Scripts\python.exe helloworld.py + +This command will not return and nothing will be printed to the console. When +port 8080 is visited by a browser on the URL ``/hello/world``, the server will +simply serve up the text "Hello world!". If your application is running on +your local system, using `<http://localhost:8080/hello/world>`_ in a browser +will show this result. -When port 8080 is visited by a browser on the root URL (``/``), the server -will simply serve up the text "Hello world!" When visited by a browser on -the URL ``/goodbye``, the server will serve up the text "Goodbye world!" +Each time you visit a URL served by the application in a browser, a logging +line will be emitted to the console displaying the hostname, the date, the +request method and path, and some additional information. This output is done +by the wsgiref server we've used to serve this application. It logs an "access +log" in Apache combined logging format to the console. -Press ``Ctrl-C`` to stop the application. +Press ``Ctrl-C`` (or ``Ctrl-Break`` on Windows) to stop the application. Now that we have a rudimentary understanding of what the application does, -let's examine it piece-by-piece. +let's examine it piece by piece. Imports ~~~~~~~ -The above ``helloworld.py`` script uses the following set of import -statements: +The above ``helloworld.py`` script uses the following set of import statements: -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: - - from pyramid.config import Configurator - from pyramid.response import Response - from paste.httpserver import serve + :lines: 1-3 The script imports the :class:`~pyramid.config.Configurator` class from the :mod:`pyramid.config` module. An instance of the :class:`~pyramid.config.Configurator` class is later used to configure your :app:`Pyramid` application. -The script uses the :class:`pyramid.response.Response` class later in the -script to create a :term:`response` object. - Like many other Python web frameworks, :app:`Pyramid` uses the :term:`WSGI` protocol to connect an application and a web server together. The -:mod:`paste.httpserver` server is used in this example as a WSGI server for -convenience, as the ``paste`` package is a dependency of :app:`Pyramid` -itself. +:mod:`wsgiref` server is used in this example as a WSGI server for convenience, +as it is shipped within the Python standard library. + +The script also imports the :class:`pyramid.response.Response` class for later +use. An instance of this class will be used to create a web response. View Callable Declarations ~~~~~~~~~~~~~~~~~~~~~~~~~~ -The above script, beneath its set of imports, defines two functions: one -named ``hello_world`` and one named ``goodbye_world``. +The above script, beneath its set of imports, defines a function named +``hello_world``. -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: + :pyobject: hello_world - def hello_world(request): - return Response('Hello world!') +The function accepts a single argument (``request``) and it returns an instance +of the :class:`pyramid.response.Response` class. The single argument to the +class' constructor is a string computed from parameters matched from the URL. +This value becomes the body of the response. - def goodbye_world(request): - return Response('Goodbye world!') - -These functions don't do anything very difficult. Both functions accept a -single argument (``request``). The ``hello_world`` function does nothing but -return a response instance with the body ``Hello world!``. The -``goodbye_world`` function returns a response instance with the body -``Goodbye world!``. - -Each of these functions is known as a :term:`view callable`. A view callable -accepts a single argument, ``request``. It is expected to return a -:term:`response` object. A view callable doesn't need to be a function; it -can be represented via another type of object, like a class or an instance, -but for our purposes here, a function serves us well. +This function is known as a :term:`view callable`. A view callable accepts a +single argument, ``request``. It is expected to return a :term:`response` +object. A view callable doesn't need to be a function; it can be represented +via another type of object, like a class or an instance, but for our purposes +here, a function serves us well. A view callable is always called with a :term:`request` object. A request object is a representation of an HTTP request sent to :app:`Pyramid` via the @@ -113,18 +103,11 @@ active :term:`WSGI` server. A view callable is required to return a :term:`response` object because a response object has all the information necessary to formulate an actual HTTP -response; this object is then converted to text by the upstream :term:`WSGI` -server and sent back to the requesting browser. To return a response, each -view callable creates an instance of the :class:`~pyramid.response.Response` -class. In the ``hello_world`` function, the string ``'Hello world!'`` is -passed to the ``Response`` constructor as the *body* of the response. In the -``goodbye_world`` function, the string ``'Goodbye world!'`` is passed. - -.. note:: As we'll see in later chapters, returning a literal - :term:`response` object from a view callable is not always required; we - can instead use a :term:`renderer` in our view configurations. If we use - a renderer, our view callable is allowed to return a value that the - renderer understands, and the renderer generates a response on our behalf. +response; this object is then converted to text by the :term:`WSGI` server +which called Pyramid and it is sent back to the requesting browser. To return +a response, each view callable creates an instance of the +:class:`~pyramid.response.Response` class. In the ``hello_world`` function, a +string is passed as the body to the response. .. index:: single: imperative configuration @@ -136,114 +119,63 @@ passed to the ``Response`` constructor as the *body* of the response. In the Application Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~ -In the above script, the following code represents the *configuration* of -this simple application. The application is configured using the previously -defined imports and function definitions, placed within the confines of an -``if`` statement: +In the above script, the following code represents the *configuration* of this +simple application. The application is configured using the previously defined +imports and function definitions, placed within the confines of an ``if`` +statement: -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: + :lines: 9-15 - if __name__ == '__main__': - config = Configurator() - config.add_view(hello_world) - config.add_view(goodbye_world, name='goodbye') - app = config.make_wsgi_app() - serve(app, host='0.0.0.0') - -Let's break this down this piece-by-piece. +Let's break this down piece by piece. Configurator Construction ~~~~~~~~~~~~~~~~~~~~~~~~~ -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: - - if __name__ == '__main__': - config = Configurator() + :lines: 9-10 The ``if __name__ == '__main__':`` line in the code sample above represents a Python idiom: the code inside this if clause is not invoked unless the script -containing this code is run directly from the command line. For example, if -the file named ``helloworld.py`` contains the entire script body, the code -within the ``if`` statement will only be invoked when ``python -helloworld.py`` is executed from the operating system command line. - -``helloworld.py`` in this case is a Python :term:`module`. Using the ``if`` -clause is necessary -- or at least best practice -- because code in any -Python module may be imported by another Python module. By using this idiom, -the script is indicating that it does not want the code within the ``if`` -statement to execute if this module is imported; the code within the ``if`` -block should only be run during a direct script execution. +containing this code is run directly from the operating system command line. +For example, if the file named ``helloworld.py`` contains the entire script +body, the code within the ``if`` statement will only be invoked when ``python +helloworld.py`` is executed from the command line. + +Using the ``if`` clause is necessary—or at least best practice—because code in +a Python ``.py`` file may be eventually imported via the Python ``import`` +statement by another ``.py`` file. ``.py`` files that are imported by other +``.py`` files are referred to as *modules*. By using the ``if __name__ == +'__main__':`` idiom, the script above is indicating that it does not want the +code within the ``if`` statement to execute if this module is imported from +another; the code within the ``if`` block should only be run during a direct +script execution. The ``config = Configurator()`` line above creates an instance of the :class:`~pyramid.config.Configurator` class. The resulting ``config`` object represents an API which the script uses to configure this particular :app:`Pyramid` application. Methods called on the Configurator will cause -registrations to be made in a :term:`application registry` associated with -the application. +registrations to be made in an :term:`application registry` associated with the +application. .. _adding_configuration: Adding Configuration ~~~~~~~~~~~~~~~~~~~~ -.. ignore-next-block -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: + :lines: 11-12 + +The first line above calls the :meth:`pyramid.config.Configurator.add_route` +method, which registers a :term:`route` to match any URL path that begins with +``/hello/`` followed by a string. - config.add_view(hello_world) - config.add_view(goodbye_world, name='goodbye') - -Each of these lines calls the :meth:`pyramid.config.Configurator.add_view` -method. The ``add_view`` method of a configurator registers a :term:`view -configuration` within the :term:`application registry`. A :term:`view -configuration` represents a set of circumstances related to the -:term:`request` that will cause a specific :term:`view callable` to be -invoked. This "set of circumstances" is provided as one or more keyword -arguments to the ``add_view`` method. Each of these keyword arguments is -known as a view configuration :term:`predicate`. - -The line ``config.add_view(hello_world)`` registers the ``hello_world`` -function as a view callable. The ``add_view`` method of a Configurator must -be called with a view callable object or a :term:`dotted Python name` as its -first argument, so the first argument passed is the ``hello_world`` function. -This line calls ``add_view`` with a *default* value for the :term:`predicate` -argument, named ``name``. The ``name`` predicate defaults to a value -equalling the empty string (``''``). This means that we're instructing -:app:`Pyramid` to invoke the ``hello_world`` view callable when the -:term:`view name` is the empty string. We'll learn in later chapters what a -:term:`view name` is, and under which circumstances a request will have a -view name that is the empty string; in this particular application, it means -that the ``hello_world`` view callable will be invoked when the root URL -``/`` is visited by a browser. - -The line ``config.add_view(goodbye_world, name='goodbye')`` registers the -``goodbye_world`` function as a view callable. The line calls ``add_view`` -with the view callable as the first required positional argument, and a -:term:`predicate` keyword argument ``name`` with the value ``'goodbye'``. -The ``name`` argument supplied in this :term:`view configuration` implies -that only a request that has a :term:`view name` of ``goodbye`` should cause -the ``goodbye_world`` view callable to be invoked. In this particular -application, this means that the ``goodbye_world`` view callable will be -invoked when the URL ``/goodbye`` is visited by a browser. - -Each invocation of the ``add_view`` method registers a :term:`view -configuration`. Each :term:`predicate` provided as a keyword argument to the -``add_view`` method narrows the set of circumstances which would cause the -view configuration's callable to be invoked. In general, a greater number of -predicates supplied along with a view configuration will more strictly limit -the applicability of its associated view callable. When :app:`Pyramid` -processes a request, the view callable with the *most specific* view -configuration (the view configuration that matches the most specific set of -predicates) is always invoked. - -In this application, :app:`Pyramid` chooses the most specific view callable -based only on view :term:`predicate` applicability. The ordering of calls to -:meth:`~pyramid.config.Configurator.add_view` is never very important. We can -register ``goodbye_world`` first and ``hello_world`` second; :app:`Pyramid` -will still give us the most specific callable when a request is dispatched to -it. +The second line registers the ``hello_world`` function as a :term:`view +callable` and makes sure that it will be called when the ``hello`` route is +matched. .. index:: single: make_wsgi_app @@ -252,74 +184,67 @@ it. WSGI Application Creation ~~~~~~~~~~~~~~~~~~~~~~~~~ -.. ignore-next-block -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: - - app = config.make_wsgi_app() + :lines: 13 After configuring views and ending configuration, the script creates a WSGI -*application* via the :meth:`pyramid.config.Configurator.make_wsgi_app` -method. A call to ``make_wsgi_app`` implies that all configuration is -finished (meaning all method calls to the configurator which set up views, -and various other configuration settings have been performed). The -``make_wsgi_app`` method returns a :term:`WSGI` application object that can -be used by any WSGI server to present an application to a requestor. -:term:`WSGI` is a protocol that allows servers to talk to Python -applications. We don't discuss :term:`WSGI` in any depth within this book, -however, you can learn more about it by visiting `wsgi.org -<http://wsgi.org>`_. - -The :app:`Pyramid` application object, in particular, is an instance of a -class representing a :app:`Pyramid` :term:`router`. It has a reference to -the :term:`application registry` which resulted from method calls to the -configurator used to configure it. The :term:`router` consults the registry -to obey the policy choices made by a single application. These policy -choices were informed by method calls to the :term:`Configurator` made -earlier; in our case, the only policy choices made were implied by two calls -to its ``add_view`` method. +*application* via the :meth:`pyramid.config.Configurator.make_wsgi_app` method. +A call to ``make_wsgi_app`` implies that all configuration is finished +(meaning all method calls to the configurator, which sets up views and various +other configuration settings, have been performed). The ``make_wsgi_app`` +method returns a :term:`WSGI` application object that can be used by any WSGI +server to present an application to a requestor. :term:`WSGI` is a protocol +that allows servers to talk to Python applications. We don't discuss +:term:`WSGI` in any depth within this book, but you can learn more about it by +visiting `wsgi.org <http://wsgi.org>`_. + +The :app:`Pyramid` application object, in particular, is an instance of a class +representing a :app:`Pyramid` :term:`router`. It has a reference to the +:term:`application registry` which resulted from method calls to the +configurator used to configure it. The :term:`router` consults the registry to +obey the policy choices made by a single application. These policy choices +were informed by method calls to the :term:`Configurator` made earlier; in our +case, the only policy choices made were implied by calls to its ``add_view`` +and ``add_route`` methods. WSGI Application Serving ~~~~~~~~~~~~~~~~~~~~~~~~ -.. ignore-next-block -.. code-block:: python +.. literalinclude:: helloworld.py :linenos: - - serve(app, host='0.0.0.0') - -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 -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 -means we want to use the default TCP port, which is 8080. - -When this line is invoked, it causes the server to start listening on TCP -port 8080. It will serve requests forever, or at least until we stop it by -killing the process which runs it (usually by pressing ``Ctrl-C`` in the -terminal we used to start it). + :lines: 14-15 + +Finally, we actually serve the application to requestors by starting up a WSGI +server. We happen to use the :mod:`wsgiref` ``make_server`` server maker for +this purpose. We pass in as the first argument ``'0.0.0.0'``, which means +"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 also specify a TCP port number to listen on, which is 8080, passing +it as the second argument. The final argument is the ``app`` object (a +:term:`router`), which is the application we wish to serve. Finally, we call +the server's ``serve_forever`` method, which starts the main loop in which it +will wait for requests from the outside world. + +When this line is invoked, it causes the server to start listening on TCP port +8080. The server will serve requests forever, or at least until we stop it by +killing the process which runs it (usually by pressing ``Ctrl-C`` or +``Ctrl-Break`` in the terminal we used to start it). Conclusion ~~~~~~~~~~ Our hello world application is one of the simplest possible :app:`Pyramid` applications, configured "imperatively". We can see that it's configured -imperatively because the full power of Python is available to us as we -perform configuration tasks. +imperatively because the full power of Python is available to us as we perform +configuration tasks. References ---------- -For more information about the API of a :term:`Configurator` object, -see :class:`~pyramid.config.Configurator` . +For more information about the API of a :term:`Configurator` object, see +:class:`~pyramid.config.Configurator` . For more information about :term:`view configuration`, see :ref:`view_config_chapter`. - -An example of using *declarative* configuration (:term:`ZCML`) instead of -imperative configuration to create a similar "hello world" is available -within the documentation for :term:`pyramid_zcml`. |
