diff options
| author | Casey Duncan <casey.duncan@gmail.com> | 2010-11-22 17:12:13 -0700 |
|---|---|---|
| committer | Casey Duncan <casey.duncan@gmail.com> | 2010-11-22 17:12:13 -0700 |
| commit | 51bed35bfb3bb862034514da257a348e33d53860 (patch) | |
| tree | 2f4a770510c71872cb42dc943f5a0fae7fdb03c9 | |
| parent | 18fc334f0fb0b3f8925f415031a87016ce574320 (diff) | |
| parent | eba45fd998b68d72b6e11f5b0bfa86d0ab17ee43 (diff) | |
| download | pyramid-51bed35bfb3bb862034514da257a348e33d53860.tar.gz pyramid-51bed35bfb3bb862034514da257a348e33d53860.tar.bz2 pyramid-51bed35bfb3bb862034514da257a348e33d53860.zip | |
Merge https://github.com/Pylons/pyramid
53 files changed, 705 insertions, 327 deletions
diff --git a/.gitignore b/.gitignore index 706f6493d..ae0b17b8e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.pt.py *.txt.py .coverage +tutorial.db env26/ env24/ env27/ diff --git a/CHANGES.txt b/CHANGES.txt index 692e3cc35..2ea0ac448 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,14 +4,54 @@ Next release Features -------- +- Add logging configuration to all paster templates. + +- ``pyramid_alchemy``, ``pyramid_routesalchemy``, and ``pylons_sqla`` paster + templates now use idiomatic SQLAlchemy configuration in their respective + ``.ini`` files and Python code. + +- ``pyramid.testig.DummyRequest`` now has a class variable, ``query_string``, + which defaults to the empty string. + +Bug Fixes +--------- + +- The ``pyramid_routesalchemy`` paster template's unit tests failed + (``AssertionError: 'SomeProject' != 'someproject'``). This is fixed. + +- Make default renderer work (renderer factory registered with no name, which + is active for every view unless the view names a specific renderer). + +Documentation +------------- + +- "Sample Applications" section of docs changed to note existence of Cluegun, + Shootout and Virginia sample applications, ported from their repoze.bfg + origin packages. + +- SQLAlchemy+URLDispatch tutorial updated to integrate changes to + ``pyramid_routesalchemy`` template. + +- Add ``pyramid.interfaces.ITemplateRenderer`` interface to Interfaces API + chapter (has ``implementation()`` method, required to be used when getting + at Chameleon macros). + + +1.0a4 (2010-11-21) +================== + +Features +-------- + - URL Dispatch now allows for replacement markers to be located anywhere in the pattern, instead of immediately following a ``/``. - URL Dispatch now uses the form ``{marker}`` to denote a replace marker in - the route pattern instead of ``:marker``. The old syntax is still backwards - compatible and accepted. The new format allows a regular expression for that - marker location to be used instead of the default ``[^/]+``, for example - ``{marker:\d+}`` is now valid to require the marker to be digits. + the route pattern instead of ``:marker``. The old colon-style marker syntax + is still accepted for backwards compatibility. The new format allows a + regular expression for that marker location to be used instead of the + default ``[^/]+``, for example ``{marker:\d+}`` is now valid to require the + marker to be digits. - Add a ``pyramid.url.route_path`` API, allowing folks to generate relative URLs. Calling ``route_path`` is the same as calling @@ -30,6 +70,10 @@ Features ``config.end()`` is no longer necessary. All paster templates have been changed to no longer call these functions. +- Fix configurator to not convert ``ImportError`` to ``ConfigurationError`` + if the import that failed was unrelated to the import requested via a + dotted name when resolving dotted names (such as view dotted names). + Documentation ------------- @@ -58,14 +102,14 @@ Bug Fixes ASCII string rather than being passed along to downstream code as a convenience to the user and to prevent puzzling second-order failures from cropping up (all failures will occur within ``pyramid.traversal.traverse`` - rather than later down the line as the result of calling + rather than later down the line as the result of calling e.g. ``traversal_path``). Backwards Incompatibilities --------------------------- - The ``pyramid.testing.zcml_configure`` API has been removed. It had been - advertised as removed since 1.2a1, but hadn't actually been. + advertised as removed since repoze.bfg 1.2a1, but hadn't actually been. Deprecations ------------ @@ -6,58 +6,17 @@ Must-Have (before 1.0) - Add a ``handler`` ZCML directive. This implies some slightly dicey refactoring of the configurator to allow it to generate ZCML - "discriminators" for views and routes. + "discriminators" for views and routes, that could be implemented in terms + of "twophase configuration" in "should have" below. - Provide a .flash API on session object. -- Make default renderer work (renderer registered with no name, which is - active for every view unless the view names a specific renderer). - - Use ``@register_view`` instead of ``@view_config`` and change view docs to use "view registration" instead of "view configuration". -- SQLAlchemy idiomatics: - - <RaFromBRC> mcdonc: those paster templates all look pretty good... the - only thing i'd consider is adjusting your config variable names to match - exactly what sqlalchemy uses as parameter names, see here: - http://www.sqlalchemy.org/docs/core/engines.html - - <RaFromBRC> mcdonc: especially in the pylons_sqla ini file, where the db - initialization is mixed in w/ the app config... - - <RaFromBRC> ... i'd use "sqlalchemy.PARAMETER" for all of the sqla - settings, so it could easily be handed to engine_from_config w/o any need - to parse by hand - - <RaFromBRC> mcdonc: in the other ini files, where sqlalchemy is given its - own part, the "sqlalchemy." prefix probably isn't necessary, but matching - the parameter names (e.g. 'url' instead of 'db_string') is still probably - a good idea - -- Non-bwcompat use of threadlocals that need to be documented or ameliorated: - - security.principals_allowed_by_permission - - resource.OverrideProvider._get_overrides: can't credibly be removed, - because it stores an overrideprovider as a module-scope global. - - traversal.traverse: this API is a stepchild, and needs to be changed. - - Configurator.add_translation_dirs: not passed any context but a message, - can't credibly be removed. - -- Better ``config.add_handler`` documentation. - -- Fix DottedNameResolver to not convert ImportError to ConfigurationError if - the import that failed was unrelated to the import requested via a dotted - name. - Should-Have ----------- -- Try to make test suite pass on IronPython. - - Add docs for httpexceptions module for each webob.exc class that inherits from WSGIHTTPException. @@ -74,9 +33,27 @@ Should-Have - Change "Cleaning up After a Request" in the urldispatch chapter to use ``request.add_response_callback``. +- Twophase configuration (config = Configurator(autocommit=False)). Maybe + use ``zope.configuration`` ConfigurationContext as config.registry.ctx and + push execution into the configurator. + Nice-to-Have ------------ +- Try to make test suite pass on IronPython. + +- Non-bwcompat use of threadlocals that need to be documented or ameliorated: + + security.principals_allowed_by_permission + + resource.OverrideProvider._get_overrides: can't credibly be removed, + because it stores an overrideprovider as a module-scope global. + + traversal.traverse: this API is a stepchild, and needs to be changed. + + Configurator.add_translation_dirs: not passed any context but a message, + can't credibly be removed. + - Supply ``X-Vhm-Host`` support. - Basic WSGI documentation (pipeline / app / server). @@ -85,7 +62,8 @@ Nice-to-Have - Change docs about creating a venusian decorator to not use ZCA. -- ``decorator=`` parameter to bfg_view. +- ``decorator=`` parameter to view_config. This would replace the existing + _map_view "decorator" if it existed. - Try to better explain the relationship between a renderer and a template in the templates chapter and elsewhere. Scan the diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index b27428d89..b3c14e5f7 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -33,4 +33,5 @@ Other Interfaces .. autointerface:: IRendererInfo + .. autointerface:: ITemplateRenderer diff --git a/docs/conf.py b/docs/conf.py index 81096da3b..e520c9d82 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -76,7 +76,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year # other places throughout the built documents. # # The short X.Y version. -version = '1.0a3' +version = '1.0a4' # The full version, including alpha/beta/rc tags. release = version diff --git a/docs/designdefense.rst b/docs/designdefense.rst index 92facf13c..e3a816269 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -40,9 +40,7 @@ Too Complex if __name__ == '__main__': config = Configurator() - config.begin() config.add_view(hello_world) - config.end() app = config.make_wsgi_app() serve(app) @@ -549,9 +547,7 @@ everything done completely imperatively. For example, the very most basic if __name__ == '__main__': config = Configurator() - config.begin() config.add_view(hello_world) - config.end() app = config.make_wsgi_app() serve(app) @@ -1674,37 +1670,6 @@ can interface with a WSGI application is placed on the server developer, not the web framework developer, making it more likely to be timely and correct. -:meth:`pyramid.configuration.Configurator.begin` and :meth:`pyramid.configuration.Configurator.end` methods -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -The methods :meth:`pyramid.configuration.Configurator.begin` and -:meth:`pyramid.configuration.Configurator.end` are used to bracket -the configuration phase of a :app:`Pyramid` application. - -These exist because existing legacy third party *configuration* (not -runtime) code relies on a threadlocal stack being populated. The -``begin`` method pushes data on to a threadlocal stack. The ``end`` -method pops it back off. - -For the simplest applications, these lines are actually not required. -I *could* omit them from every Pyramid hello world app without ill -effect. However, when users use certain configuration methods (ones -not represented in the hello world app), calling code will begin to -fail when it is not bracketed between a ``begin()`` and an ``end()``. -It is just easier to tell users that this bracketing is required than -to try to explain to them which circumstances it is actually required -and which it is not, because the explanation is often torturous. - -The effectively-required execution of these two methods is a wholly -bogus artifact of an early bad design decision which encouraged -application developers to use threadlocal data structures during the -execution of configuration plugins. However, I don't hate my -framework's users enough to break backwards compatibility for the sake -of removing two boilerplate lines of code, so it stays, at least for -the foreseeable future. If I eventually figure out a way to remove -the requirement, these methods will turn into no-ops and they will be -removed from the documenation. - Wrapping Up +++++++++++ @@ -1724,9 +1689,7 @@ where comments take into account what we've discussed in the if __name__ == '__main__': from pyramid.configuration import Configurator config = Configurator() # no global application object. - config.begin() # bogus, but required. config.add_view(hello_world) # explicit non-decorator registration - config.end() # bogus, but required. app = config.make_wsgi_app() # explicitly WSGI serve(app, host='0.0.0.0') # explicitly WSGI diff --git a/docs/index.rst b/docs/index.rst index bfe956af2..72c21bbc8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -113,38 +113,39 @@ Design Documentation Sample Applications =================== -.. warning:: +`cluegun <https://github.com/Pylons/cluegun>`_ is a simple pastebin +application based on Rocky Burt's `ClueBin +<http://pypi.python.org/pypi/ClueBin/0.2.3>`_. It demonstrates form +processing, security, and the use of :term:`ZODB` within a :app:`Pyramid` +application. Check this application out via:: - These applications are for an older version of :app:`Pyramid`, - which was named :mod:`repoze.bfg`. We'll be updating them soon to - use :app:`Pyramid`. + git clone git://github.com/Pylons/cluegun.git -`repoze.cluegun <http://svn.repoze.org/repoze.cluegun/trunk/>`_ is a -simple pastebin application based on Rocky Burt's `ClueBin -<http://pypi.python.org/pypi/ClueBin/0.2.3>`_. It demonstrates form -processing, security, and the use of :term:`ZODB` within a -:mod:`repoze.bfg` application. It also has very simple -:term:`repoze.who` integration. Check this application out of -Subversion via:: +`virginia <https://github.com/Pylons/virginia>`_ is a very simple dynamic +file rendering application. It is willing to render structured text +documents, HTML documents, and images from a filesystem directory. An +earlier version of this application runs the `repoze.org +<http://repoze.org>`_ website. Check this application out via:: - svn co http://svn.repoze.org/repoze.cluegun/trunk repoze.cluegun + git clone git://github.com/Pylons/virginia.git -`repoze.virginia <http://svn.repoze.org/repoze.virginia/trunk/>`_ is a -very simple dynamic file rendering application. It is willing to -render structured text documents, HTML documents, and images from a -filesystem directory. This application runs the `repoze.org -<http://repoze.org>`_ website. Check this application out of -Subversion via:: +`shootout <https://github.com/Pylons/shootout>`_ is an example "idea +competition" application by Carlos de la Guardia. It demonstrates a hybrid +of :term:`URL dispatch` and :term:`traversal` and integration with +`SQLAlchemy <http://www.sqlalchemy.org/>`_, :term:`repoze.who`, and +`Deliverance <http://www.deliveranceproject.org/>`_. Check this application +out of version control via:: - svn co http://svn.repoze.org/repoze.virginia/trunk repoze.virginia + git clone git://github.com/Pylons/shootout.git -`repoze.shootout <http://svn.repoze.org/repoze.shootout/trunk/>`_ is -an example "idea competition" application by Carlos de la Guardia. It -demonstrates a hybrid of :term:`URL dispatch` and :term:`traversal` -and integration with `SQLAlchemy <http://www.sqlalchemy.org/>`_ and -:term:`repoze.who`. Check this application out of Subversion via:: +Older Sample Applications (repoze.bfg) +====================================== + +.. note:: - svn co http://svn.repoze.org/repoze.shootout/trunk repoze.shootout + These applications are for an older version of :app:`Pyramid`, which was + named :mod:`repoze.bfg`. They won't work unmodified under Pyramid, but + might provide useful clues. `bfgsite <http://svn.repoze.org/bfgsite/trunk>`_ is the software which runs the `bfg.repoze.org <http://bfg.repoze.org>`_ website. It diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini index 9c51cfe6e..80d89e46a 100644 --- a/docs/narr/MyProject/development.ini +++ b/docs/narr/MyProject/development.ini @@ -15,3 +15,29 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/narr/contextfinding.rst b/docs/narr/contextfinding.rst index 457e1a526..691ad7b8e 100644 --- a/docs/narr/contextfinding.rst +++ b/docs/narr/contextfinding.rst @@ -74,7 +74,7 @@ URL dispatch can easily handle URLs such as ``http://example.com/members/Chris``, where it's assumed that each item "below" ``members`` in the URL represents a single member in some system. You just match everything "below" ``members`` to a particular -:term:`view callable`, e.g. ``/members/:memberid``. +:term:`view callable`, e.g. ``/members/{memberid}``. However, URL dispatch is not very convenient if you'd like your URLs to represent an arbitrary hierarchy. For example, if you need to diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst index 48a3ea134..b9dbcab7d 100644 --- a/docs/narr/declarative.rst +++ b/docs/narr/declarative.rst @@ -655,7 +655,7 @@ declaration` causes a route to be added to the application. <route name="myroute" - pattern="/prefix/:one/:two" + pattern="/prefix/{one}/{two}" view=".views.myview" /> diff --git a/docs/narr/handlers.rst b/docs/narr/handlers.rst index b8e7b5d9b..022f27115 100644 --- a/docs/narr/handlers.rst +++ b/docs/narr/handlers.rst @@ -59,11 +59,11 @@ be performed in order to register it with the system: .. code-block:: python - config.add_handler('hello', '/hello/:action', handler=Hello) + config.add_handler('hello', '/hello/{action}', handler=Hello) This example will result in a route being added for the pattern -``/hello/:action``, each method of the ``Hello`` class will then be examined -to register the views. The value of ``:action`` in the route pattern will be +``/hello/{action}``, each method of the ``Hello`` class will then be examined +to register the views. The value of ``{action}`` in the route pattern will be used to determine which view should be called, and each view in the class will be setup with a view predicate that requires a specific ``action`` name. @@ -98,7 +98,7 @@ For example: .. code-block:: python - config.add_handler('hello', '/hello/:action', + config.add_handler('hello', '/hello/{action}', handler='mypackage.handlers:MyHandler') In larger applications, it is advised to use a :term:`resource specification` @@ -219,7 +219,7 @@ Example: return {} # in the config - config.add_handler('hello', '/hello/:action', handler=Hello) + config.add_handler('hello', '/hello/{action}', handler=Hello) With this configuration, the url ``/hello/home`` will find a view configuration that results in calling the ``show_template`` method, then rendering the diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index b89d10c9f..e704463c7 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -42,8 +42,8 @@ configuration: # config is an instance of pyramid.configuration.Configurator - config.add_route('foobar', ':foo/:bar', view='myproject.views.foobar') - config.add_route('bazbuz', ':baz/:buz', view='myproject.views.bazbuz') + config.add_route('foobar', '{foo}/{bar}', view='myproject.views.foobar') + config.add_route('bazbuz', '{baz}/{buz}', view='myproject.views.bazbuz') Each :term:`route` typically corresponds to a single view callable, and when that route is matched during a request, the view callable @@ -185,7 +185,7 @@ of a route's pattern: .. code-block:: python :linenos: - config.add_route('home', ':foo/:bar/*traverse') + config.add_route('home', '{foo}/{bar}/*traverse') A ``*traverse`` token at the end of the pattern in a route's configuration implies a "remainder" *capture* value. When it is used, @@ -243,7 +243,7 @@ route configuration statement: .. code-block:: python :linenos: - config.add_route('home', ':foo/:bar/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') The ``factory`` above points at the function we've defined. It @@ -267,14 +267,14 @@ to do. When the route configuration named ``home`` above is matched during a request, the matchdict generated will be based on its pattern: -``:foo/:bar/*traverse``. The "capture value" implied by the +``{foo}/{bar}/*traverse``. The "capture value" implied by the ``*traverse`` element in the pattern will be used to traverse the graph in order to find a context, starting from the root object returned from the root factory. In the above example, the :term:`root` object found will be the instance named ``root`` in ``routes.py``. -If the URL that matched a route with the pattern ``:foo/:bar/*traverse``, +If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse``, is ``http://example.com/one/two/a/b/c``, the traversal path used against the root object will be ``a/b/c``. As a result, :app:`Pyramid` will attempt to traverse through the edges ``a``, @@ -296,7 +296,7 @@ invoked after a route matches: .. code-block:: python :linenos: - config.add_route('home', ':foo/:bar/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') config.add_view('mypackage.views.myview', route_name='home') @@ -326,7 +326,7 @@ when a hybrid route is matched: .. code-block:: python :linenos: - config.add_route('home', ':foo/:bar/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', factory='mypackage.routes.root_factory') config.add_view('mypackage.views.myview', name='home') config.add_view('mypackage.views.another_view', name='another', @@ -371,14 +371,14 @@ Here's a use of the ``traverse`` pattern in a call to .. code-block:: python :linenos: - config.add_route('abc', '/articles/:article/edit', - traverse='/articles/:article') + config.add_route('abc', '/articles/{article}/edit', + traverse='/articles/{article}') The syntax of the ``traverse`` argument is the same as it is for ``pattern``. -If, as above, the ``pattern`` provided is ``articles/:article/edit``, -and the ``traverse`` argument provided is ``/:article``, when a +If, as above, the ``pattern`` provided is ``articles/{article}/edit``, +and the ``traverse`` argument provided is ``/{article}``, when a request comes in that causes the route to match in such a way that the ``article`` match value is ``1`` (when the request URI is ``/articles/1/edit``), the traversal path will be generated as ``/1``. @@ -467,7 +467,7 @@ startup time. .. code-block:: python :linenos: - config.add_route('home', ':foo/:bar/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', view='myproject.views.home') config.add_view('myproject.views.another', route_name='home') @@ -479,7 +479,7 @@ supply a view attribute. For example, this ``add_route`` call: .. code-block:: python :linenos: - config.add_route('home', ':foo/:bar/*traverse', + config.add_route('home', '{foo}/{bar}/*traverse', view='myproject.views.home') Can also be spelled like so: @@ -487,7 +487,7 @@ Can also be spelled like so: .. code-block:: python :linenos: - config.add_route('home', ':foo/:bar/*traverse') + config.add_route('home', '{foo}/{bar}/*traverse') config.add_view('myproject.views.home', route_name='home') The two spellings are logically equivalent. In fact, the former is diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index 9bcc17e97..01fdd351e 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -32,27 +32,21 @@ entry point happens to be the ``app`` function within the file named #. *Lines 1-4*. Imports to support later code. -#. *Lines 9-11*. Get the database configuration string from the - ``development.ini`` file's ``[app:sqlalchemy]`` section. This will be a - URI (something like ``sqlite://``). +#. *Line 9*. Create a SQLAlchemy database engine from the ``sqlalchemy.`` + prefixed settings in the ``development.ini`` file's ``[app:tutorial]`` + section. This will be a URI (something like ``sqlite://``). -#. *Line 12*. Get the database echo setting from ``development.ini`` - file's ``[app:sqlalchemy]`` section. This will either be ``true`` - or ``false``. If ``true``, the application will print SQL to the - console as it is generated and run by SQLAlchemy. By default, it - is false. +#. *Line 10*. We initialize our SQL database using SQLAlchemy, passing + it the engine -#. Line *13*. We initialize our SQL database using SQLAlchemy, passing - it the db string and a variant of the db_echo value. - -#. *Line 14*. We construct a :term:`Configurator`. ``settings`` is +#. *Line 11*. We construct a :term:`Configurator`. ``settings`` is passed as a keyword argument with the dictionary values passed by PasteDeploy as the ``settings`` argument. This will be a dictionary of settings parsed by PasteDeploy, which contains deployment-related values such as ``reload_templates``, ``db_string``, etc. -#. *Line 15*. We call +#. *Line 12*. We call :meth:`pyramid.configuration.Configurator.add_static_view` with the arguments ``static`` (the name), and ``tutorial:static`` (the path). This registers a static resource view which will match any URL that starts with @@ -64,7 +58,7 @@ entry point happens to be the ``app`` function within the file named ``/static/foo``) will be used to compose a path to a static file resource, such as a CSS file. -#. *Lines 16-17*. Register a :term:`route configuration` via the +#. *Lines 13-14*. Register a :term:`route configuration` via the :meth:`pyramid.configuration.Configurator.add_route` method that will be used when the URL is ``/``. Since this route has an ``pattern`` equalling ``/`` it is the "default" route. The argument named ``view`` with the @@ -78,7 +72,7 @@ entry point happens to be the ``app`` function within the file named ``tutorial.views.my_view`` view returns a dictionary, a :term:`renderer` will use this template to create a response. -#. *Line 18*. We use the +#. *Line 15*. We use the :meth:`pyramid.configuration.Configurator.make_wsgi_app` method to return a :term:`WSGI` application. @@ -97,29 +91,28 @@ Here is the source for ``models.py``: :linenos: :language: py -#. *Lines 1-14*. Imports to support later code. +#. *Lines 1-13*. Imports to support later code. -#. *Line 16*. We set up a SQLAlchemy "DBSession" object here. We +#. *Line 15*. We set up a SQLAlchemy "DBSession" object here. We specify that we'd like to use the "ZopeTransactionExtension". This extension is an extension which allows us to use a *transaction manager* instead of controlling commits and aborts to database operations by hand. -#. *Line 17*. We create a declarative ``Base`` object to use as a +#. *Line 16*. We create a declarative ``Base`` object to use as a base class for our model. -#. *Lines 19-27*. A model class named ``MyModel``. It has an +#. *Lines 18-26*. A model class named ``MyModel``. It has an ``__init__`` that takes a two arguments (``name``, and ``value``). It stores these values as ``self.name`` and ``self.value`` within the ``__init__`` function itself. The ``MyModel`` class also has a ``__tablename__`` attribute. This informs SQLAlchemy which table to use to store the data representing instances of this class. -#. *Lines 29-34*. A function named ``populate`` which adds a single +#. *Lines 28-33*. A function named ``populate`` which adds a single model instance into our SQL storage and commits a transaction. -#. *Lines 36-44*. A function named ``initialize_sql`` which sets up - an actual SQL database and binds it to our SQLAlchemy DBSession - object. It also calls the ``populate`` function, to do initial - database population. +#. *Lines 35-42*. A function named ``initialize_sql`` which receives a SQL + database engine and binds it to our SQLAlchemy DBSession object. It also + calls the ``populate`` function, to do initial database population. diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index b87cd6a64..0f446bb4e 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -24,7 +24,7 @@ The request passed to every view that is called as the result of a route match has an attribute named ``matchdict`` that contains the elements placed into the URL by the ``pattern`` of a ``route`` statement. For instance, if a call to :meth:`pyramid.configuration.Configurator.add_route` in -``__init__.py`` had the pattern ``:one/:two``, and the URL at +``__init__.py`` had the pattern ``{one}/{two}``, and the URL at ``http://example.com/foo/bar`` was invoked, matching this pattern, the matchdict dictionary attached to the request passed to the view would have a ``one`` key with the value ``foo`` and a ``two`` key with the value ``bar``. @@ -277,16 +277,16 @@ the order they're found in the ``__init__.py`` file. to the view named ``view_wiki`` in our ``views.py`` file with the name ``view_wiki``. This is the :term:`default view` for the wiki. -#. Add a declaration which maps the pattern ``/:pagename`` to the view named +#. Add a declaration which maps the pattern ``/{pagename}`` to the view named ``view_page`` in our ``views.py`` file with the view name ``view_page``. This is the regular view for a page. #. Add a declaration which maps the pattern - ``/add_page/:pagename`` to the view named ``add_page`` in our + ``/add_page/{pagename}`` to the view named ``add_page`` in our ``views.py`` file with the name ``add_page``. This is the add view for a new page. -#. Add a declaration which maps the pattern ``/:pagename/edit_page`` to the +#. Add a declaration which maps the pattern ``/{pagename}/edit_page`` to the view named ``edit_page`` in our ``views.py`` file with the name ``edit_page``. This is the edit view for a page. diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 8269617f2..dbac349b9 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -2,7 +2,7 @@ from pyramid.configuration import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql from tutorial.security import groupfinder @@ -10,11 +10,8 @@ from tutorial.security import groupfinder def main(global_config, **settings): """ This function returns a WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) authn_policy = AuthTktAuthenticationPolicy( 'sosecret', callback=groupfinder) authz_policy = ACLAuthorizationPolicy() @@ -29,14 +26,14 @@ def main(global_config, **settings): view_renderer='tutorial:templates/login.pt') config.add_route('logout', '/logout', view='tutorial.login.logout') - config.add_route('view_page', '/:pagename', + config.add_route('view_page', '/{pagename}', view='tutorial.views.view_page', view_renderer='tutorial:templates/view.pt') - config.add_route('add_page', '/add_page/:pagename', + config.add_route('add_page', '/add_page/{pagename}', view='tutorial.views.add_page', view_renderer='tutorial:templates/edit.pt', view_permission='edit') - config.add_route('edit_page', '/:pagename/edit_page', + config.add_route('edit_page', '/{pagename}/edit_page', view='tutorial.views.edit_page', view_renderer='tutorial:templates/edit.pt', view_permission='edit') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py index 7580220b6..487299c4c 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models.py @@ -3,7 +3,6 @@ import transaction from pyramid.security import Allow from pyramid.security import Everyone -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Text @@ -30,8 +29,7 @@ class Page(Base): self.name = name self.data = data -def initialize_sql(db_string, echo=False): - engine = create_engine(db_string, echo=echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py index 65330ce17..c78899797 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py @@ -14,9 +14,9 @@ def _initTestingDB(): return DBSession def _registerRoutes(config): - config.add_route('view_page', ':pagename') - config.add_route('edit_page', ':pagename/edit_page') - config.add_route('add_page', 'add_page/:pagename') + config.add_route('view_page', '{pagename}') + config.add_route('edit_page', '{pagename}/edit_page') + config.add_route('add_page', 'add_page/{pagename}') class ViewWikiTests(unittest.TestCase): def setUp(self): @@ -28,7 +28,7 @@ class ViewWikiTests(unittest.TestCase): def test_it(self): from tutorial.views import view_wiki - self.config.add_route('view_page', ':pagename') + self.config.add_route('view_page', '{pagename}') request = testing.DummyRequest() response = view_wiki(request) self.assertEqual(response.location, 'http://example.com/FrontPage') diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 0ae61fccd..5236a538d 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -1,16 +1,13 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.my_view', diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py index a1726ebf4..9da906752 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py @@ -1,6 +1,5 @@ import transaction -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Unicode @@ -33,8 +32,7 @@ def populate(): session.flush() transaction.commit() -def initialize_sql(db_string, db_echo=False): - engine = create_engine(db_string, echo=db_echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py index 72f0c89d8..2db1bc5b6 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py @@ -3,8 +3,9 @@ from pyramid.configuration import Configurator from pyramid import testing def _initTestingDB(): + from sqlalchemy import create_engine from tutorial.models import initialize_sql - session = initialize_sql('sqlite://') + session = initialize_sql(create_engine('sqlite://')) return session class TestMyView(unittest.TestCase): diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index 6e291ffdf..e1baa2d64 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -1,16 +1,13 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql def main(global_config, **settings): """ This function returns a WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.my_view', diff --git a/docs/tutorials/wiki2/src/models/tutorial/models.py b/docs/tutorials/wiki2/src/models/tutorial/models.py index ec9d2b25c..23b8afab8 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models.py @@ -1,6 +1,5 @@ import transaction -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Text @@ -27,8 +26,7 @@ class Page(Base): self.name = name self.data = data -def initialize_sql(db_string, echo=False): - engine = create_engine(db_string, echo=echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index e1d0ab598..23b01a338 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/tutorial.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index 947ce9b93..91c299e24 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -1,26 +1,23 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from tutorial.models import initialize_sql def main(global_config, **settings): """ This function returns a WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) config.add_static_view('static', 'tutorial:static') config.add_route('home', '/', view='tutorial.views.view_wiki') - config.add_route('view_page', '/:pagename', + config.add_route('view_page', '/{pagename}', view='tutorial.views.view_page', view_renderer='tutorial:templates/view.pt') - config.add_route('add_page', '/add_page/:pagename', + config.add_route('add_page', '/add_page/{pagename}', view='tutorial.views.add_page', view_renderer='tutorial:templates/edit.pt') - config.add_route('edit_page', '/:pagename/edit_page', + config.add_route('edit_page', '/{pagename}/edit_page', view='tutorial.views.edit_page', view_renderer='tutorial:templates/edit.pt') return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/views/tutorial/models.py b/docs/tutorials/wiki2/src/views/tutorial/models.py index ec9d2b25c..23b8afab8 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models.py @@ -1,6 +1,5 @@ import transaction -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Text @@ -27,8 +26,7 @@ class Page(Base): self.name = name self.data = data -def initialize_sql(db_string, echo=False): - engine = create_engine(db_string, echo=echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py index 40336fca4..435e4b588 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py @@ -14,9 +14,9 @@ def _initTestingDB(): return DBSession def _registerRoutes(config): - config.add_route('view_page', ':pagename') - config.add_route('edit_page', ':pagename/edit_page') - config.add_route('add_page', 'add_page/:pagename') + config.add_route('view_page', '{pagename}') + config.add_route('edit_page', '{pagename}/edit_page') + config.add_route('add_page', 'add_page/{pagename}') class ViewWikiTests(unittest.TestCase): def setUp(self): @@ -28,7 +28,7 @@ class ViewWikiTests(unittest.TestCase): def test_it(self): from tutorial.views import view_wiki - self.config.add_route('view_page', ':pagename') + self.config.add_route('view_page', '{pagename}') request = testing.DummyRequest() response = view_wiki(request) self.assertEqual(response.location, 'http://example.com/FrontPage') diff --git a/docs/zcml/route.rst b/docs/zcml/route.rst index ed849e3c1..c3bec72df 100644 --- a/docs/zcml/route.rst +++ b/docs/zcml/route.rst @@ -10,7 +10,7 @@ Attributes ~~~~~~~~~~ ``pattern`` - The pattern of the route e.g. ``ideas/:idea``. This attribute is + The pattern of the route e.g. ``ideas/{idea}``. This attribute is required. See :ref:`route_pattern_syntax` for information about the syntax of route patterns. @@ -51,9 +51,9 @@ Attributes The syntax of the ``traverse`` argument is the same as it is for ``pattern``. For example, if the ``pattern`` provided to the - ``route`` directive is ``articles/:article/edit``, and the + ``route`` directive is ``articles/{article}/edit``, and the ``traverse`` argument provided to the ``route`` directive is - ``/:article``, when a request comes in that causes the route to + ``/{article}``, when a request comes in that causes the route to match in such a way that the ``article`` match value is '1' (when the request URI is ``/articles/1/edit``), the traversal path will be generated as ``/1``. This means that the root object's diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 63d09efe3..6ebb56ec3 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -296,12 +296,17 @@ class Configurator(object): attr=None, renderer=None, wrapper_viewname=None, viewname=None, accept=None, order=MAX_ORDER, phash=DEFAULT_PHASH): + if renderer is None: # use default renderer if one exists + default_renderer_factory = self.registry.queryUtility( + IRendererFactory) + if default_renderer_factory is not None: + renderer = {'name':None, 'package':self.package} view = self.maybe_dotted(view) authn_policy = self.registry.queryUtility(IAuthenticationPolicy) authz_policy = self.registry.queryUtility(IAuthorizationPolicy) settings = self.registry.settings logger = self.registry.queryUtility(IDebugLogger) - mapped_view = _map_view(view, attr, renderer, self.registry) + mapped_view = _map_view(view, self.registry, attr, renderer) owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) secured_view = _secure_view(owrapped_view, permission, authn_policy, authz_policy) @@ -699,6 +704,8 @@ class Configurator(object): Any extra keyword arguments are passed along to ``add_route``. + See :ref:`handlers_chapter` for more explanatory documentation. + This method returns the result of add_route.""" handler = self.maybe_dotted(handler) @@ -1260,9 +1267,9 @@ class Configurator(object): The syntax of the ``traverse`` argument is the same as it is for ``pattern``. For example, if the ``pattern`` provided to - ``add_route`` is ``articles/:article/edit``, and the + ``add_route`` is ``articles/{article}/edit``, and the ``traverse`` argument provided to ``add_route`` is - ``/:article``, when a request comes in that causes the route + ``/{article}``, when a request comes in that causes the route to match in such a way that the ``article`` match value is '1' (when the request URI is ``/articles/1/edit``), the traversal path will be generated as ``/1``. This means that @@ -1306,7 +1313,7 @@ class Configurator(object): pattern - The pattern of the route e.g. ``ideas/:idea``. This + The pattern of the route e.g. ``ideas/{idea}``. This argument is required. See :ref:`route_path_pattern_syntax` for information about the syntax of route patterns. If the pattern doesn't match the current URL, route matching @@ -1591,7 +1598,9 @@ class Configurator(object): Add a :app:`Pyramid` :term:`renderer` factory to the current configuration state. - The ``name`` argument is the renderer name. + The ``name`` argument is the renderer name. Use ``None`` to + represent the default renderer (a renderer which will be used for all + views unless they name another renderer specifically). The ``factory`` argument is Python reference to an implementation of a :term:`renderer` factory or a @@ -1605,6 +1614,11 @@ class Configurator(object): to use this method. """ factory = self.maybe_dotted(factory) + # if name is None or the empty string, we're trying to register + # a default renderer, but registerUtility is too dumb to accept None + # as a name + if not name: + name = '' self.registry.registerUtility( factory, IRendererFactory, name=name, info=_info) @@ -2426,7 +2440,7 @@ def is_response(ob): return True return False -def _map_view(view, attr=None, renderer=None, registry=None): +def _map_view(view, registry, attr=None, renderer=None): wrapped_view = view helper = None @@ -2844,14 +2858,10 @@ class DottedNameResolver(object): def maybe_resolve(self, dotted): if isinstance(dotted, basestring): - try: - if ':' in dotted: - return self._pkg_resources_style(dotted) - else: - return self._zope_dottedname_style(dotted) - except ImportError: - raise ConfigurationError( - 'The dotted name %r cannot be imported' % (dotted,)) + if ':' in dotted: + return self._pkg_resources_style(dotted) + else: + return self._zope_dottedname_style(dotted) return dotted diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 9b895e020..f021ba60b 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -100,6 +100,26 @@ class IBeforeRender(Interface): """ Return the value for key ``k`` from the renderer globals dictionary, or the default if no such value exists.""" +class IRenderer(Interface): + def __call__(value, system): + """ Call a the renderer implementation with the result of the + view (``value``) passed in and return a result (a string or + unicode object useful as a response body). Values computed by + the system are passed by the system in the ``system`` + parameter, which is a dictionary. Keys in the dictionary + include: ``view`` (the view callable that returned the value), + ``renderer_name`` (the template name or simple name of the + renderer), ``context`` (the context object passed to the + view), and ``request`` (the request object passed to the + view).""" + +class ITemplateRenderer(IRenderer): + def implementation(): + """ Return the object that the underlying templating system + uses to render the template; it is typically a callable that + accepts arbitrary keyword arguments and returns a string or + unicode object """ + # internal interfaces class IRequest(Interface): @@ -233,19 +253,6 @@ class ITraverser(Interface): ITraverserFactory = ITraverser # b / c for 1.0 code -class IRenderer(Interface): - def __call__(value, system): - """ Call a the renderer implementation with the result of the - view (``value``) passed in and return a result (a string or - unicode object useful as a response body). Values computed by - the system are passed by the system in the ``system`` - parameter, which is a dictionary. Keys in the dictionary - include: ``view`` (the view callable that returned the value), - ``renderer_name`` (the template name or simple name of the - renderer), ``context`` (the context object passed to the - view), and ``request`` (the request object passed to the - view).""" - class IRendererFactory(Interface): def __call__(name): """ Return an object that implements ``IRenderer`` """ @@ -259,13 +266,6 @@ class IRendererGlobalsFactory(Interface): ``renderer_name``, which will be the name of the renderer in use.""" -class ITemplateRenderer(IRenderer): - def implementation(): - """ Return the object that the underlying templating system - uses to render the template; it is typically a callable that - accepts arbitrary keyword arguments and returns a string or - unicode object """ - class IViewPermission(Interface): def __call__(context, request): """ Return True if the permission allows, return False if it denies. """ diff --git a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl index c20e7dd15..f6591ab32 100755 --- a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl @@ -1,16 +1,13 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from {{package}}.models import appmaker def main(global_config, **settings): """ This function returns a WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - get_root = appmaker(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + get_root = appmaker(engine) config = Configurator(settings=settings, root_factory=get_root) config.add_static_view('static', '{{package}}:static') config.add_view('{{package}}.views.view_root', diff --git a/pyramid/paster_templates/alchemy/+package+/models.py b/pyramid/paster_templates/alchemy/+package+/models.py index 336613cf9..1134cce07 100755 --- a/pyramid/paster_templates/alchemy/+package+/models.py +++ b/pyramid/paster_templates/alchemy/+package+/models.py @@ -73,8 +73,7 @@ def populate(): session.flush() transaction.commit() -def initialize_sql(db_string, db_echo=False): - engine = create_engine(db_string, echo=db_echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) @@ -83,6 +82,6 @@ def initialize_sql(db_string, db_echo=False): except IntegrityError: pass -def appmaker(db_string, db_echo=False): - initialize_sql(db_string, db_echo) +def appmaker(engine): + initialize_sql(engine) return default_get_root diff --git a/pyramid/paster_templates/alchemy/development.ini_tmpl b/pyramid/paster_templates/alchemy/development.ini_tmpl index de4605e33..4f4b98ac0 100644 --- a/pyramid/paster_templates/alchemy/development.ini_tmpl +++ b/pyramid/paster_templates/alchemy/development.ini_tmpl @@ -5,8 +5,7 @@ debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/{{package}}.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [pipeline:main] pipeline = @@ -18,3 +17,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl index dd6edd3cf..9f3793538 100644 --- a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl @@ -8,7 +8,7 @@ def main(global_config, **settings): session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) config.add_static_view('static', '{{package}}:static/') - config.add_handler('action', '/:action', + config.add_handler('action', '/{action}', '{{package}}.handlers.hello:HelloHandler') config.add_handler('home', '/', '{{package}}.handlers.hello:HelloHandler', action='index') diff --git a/pyramid/paster_templates/pylons_basic/development.ini_tmpl b/pyramid/paster_templates/pylons_basic/development.ini_tmpl index 569256739..b14ecc7e2 100644 --- a/pyramid/paster_templates/pylons_basic/development.ini_tmpl +++ b/pyramid/paster_templates/pylons_basic/development.ini_tmpl @@ -13,10 +13,37 @@ session.key = {{project}} session.secret = {{random_string}} [pipeline:main] -pipeline = egg:WebError#evalerror - {{project}} +pipeline = + egg:WebError#evalerror + {{project}} [server:main] use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl index 57371eec9..5e284ec88 100644 --- a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl @@ -8,7 +8,7 @@ def main(global_config, **settings): session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) config.add_static_view('static', '{{package}}:static/') - config.add_handler('action', '/:action', '{{package}}.handlers:MyHandler') + config.add_handler('action', '/{action}', '{{package}}.handlers:MyHandler') config.add_handler('home', '/', '{{package}}.handlers:MyHandler', action='index') config.add_subscriber('{{package}}.subscribers.add_renderer_globals', diff --git a/pyramid/paster_templates/pylons_minimal/development.ini_tmpl b/pyramid/paster_templates/pylons_minimal/development.ini_tmpl index 569256739..d31cd1ca2 100644 --- a/pyramid/paster_templates/pylons_minimal/development.ini_tmpl +++ b/pyramid/paster_templates/pylons_minimal/development.ini_tmpl @@ -20,3 +20,29 @@ pipeline = egg:WebError#evalerror use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl index 0a4313976..b67a2c35b 100644 --- a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl @@ -1,22 +1,21 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool from pyramid_beaker import session_factory_from_settings +from sqlalchemy import engine_from_config + +from {{package}}.models import initialize_sql + def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - from {{package}}.models import initialize_sql - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application " - "configuration.") - initialize_sql(db_string, asbool(settings.get('db_echo'))) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) config.add_static_view('static', '{{package}}:static/') - config.add_handler('main', '/:action', '{{package}}.handlers:MyHandler') + config.add_handler('main', '/{action}', '{{package}}.handlers:MyHandler') config.add_handler('home', '/', '{{package}}.handlers:MyHandler', action='index') config.add_subscriber('{{package}}.subscribers.add_renderer_globals', diff --git a/pyramid/paster_templates/pylons_sqla/+package+/models.py b/pyramid/paster_templates/pylons_sqla/+package+/models.py index 092166902..6418f0c8b 100644 --- a/pyramid/paster_templates/pylons_sqla/+package+/models.py +++ b/pyramid/paster_templates/pylons_sqla/+package+/models.py @@ -36,8 +36,7 @@ def populate(): DBSession.flush() transaction.commit() -def initialize_sql(db_string, db_echo=False): - engine = create_engine(db_string, echo=db_echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl b/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl index 099df47bd..b55760b37 100644 --- a/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl @@ -3,8 +3,9 @@ import unittest class MyHandlerTests(unittest.TestCase): def setUp(self): from pyramid.configuration import Configurator + from sqlalchemy import create_engine from {{package}}.models import initialize_sql - self.session = initialize_sql('sqlite://') + self.session = initialize_sql(create_engine('sqlite://')) self.config = Configurator() self.config.begin() diff --git a/pyramid/paster_templates/pylons_sqla/development.ini_tmpl b/pyramid/paster_templates/pylons_sqla/development.ini_tmpl index 936d41b29..1cc1a42e4 100644 --- a/pyramid/paster_templates/pylons_sqla/development.ini_tmpl +++ b/pyramid/paster_templates/pylons_sqla/development.ini_tmpl @@ -1,13 +1,12 @@ [app:{{project}}] use = egg:{{project}} reload_templates = true -mako.directories = {{package}}:templates debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/tutorial.db -db_echo = true +mako.directories = {{package}}:templates +sqlalchemy.url = sqlite:///%(here)s/{{project}}.db session.type = file session.data_dir = %(here)s/data/sessions/data session.lock_dir = %(here)s/data/sessions/lock @@ -24,3 +23,37 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl index e68e0ed66..5c326caa8 100644 --- a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl @@ -1,16 +1,13 @@ from pyramid.configuration import Configurator -from pyramid.settings import asbool +from sqlalchemy import engine_from_config from {{package}}.models import initialize_sql def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - db_string = settings.get('db_string') - if db_string is None: - raise ValueError("No 'db_string' value in application configuration.") - db_echo = settings.get('db_echo', 'false') - initialize_sql(db_string, asbool(db_echo)) + engine = engine_from_config(settings, 'sqlalchemy.') + initialize_sql(engine) config = Configurator(settings=settings) config.add_static_view('static', '{{package}}:static') config.add_route('home', '/', view='{{package}}.views.my_view', diff --git a/pyramid/paster_templates/routesalchemy/+package+/models.py b/pyramid/paster_templates/routesalchemy/+package+/models.py index a1726ebf4..9da906752 100644 --- a/pyramid/paster_templates/routesalchemy/+package+/models.py +++ b/pyramid/paster_templates/routesalchemy/+package+/models.py @@ -1,6 +1,5 @@ import transaction -from sqlalchemy import create_engine from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Unicode @@ -33,8 +32,7 @@ def populate(): session.flush() transaction.commit() -def initialize_sql(db_string, db_echo=False): - engine = create_engine(db_string, echo=db_echo) +def initialize_sql(engine): DBSession.configure(bind=engine) Base.metadata.bind = engine Base.metadata.create_all(engine) diff --git a/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl index de75e5df0..cfab37670 100644 --- a/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl @@ -3,8 +3,9 @@ from pyramid.configuration import Configurator from pyramid import testing def _initTestingDB(): + from sqlalchemy import create_engine from {{package}}.models import initialize_sql - session = initialize_sql('sqlite://') + session = initialize_sql(create_engine('sqlite://')) return session class TestMyView(unittest.TestCase): @@ -21,4 +22,4 @@ class TestMyView(unittest.TestCase): request = testing.DummyRequest() info = my_view(request) self.assertEqual(info['root'].name, 'root') - self.assertEqual(info['project'], '{{package}}') + self.assertEqual(info['project'], '{{project}}') diff --git a/pyramid/paster_templates/routesalchemy/development.ini_tmpl b/pyramid/paster_templates/routesalchemy/development.ini_tmpl index de4605e33..a1cbff75f 100644 --- a/pyramid/paster_templates/routesalchemy/development.ini_tmpl +++ b/pyramid/paster_templates/routesalchemy/development.ini_tmpl @@ -1,20 +1,53 @@ -[app:{{package}}] -use = egg:{{package}} +[app:{{project}}] +use = egg:{{project}} reload_templates = true debug_authorization = false debug_notfound = false debug_templates = true default_locale_name = en -db_string = sqlite:///%(here)s/{{package}}.db -db_echo = false +sqlalchemy.url = sqlite:///%(here)s/{{project}}.db [pipeline:main] pipeline = egg:WebError#evalerror egg:repoze.tm2#tm - {{package}} + {{project}} [server:main] use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/paster_templates/starter/development.ini_tmpl b/pyramid/paster_templates/starter/development.ini_tmpl index 5031742db..328422ef8 100644 --- a/pyramid/paster_templates/starter/development.ini_tmpl +++ b/pyramid/paster_templates/starter/development.ini_tmpl @@ -15,3 +15,29 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/paster_templates/starter_zcml/development.ini_tmpl b/pyramid/paster_templates/starter_zcml/development.ini_tmpl index 5031742db..328422ef8 100644 --- a/pyramid/paster_templates/starter_zcml/development.ini_tmpl +++ b/pyramid/paster_templates/starter_zcml/development.ini_tmpl @@ -15,3 +15,29 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/paster_templates/zodb/development.ini_tmpl b/pyramid/paster_templates/zodb/development.ini_tmpl index 85a0681b7..671c8b069 100644 --- a/pyramid/paster_templates/zodb/development.ini_tmpl +++ b/pyramid/paster_templates/zodb/development.ini_tmpl @@ -18,3 +18,29 @@ pipeline = use = egg:Paste#http host = 0.0.0.0 port = 6543 + +# Begin logging configuration + +[loggers] +keys = root + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s + +# End logging configuration diff --git a/pyramid/testing.py b/pyramid/testing.py index 4acede879..cc0ee5c5b 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -41,7 +41,7 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True): argument. The authentication policy will return the userid identifier implied by the ``userid`` argument and the group ids implied by the ``groupids`` argument when the - :func:`pyramid.security.authenticated_userid` or + :func:`pyramid.security.authenticated_userid` or :func:`pyramid.security.effective_principals` APIs are used. This function is most useful when testing code that uses the APIs @@ -211,7 +211,7 @@ def registerAdapter(impl, for_=Interface, provides=Interface, name=''): The ``name`` argument is the empty string by default; it implies the name under which the adapter is registered. - + See `The ZCA book <http://www.muthukadan.net/docs/zca.html>`_ for more information about ZCA adapters. @@ -282,7 +282,7 @@ def registerSettings(dictarg=None, **kw): registerSettings({'external_uri':'http://example.com'}) Or a set of key/value pairs:: - + registerSettings(external_uri='http://example.com') Use of this function is required when you need to test code that calls @@ -361,7 +361,7 @@ class DummyTemplateRenderer(object): def implementation(self): return self._implementation - + def __call__(self, kw, system=None): if system: self._received.update(system) @@ -393,7 +393,7 @@ class DummyTemplateRenderer(object): raise AssertionError( 'A value for key "%s" was not passed to the renderer' % k) - + if myval != v: raise AssertionError( '\nasserted value for %s: %r\nactual value: %r' % ( @@ -431,7 +431,7 @@ class DummyModel: val.__name__ = name val.__parent__ = self self.subs[name] = val - + def __getitem__(self, name): """ Return a named subobject (see ``__setitem__``)""" ob = self.subs[name] @@ -465,7 +465,7 @@ class DummyModel: def __contains__(self, name): return name in self.subs - + def clone(self, __name__=_marker, __parent__=_marker, **kw): """ Create a clone of the model object. If ``__name__`` or ``__parent__`` arguments are passed, use these values to @@ -485,7 +485,7 @@ class DummyModel: class DummyRequest(object): """ A dummy request object (imitates a :term:`request` object). - + The ``params``, ``environ``, ``headers``, ``path``, and ``cookies`` arguments correspond to their :term`WebOb` equivalents. @@ -503,6 +503,7 @@ class DummyRequest(object): application_url = 'http://example.com' host = 'example.com:80' content_length = 0 + query_string = '' response_callbacks = () def __init__(self, params=None, environ=None, headers=None, path='/', cookies=None, post=None, **kw): @@ -721,8 +722,8 @@ class DummyRendererFactory(object): raise KeyError('No testing renderer registered for %r' % spec) return renderer - - + + class MockTemplate(object): def __init__(self, response): self._received = {} @@ -750,7 +751,7 @@ def skip_on(*platforms): return decorator skip_on.os_name = os.name # for testing try: # pragma: no cover - import __pypy__ + import __pypy__ skip_on.pypy = True except ImportError: skip_on.pypy = False diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py index a8ea63f54..5113d1f4e 100644 --- a/pyramid/tests/test_configuration.py +++ b/pyramid/tests/test_configuration.py @@ -216,9 +216,8 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(result, pyramid.tests) def test_maybe_dotted_string_fail(self): - from pyramid.configuration import ConfigurationError config = self._makeOne() - self.assertRaises(ConfigurationError, + self.assertRaises(ImportError, config.maybe_dotted, 'cant.be.found') def test_maybe_dotted_notstring_success(self): @@ -2559,6 +2558,36 @@ class ConfiguratorTests(unittest.TestCase): self.failIf(result is view) self.assertEqual(result(None, None).body, 'moo') + def test_derive_view_with_default_renderer_no_explicit_renderer(self): + def view(request): + return 'OK' + config = self._makeOne() + class moo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'moo' + config.add_renderer(None, moo) + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'moo') + + def test_derive_view_with_default_renderer_with_explicit_renderer(self): + def view(request): + return 'OK' + config = self._makeOne() + class moo(object): pass + class foo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'foo' + config.add_renderer(None, moo) + config.add_renderer('foo', foo) + result = config.derive_view(view, renderer='foo') + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'foo') + def test_derive_view_class_without_attr(self): class View(object): def __init__(self, request): @@ -3242,9 +3271,9 @@ class Test__map_view(unittest.TestCase): request.registry = self.registry return request - def _callFUT(self, *arg, **kw): + def _callFUT(self, view, **kw): from pyramid.configuration import _map_view - return _map_view(*arg, **kw) + return _map_view(view, self.registry, **kw) def test__map_view_as_function_context_and_request(self): def view(context, request): @@ -3543,8 +3572,7 @@ class Test__map_view(unittest.TestCase): def view(context, request): return {'a':'1'} info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, renderer=info, - registry=self.registry) + result = self._callFUT(view, renderer=info) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -4397,9 +4425,7 @@ class TestDottedNameResolver(unittest.TestCase): def test_resolve_missing_raises(self): typ = self._makeOne() - e = self.config_exc(typ.resolve, 'cant.be.found') - self.assertEqual(e.args[0], - "The dotted name 'cant.be.found' cannot be imported") + self.assertRaises(ImportError, typ.resolve, 'cant.be.found') def test_ctor_string_module_resolveable(self): import pyramid.tests diff --git a/pyramid/url.py b/pyramid/url.py index 76d95689c..2e73e9cf5 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -32,7 +32,7 @@ def route_url(route_name, request, *elements, **kw): enough arguments, for example). For example, if you've defined a route named "foobar" with the path - ``:foo/:bar/*traverse``:: + ``:foo/{bar}/*traverse``:: route_url('foobar', request, foo='1') => <KeyError exception> route_url('foobar', request, foo='1', bar='2') => <KeyError exception> @@ -166,7 +166,7 @@ def route_path(route_name, request, *elements, **kw): and anchor data are present in the returned string. For example, if you've defined a route named 'foobar' with the path - ``/:foo/:bar``, this call to ``route_path``:: + ``/{foo}/{bar}``, this call to ``route_path``:: route_path('foobar', request, foo='1', bar='2') |
