summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCasey Duncan <casey.duncan@gmail.com>2010-11-27 22:04:31 -0700
committerCasey Duncan <casey.duncan@gmail.com>2010-11-27 22:04:31 -0700
commitcc0f6a2cf76e5cee3678446b0e226367dc96d158 (patch)
tree2e9ce26cd0ab997d5ed18a0976dd2cda30057d57
parentef921f9724c9810f0147a10a5d5b9112bc2e9ce1 (diff)
parentb5f8a5ee066c39fb6be2f677551fd4211670b2de (diff)
downloadpyramid-cc0f6a2cf76e5cee3678446b0e226367dc96d158.tar.gz
pyramid-cc0f6a2cf76e5cee3678446b0e226367dc96d158.tar.bz2
pyramid-cc0f6a2cf76e5cee3678446b0e226367dc96d158.zip
Merge https://github.com/Pylons/pyramid
-rw-r--r--CHANGES.txt28
-rw-r--r--TODO.txt4
-rw-r--r--docs/_static/pylons.png (renamed from docs/_static/pyramid.png)bin4063 -> 4063 bytes
-rw-r--r--docs/_static/pylons_small.pngbin0 -> 4063 bytes
-rw-r--r--docs/conf.py9
-rw-r--r--docs/narr/MyProject/myproject/__init__.py1
-rw-r--r--docs/narr/MyProject/myproject/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/narr/MyProject/myproject/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/narr/MyProject/myproject/static/ie6.css8
-rw-r--r--docs/narr/MyProject/myproject/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--docs/narr/MyProject/myproject/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/narr/MyProject/myproject/static/pylons.css47
-rw-r--r--docs/narr/MyProject/myproject/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/narr/MyProject/myproject/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/narr/MyProject/myproject/templates/mytemplate.pt25
-rw-r--r--docs/narr/environment.rst18
-rw-r--r--docs/narr/handlers.rst4
-rw-r--r--docs/narr/project.pngbin81057 -> 84679 bytes
-rw-r--r--docs/narr/project.rst99
-rw-r--r--docs/narr/startup.rst6
-rw-r--r--docs/narr/urldispatch.rst3
-rw-r--r--docs/tutorials/modwsgi/index.rst23
-rw-r--r--pyramid/compat/__init__.py7
-rw-r--r--pyramid/configuration.py141
-rw-r--r--pyramid/mako_templating.py28
-rwxr-xr-xpyramid/paster_templates/alchemy/+package+/models.py2
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/models.py2
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/models.py2
-rw-r--r--pyramid/paster_templates/starter/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/starter/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/pylons.css47
-rw-r--r--pyramid/paster_templates/starter/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/templates/mytemplate.pt25
-rw-r--r--pyramid/settings.py2
-rw-r--r--pyramid/tests/test_configuration.py175
-rw-r--r--pyramid/tests/test_mako_templating.py164
-rw-r--r--pyramid/tests/test_util.py177
-rw-r--r--pyramid/util.py144
-rw-r--r--setup.py2
43 files changed, 752 insertions, 449 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 2ea0ac448..6f8ec355b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -10,8 +10,24 @@ Features
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.
+- ``pyramid.testing.DummyRequest`` now has a class variable,
+ ``query_string``, which defaults to the empty string.
+
+- Add support for json on GAE by catching NotImplementedError and importing
+ simplejson from django.utils.
+
+- The Mako renderer now accepts a resource specification for
+ ``mako.module_directory``.
+
+- New boolean Mako settings variable ``mako.strict_undefined``. See `Mako
+ Context Variables
+ <http://www.makotemplates.org/docs/runtime.html#context-variables>`_ for
+ its meaning.
+
+Dependencies
+------------
+
+- Depend on Mako 0.3.6+ (we now require the ``strict_undefined`` feature).
Bug Fixes
---------
@@ -22,6 +38,12 @@ Bug Fixes
- Make default renderer work (renderer factory registered with no name, which
is active for every view unless the view names a specific renderer).
+- The Mako renderer did not properly turn the ``mako.imports``,
+ ``mako.default_filters``, and ``mako.imports`` settings into lists.
+
+- The Mako renderer did not properly convert the ``mako.error_handler``
+ setting from a dotted name to a callable.
+
Documentation
-------------
@@ -36,6 +58,8 @@ Documentation
chapter (has ``implementation()`` method, required to be used when getting
at Chameleon macros).
+- Add a "Modifying Package Structure" section to the project narrative
+ documentation chapter (explain turning a module into a package).
1.0a4 (2010-11-21)
==================
diff --git a/TODO.txt b/TODO.txt
index 4319e3cca..0033eb3b3 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -20,8 +20,6 @@ Should-Have
- Add docs for httpexceptions module for each webob.exc class that inherits
from WSGIHTTPException.
-- Create a ``docs`` directory for each paster template.
-
- Remove "BFG" from Pyramid-specific environ variables.
- translationdir ZCML directive use of ``path_spec`` should maybe die.
@@ -108,3 +106,5 @@ Nice-to-Have
- Add functionality that mocks the behavior of ``repoze.browserid``.
+- One way to split up views chapter: views with renderers / views without
+ renderers.
diff --git a/docs/_static/pyramid.png b/docs/_static/pylons.png
index 53f83e4d0..53f83e4d0 100644
--- a/docs/_static/pyramid.png
+++ b/docs/_static/pylons.png
Binary files differ
diff --git a/docs/_static/pylons_small.png b/docs/_static/pylons_small.png
new file mode 100644
index 000000000..53f83e4d0
--- /dev/null
+++ b/docs/_static/pylons_small.png
Binary files differ
diff --git a/docs/conf.py b/docs/conf.py
index e520c9d82..3de2f7ac4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -110,6 +110,9 @@ add_module_names = False
# The name of the Pygments (syntax highlighting) style to use.
#pygments_style = book and 'bw' or 'tango'
+# The default language to highlight source code in.
+highlight_language = 'guess'
+
# Options for HTML output
# -----------------------
@@ -128,11 +131,11 @@ html_theme = 'pylons'
html_title = 'The Pyramid Web Application Development Framework v%s' % release
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+html_short_title = 'Home'
# The name of an image file (within the static path) to place at the top of
# the sidebar.
-#html_logo = '_static/pylons.png'
+html_logo = '_static/pyramid.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
@@ -201,7 +204,7 @@ latex_documents = [
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-latex_logo = '_static/pyramid.png'
+latex_logo = '_static/pylons_small.png'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
diff --git a/docs/narr/MyProject/myproject/__init__.py b/docs/narr/MyProject/myproject/__init__.py
index 580dfe546..936404c82 100644
--- a/docs/narr/MyProject/myproject/__init__.py
+++ b/docs/narr/MyProject/myproject/__init__.py
@@ -10,3 +10,4 @@ def main(global_config, **settings):
renderer='myproject:templates/mytemplate.pt')
config.add_static_view('static', 'myproject:static')
return config.make_wsgi_app()
+
diff --git a/docs/narr/MyProject/myproject/static/footerbg.png b/docs/narr/MyProject/myproject/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/narr/MyProject/myproject/static/footerbg.png
Binary files differ
diff --git a/docs/narr/MyProject/myproject/static/headerbg.png b/docs/narr/MyProject/myproject/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/narr/MyProject/myproject/static/headerbg.png
Binary files differ
diff --git a/docs/narr/MyProject/myproject/static/ie6.css b/docs/narr/MyProject/myproject/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/narr/MyProject/myproject/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/narr/MyProject/myproject/static/logo.png b/docs/narr/MyProject/myproject/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/docs/narr/MyProject/myproject/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/narr/MyProject/myproject/static/middlebg.png b/docs/narr/MyProject/myproject/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/narr/MyProject/myproject/static/middlebg.png
Binary files differ
diff --git a/docs/narr/MyProject/myproject/static/pylons.css b/docs/narr/MyProject/myproject/static/pylons.css
index c153be07f..33b21ac1a 100644
--- a/docs/narr/MyProject/myproject/static/pylons.css
+++ b/docs/narr/MyProject/myproject/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,25 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top{color:#000000;height:230px;
+background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +58,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/narr/MyProject/myproject/static/pyramid.png b/docs/narr/MyProject/myproject/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/narr/MyProject/myproject/static/pyramid.png
Binary files differ
diff --git a/docs/narr/MyProject/myproject/static/transparent.gif b/docs/narr/MyProject/myproject/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/narr/MyProject/myproject/static/transparent.gif
Binary files differ
diff --git a/docs/narr/MyProject/myproject/templates/mytemplate.pt b/docs/narr/MyProject/myproject/templates/mytemplate.pt
index 6ad23d44f..02fc00eeb 100644
--- a/docs/narr/MyProject/myproject/templates/mytemplate.pt
+++ b/docs/narr/MyProject/myproject/templates/mytemplate.pt
@@ -7,21 +7,21 @@
<meta name="description" content="pyramid web application" />
<link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
<link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" rel="stylesheet" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.application_url}/static/ie6.css" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.application_url}/static/pyramid.png" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst
index ecf85e464..c3fe401ec 100644
--- a/docs/narr/environment.rst
+++ b/docs/narr/environment.rst
@@ -249,6 +249,24 @@ will be placed into the module level preamble of all generated Python modules.
| |
+-----------------------------+
+
+Mako Strict Undefined
++++++++++++++++++++++
+
+``true`` or ``false``, representing the "strict undefined" behavior of Mako
+(see `Mako Context Variables
+<http://www.makotemplates.org/docs/runtime.html#context-variables>`_). By
+default, this is ``false``.
+
++-----------------------------+
+| Config File Setting Name |
++=============================+
+| ``mako.strict_undefined`` |
+| |
+| |
+| |
++-----------------------------+
+
Examples
--------
diff --git a/docs/narr/handlers.rst b/docs/narr/handlers.rst
index 022f27115..d82f42bdb 100644
--- a/docs/narr/handlers.rst
+++ b/docs/narr/handlers.rst
@@ -168,8 +168,8 @@ information on the handler method which is used by
configuration.
All keyword arguments are recorded, and passed to
-:meth:`!pyramid.configuration.Configurator.add_view`. Any valid keyword
-arguments for :meth:`!pyramid.configuration.Configurator.add_view` can thus be
+:meth:`~pyramid.configuration.Configurator.add_view`. Any valid keyword
+arguments for :meth:`~pyramid.configuration.Configurator.add_view` can thus be
used with the :class:`~pyramid.view.action` decorator to further restrict when
the view will be called.
diff --git a/docs/narr/project.png b/docs/narr/project.png
index 78db4dbbe..da5bc870b 100644
--- a/docs/narr/project.png
+++ b/docs/narr/project.png
Binary files differ
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 225ced59c..c676917b4 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -268,7 +268,7 @@ points to *your application* as opposed to any other section within the
``.ini`` file. For example, if your application ``.ini`` file might have a
``[app:MyProject]`` section that looks like so:
-.. code-block:: ini
+.. code-block:: guess
:linenos:
[app:MyProject]
@@ -282,7 +282,7 @@ points to *your application* as opposed to any other section within the
If so, you can use the following command to invoke a debug shell using
the name ``MyProject`` as a section name:
-.. code-block:: text
+.. code-block:: text
[chrism@vitaminf shellenv]$ ../bin/paster pshell development.ini MyProject
Python 2.4.5 (#1, Aug 29 2008, 12:27:37)
@@ -299,7 +299,7 @@ happen, even if you have IPython installed, you can pass the
``--disable-ipython`` flag to the ``pshell`` command to use a standard
Python interpreter shell unconditionally.
-.. code-block:: text
+.. code-block:: text
[chrism@vitaminf shellenv]$ ../bin/paster pshell --disable-ipython \
development.ini MyProject
@@ -314,7 +314,7 @@ Python interpreter shell unconditionally.
than an ``app``. For example, if you have the following ``.ini`` file
content:
- .. code-block:: ini
+ .. code-block:: guess
:linenos:
[app:MyProject]
@@ -326,13 +326,15 @@ Python interpreter shell unconditionally.
default_locale_name = en
[pipeline:main]
- pipeline = egg:WebError#evalerror
- myapp
+ pipeline =
+ egg:WebError#evalerror
+ MyProject
- If you use ``main`` as the section name argument instead of ``myapp``
- against the above ``.ini`` file, an error will occur. Use the most
- specific reference to your application within the ``.ini`` file possible
- as the section name argument.
+ Use ``MyProject`` instead of ``main`` as the section name argument to
+ ``pshell`` against the above ``.ini`` file (e.g. ``paster pshell
+ development.ini MyProject``). If you use ``main`` instead, an error will
+ occur. Use the most specific reference to your application within the
+ ``.ini`` file possible as the section name argument.
Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows).
@@ -442,7 +444,9 @@ name except for case). All :app:`Pyramid` ``paster`` -generated projects
share a similar structure.
The ``MyProject`` project we've generated has the following directory
-structure::
+structure:
+
+.. code-block:: text
MyProject/
|-- CHANGES.txt
@@ -503,6 +507,7 @@ serve``, as well as the deployment settings provided to that application.
The generated ``development.ini`` file looks like so:
.. literalinclude:: MyProject/development.ini
+ :language: guess
:linenos:
This file contains several "sections" including ``[app:MyProject]``,
@@ -628,6 +633,7 @@ distributing your application.
Our generated ``setup.py`` looks like this:
.. literalinclude:: MyProject/setup.py
+ :language: python
:linenos:
The ``setup.py`` file calls the setuptools ``setup`` function, which
@@ -665,7 +671,7 @@ file when distributing your application to other people, or when
versioning your application for your own use. For fun, you can try
this command now:
-.. code-block:: python
+.. code-block:: text
$ python setup.py sdist
@@ -696,6 +702,7 @@ contains various settings related to testing and internationalization:
Our generated ``setup.cfg`` looks like this:
.. literalinclude:: MyProject/setup.cfg
+ :language: guess
:linenos:
The values in the default setup file allow various commonly-used
@@ -743,6 +750,7 @@ This is the file named ``__init__.py``. The presence of an ``__init__.py``
also informs Python that the directory which contains it is a *package*.
.. literalinclude:: MyProject/myproject/__init__.py
+ :language: python
:linenos:
#. Line 1 imports the :term:`Configurator` class from
@@ -789,6 +797,7 @@ code which accepts a :term:`request` and which returns a
:term:`response`.
.. literalinclude:: MyProject/myproject/views.py
+ :language: python
:linenos:
This bit of code was registered as the view callable within ``__init__.py``
@@ -835,6 +844,7 @@ and provide APIs which mutate and return this data. We write a class
named ``MyModel`` that provides the behavior.
.. literalinclude:: MyProject/myproject/models.py
+ :language: python
:linenos:
#. Lines 1-2 define the MyModel class.
@@ -881,6 +891,7 @@ by view functions themselves. See :ref:`templates_used_directly` and
The ``tests.py`` module includes unit tests for your application.
.. literalinclude:: MyProject/myproject/tests.py
+ :language: python
:linenos:
This sample ``tests.py`` file has a single unit test defined within
@@ -891,3 +902,67 @@ provided as convenience and example.
See :ref:`unittesting_chapter` for more information about writing
:app:`Pyramid` unit tests.
+
+Modifying Package Structure
+----------------------------
+
+It is best practice for your application's code layout to not stray too much
+from accepted Pyramid paster template defaults. If you refrain from changing
+things very much, other Pyramid coders will be able to more quickly
+understand your application. However, the code layout choices made for you
+by a paster template are in no way magical or required. Despite the choices
+made for you by any paster template, you can decide to lay your code out any
+way you see fit.
+
+For example, the configuration method named
+:meth:`~pyramid.configuration.Configurator.add_view` requires you to pass a
+:term:`dotted Python name` or a direct object reference as the class or
+function to be used as a view. By default, the ``pyramid_starter`` paster
+template would have you add view functions to the ``views.py`` module in your
+package. However, you might be more comfortable creating a ``views``
+*directory*, and adding a single file for each view.
+
+If your project package name was ``myproject`` and you wanted to arrange all
+your views in a Python subpackage within the ``myproject`` :term:`package`
+named ``views`` instead of within a single ``views.py`` file, you might:
+
+- Create a ``views`` directory inside your ``mypackage`` package directory
+ (the same directory which holds ``views.py``).
+
+- *Move* the existing ``views.py`` file to a file inside the new ``views``
+ directory named, say, ``blog.py``.
+
+- Create a file within the new ``views`` directory named ``__init__.py`` (it
+ can be empty, this just tells Python that the ``views`` directory is a
+ *package*.
+
+Then change the __init__.py of your myproject project (*not* the
+``__init__.py`` you just created in the ``views`` directory, the one in its
+parent directory). For example, from something like:
+
+.. code-block:: python
+
+ config.add_view('myproject.views.my_view',
+ renderer='myproject:templates/mytemplate.pt')
+
+To this:
+
+.. code-block:: python
+
+ config.add_view('myproject.views.blogs.my_view',
+ renderer='myproject:templates/mytemplate.pt')
+
+You can then continue to add files to the ``views`` directory, and refer to
+views or handler classes/functions within those files via the dotted name
+passed as the first argument to ``add_view``. For example:
+
+.. code-block:: python
+
+ config.add_view('myproject.views.anothermodule.my_view',
+ renderer='myproject:templates/anothertemplate.pt')
+
+This pattern can be used to rearrage code referred to by any Pyramid API
+argument which accepts a :term:`dotted Python name` or direct object
+reference.
+
+
diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst
index c57525f4c..427acc319 100644
--- a/docs/narr/startup.rst
+++ b/docs/narr/startup.rst
@@ -86,9 +86,9 @@ press ``return`` after running ``paster serve development.ini``.
:linenos:
In this case, the ``myproject.run:app`` function referred to by the entry
- point URI ``egg:MyProject#app`` (see :ref:`MyProject_ini` for more
- information about entry point URIs, and how they relate to callables),
- will receive the key/value pairs ``{'reload_templates':'true',
+ point URI ``egg:MyProject`` (see :ref:`MyProject_ini` for more information
+ about entry point URIs, and how they relate to callables), will receive
+ the key/value pairs ``{'reload_templates':'true',
'debug_authorization':'false', 'debug_notfound':'false',
'debug_templates':'true', 'default_locale_name':'en'}``.
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index dfb060d32..1b911a4a8 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -349,7 +349,8 @@ also capture the remainder of the URL, for example:
foo/{baz}/{bar}{fizzle:.*}
-The above pattern will match these URLs, generating the following matchdicts:
+The above pattern will match these URLs, generating the following
+matchdicts::
foo/1/2/ -> {'baz':'1', 'bar':'2', 'fizzle':()}
foo/abc/def/a/b/c -> {'baz':'abc', 'bar':'def', 'fizzle': 'a/b/c')}
diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst
index ff5aaea18..766f05b56 100644
--- a/docs/tutorials/modwsgi/index.rst
+++ b/docs/tutorials/modwsgi/index.rst
@@ -56,21 +56,20 @@ commands and files.
$ cd ~/modwsgi/env
$ bin/easy_install pyramid
-#. Create and install your :app:`Pyramid` application. For the
- purposes of this tutorial, we'll just be using the ``bfg_starter``
- application as a baseline application. Substitute your existing
- :app:`Pyramid` application as necessary if you already have
- one.
+#. Create and install your :app:`Pyramid` application. For the purposes of
+ this tutorial, we'll just be using the ``pyramid_starter`` application as
+ a baseline application. Substitute your existing :app:`Pyramid`
+ application as necessary if you already have one.
.. code-block:: text
$ cd ~/modwsgi/env
- $ bin/paster create -t bfg_starter myapp
+ $ bin/paster create -t pyramid_starter myapp
$ cd myapp
$ ../bin/python setup.py install
#. Within the virtualenv directory (``~/modwsgi/env``), create a
- script named ``bfg.wsgi``. Give it these contents:
+ script named ``pyramid.wsgi``. Give it these contents:
.. code-block:: python
@@ -84,12 +83,12 @@ commands and files.
assignment to the name ``application`` is important: mod_wsgi
requires finding such an assignment when it opens the file.
-#. Make the ``bfg.wsgi`` script executable.
+#. Make the ``pyramid.wsgi`` script executable.
.. code-block:: text
$ cd ~/modwsgi/env
- $ chmod 755 bfg.wsgi
+ $ chmod 755 pyramid.wsgi
#. Edit your Apache configuration and add some stuff. I happened to
create a file named ``/etc/apache2/other/modwsgi.conf`` on my own
@@ -101,12 +100,12 @@ commands and files.
# play badly with C extensions.
WSGIApplicationGroup %{GLOBAL}
WSGIPassAuthorization On
- WSGIDaemonProcess bfg user=chrism group=staff processes=1 threads=4 \
+ WSGIDaemonProcess pyramid user=chrism group=staff processes=1 threads=4 \
python-path=/Users/chrism/modwsgi/env/lib/python2.6/site-packages
- WSGIScriptAlias /myapp /Users/chrism/modwsgi/env/bfg.wsgi
+ WSGIScriptAlias /myapp /Users/chrism/modwsgi/env/pyramid.wsgi
<Directory /Users/chrism/modwsgi/env>
- WSGIProcessGroup bfg
+ WSGIProcessGroup pyramid
Order allow, deny
Allow from all
</Directory>
diff --git a/pyramid/compat/__init__.py b/pyramid/compat/__init__.py
index 205175132..8bbf79703 100644
--- a/pyramid/compat/__init__.py
+++ b/pyramid/compat/__init__.py
@@ -133,7 +133,12 @@ except NameError: # pragma: no cover
try:
import json
except ImportError: # pragma: no cover
- import simplejson as json
+ try:
+ import simplejson as json
+ except NotImplementedError:
+ from django.utils import simplejson as json # GAE
+
+
try:
from hashlib import md5
diff --git a/pyramid/configuration.py b/pyramid/configuration.py
index 6ebb56ec3..29a322c7f 100644
--- a/pyramid/configuration.py
+++ b/pyramid/configuration.py
@@ -3,7 +3,6 @@ import re
import sys
import threading
import inspect
-import pkg_resources
import venusian
@@ -67,7 +66,6 @@ from pyramid.i18n import get_localizer
from pyramid.log import make_stream_logger
from pyramid.mako_templating import renderer_factory as mako_renderer_factory
from pyramid.path import caller_package
-from pyramid.path import package_of
from pyramid.path import package_path
from pyramid.registry import Registry
from pyramid.renderers import RendererHelper
@@ -83,6 +81,7 @@ from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import find_interface
from pyramid.traversal import traversal_path
from pyramid.urldispatch import RoutesMapper
+from pyramid.util import DottedNameResolver
from pyramid.view import default_exceptionresponse_view
from pyramid.view import render_view_to_response
@@ -2727,144 +2726,6 @@ def make_app(root_factory, package=None, filename='configure.zcml',
config.end()
return config.make_wsgi_app()
-class DottedNameResolver(object):
- """ This class resolves dotted name references to 'global' Python
- objects (objects which can be imported) to those objects.
-
- Two dotted name styles are supported during deserialization:
-
- - ``pkg_resources``-style dotted names where non-module attributes
- of a package are separated from the rest of the path using a ':'
- e.g. ``package.module:attr``.
-
- - ``zope.dottedname``-style dotted names where non-module
- attributes of a package are separated from the rest of the path
- using a '.' e.g. ``package.module.attr``.
-
- These styles can be used interchangeably. If the serialization
- contains a ``:`` (colon), the ``pkg_resources`` resolution
- mechanism will be chosen, otherwise the ``zope.dottedname``
- resolution mechanism will be chosen.
-
- The constructor accepts a single argument named ``package`` which
- should be a one of:
-
- - a Python module or package object
-
- - A fully qualified (not relative) dotted name to a module or package
-
- - The value ``None``
-
- The ``package`` is used when relative dotted names are supplied to
- the resolver's ``resolve`` and ``maybe_resolve`` methods. A
- dotted name which has a ``.`` (dot) or ``:`` (colon) as its first
- character is treated as relative.
-
- If the value ``None`` is supplied as the package name, the
- resolver will only be able to resolve fully qualified (not
- relative) names. Any attempt to resolve a relative name when the
- ``package`` is ``None`` will result in an
- :exc:`pyramid.configuration.ConfigurationError` exception.
-
- If a *module* or *module name* (as opposed to a package or package
- name) is supplied as ``package``, its containing package is
- computed and this package used to derive the package name (all
- names are resolved relative to packages, never to modules). For
- example, if the ``package`` argument to this type was passed the
- string ``xml.dom.expatbuilder``, and ``.mindom`` is supplied to
- the ``resolve`` method, the resulting import would be for
- ``xml.minidom``, because ``xml.dom.expatbuilder`` is a module
- object, not a package object.
-
- If a *package* or *package name* (as opposed to a module or module
- name) is supplied as ``package``, this package will be used to
- relative compute dotted names. For example, if the ``package``
- argument to this type was passed the string ``xml.dom``, and
- ``.minidom`` is supplied to the ``resolve`` method, the resulting
- import would be for ``xml.minidom``.
-
- When a dotted name cannot be resolved, a
- :class:`pyramid.exceptions.ConfigurationError` error is raised.
- """
- def __init__(self, package):
- if package is None:
- self.package_name = None
- self.package = None
- else:
- if isinstance(package, basestring):
- try:
- __import__(package)
- except ImportError:
- raise ConfigurationError(
- 'The dotted name %r cannot be imported' % (package,))
- package = sys.modules[package]
- self.package = package_of(package)
- self.package_name = self.package.__name__
-
- def _pkg_resources_style(self, value):
- """ package.module:attr style """
- if value.startswith('.') or value.startswith(':'):
- if not self.package_name:
- raise ConfigurationError(
- 'relative name %r irresolveable without '
- 'package_name' % (value,))
- if value in ['.', ':']:
- value = self.package_name
- else:
- value = self.package_name + value
- return pkg_resources.EntryPoint.parse(
- 'x=%s' % value).load(False)
-
- def _zope_dottedname_style(self, value):
- """ package.module.attr style """
- module = self.package_name and self.package_name or None
- if value == '.':
- if self.package_name is None:
- raise ConfigurationError(
- 'relative name %r irresolveable without package' % (value,)
- )
- name = module.split('.')
- else:
- name = value.split('.')
- if not name[0]:
- if module is None:
- raise ConfigurationError(
- 'relative name %r irresolveable without '
- 'package' % (value,)
- )
- module = module.split('.')
- name.pop(0)
- while not name[0]:
- module.pop()
- name.pop(0)
- name = module + name
-
- used = name.pop(0)
- found = __import__(used)
- for n in name:
- used += '.' + n
- try:
- found = getattr(found, n)
- except AttributeError:
- __import__(used)
- found = getattr(found, n) # pragma: no cover
-
- return found
-
- def resolve(self, dotted):
- if not isinstance(dotted, basestring):
- raise ConfigurationError('%r is not a string' % (dotted,))
- return self.maybe_resolve(dotted)
-
- def maybe_resolve(self, dotted):
- if isinstance(dotted, basestring):
- if ':' in dotted:
- return self._pkg_resources_style(dotted)
- else:
- return self._zope_dottedname_style(dotted)
- return dotted
-
-
class ActionPredicate(object):
action_name = 'action'
def __init__(self, action):
diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py
index 6356e0db4..a5f9b0f68 100644
--- a/pyramid/mako_templating.py
+++ b/pyramid/mako_templating.py
@@ -8,6 +8,8 @@ from pyramid.interfaces import ITemplateRenderer
from pyramid.exceptions import ConfigurationError
from pyramid.resource import resolve_resource_spec
from pyramid.resource import abspath_from_resource_spec
+from pyramid.settings import asbool
+from pyramid.util import DottedNameResolver
from mako.lookup import TemplateLookup
from mako import exceptions
@@ -61,24 +63,40 @@ def renderer_factory(info):
lookup = registry.queryUtility(IMakoLookup)
if lookup is None:
reload_templates = settings.get('reload_templates', False)
- directories = settings.get('mako.directories')
- module_directory = settings.get('mako.module_directory')
+ directories = settings.get('mako.directories', None)
+ module_directory = settings.get('mako.module_directory', None)
input_encoding = settings.get('mako.input_encoding', 'utf-8')
error_handler = settings.get('mako.error_handler', None)
default_filters = settings.get('mako.default_filters', None)
- imports = settings.get('mako.imports', [])
+ imports = settings.get('mako.imports', None)
+ strict_undefined = settings.get('mako.strict_undefined', 'false')
if directories is None:
raise ConfigurationError(
'Mako template used without a ``mako.directories`` setting')
- directories = directories.splitlines()
+ if not hasattr(directories, '__iter__'):
+ directories = filter(None, directories.splitlines())
directories = [ abspath_from_resource_spec(d) for d in directories ]
+ if module_directory is not None:
+ module_directory = abspath_from_resource_spec(module_directory)
+ if error_handler is not None:
+ dotted = DottedNameResolver(info.package)
+ error_handler = dotted.maybe_resolve(error_handler)
+ if default_filters is not None:
+ if not hasattr(default_filters, '__iter__'):
+ default_filters = filter(None, default_filters.splitlines())
+ if imports is not None:
+ if not hasattr(imports, '__iter__'):
+ imports = filter(None, imports.splitlines())
+ strict_undefined = asbool(strict_undefined)
+
lookup = PkgResourceTemplateLookup(directories=directories,
module_directory=module_directory,
input_encoding=input_encoding,
error_handler=error_handler,
default_filters=default_filters,
imports=imports,
- filesystem_checks=reload_templates)
+ filesystem_checks=reload_templates,
+ strict_undefined=strict_undefined)
registry_lock.acquire()
try:
registry.registerUtility(lookup, IMakoLookup)
diff --git a/pyramid/paster_templates/alchemy/+package+/models.py b/pyramid/paster_templates/alchemy/+package+/models.py
index 1134cce07..9942f2bb0 100755
--- a/pyramid/paster_templates/alchemy/+package+/models.py
+++ b/pyramid/paster_templates/alchemy/+package+/models.py
@@ -80,7 +80,7 @@ def initialize_sql(engine):
try:
populate()
except IntegrityError:
- pass
+ DBSession.rollback()
def appmaker(engine):
initialize_sql(engine)
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/models.py b/pyramid/paster_templates/pylons_sqla/+package+/models.py
index 6418f0c8b..181232072 100644
--- a/pyramid/paster_templates/pylons_sqla/+package+/models.py
+++ b/pyramid/paster_templates/pylons_sqla/+package+/models.py
@@ -43,4 +43,4 @@ def initialize_sql(engine):
try:
populate()
except IntegrityError:
- pass
+ DBSession.rollback()
diff --git a/pyramid/paster_templates/routesalchemy/+package+/models.py b/pyramid/paster_templates/routesalchemy/+package+/models.py
index 9da906752..e8755ba78 100644
--- a/pyramid/paster_templates/routesalchemy/+package+/models.py
+++ b/pyramid/paster_templates/routesalchemy/+package+/models.py
@@ -39,4 +39,4 @@ def initialize_sql(engine):
try:
populate()
except IntegrityError:
- pass
+ DBSession.rollback()
diff --git a/pyramid/paster_templates/starter/+package+/static/footerbg.png b/pyramid/paster_templates/starter/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/headerbg.png b/pyramid/paster_templates/starter/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/ie6.css b/pyramid/paster_templates/starter/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/starter/+package+/static/logo.png b/pyramid/paster_templates/starter/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/starter/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/middlebg.png b/pyramid/paster_templates/starter/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/pylons.css b/pyramid/paster_templates/starter/+package+/static/pylons.css
index c153be07f..33b21ac1a 100644
--- a/pyramid/paster_templates/starter/+package+/static/pylons.css
+++ b/pyramid/paster_templates/starter/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
li{display:list-item;}
-/* nested lists have no top/bottom margins */
ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-/* 2 deep unordered lists use a circle */
ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-/* 3 deep (or more) unordered lists use a square */
ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
.hidden{display:none;}
p{line-height:1.5em;}
-h1{font-size:1.75em;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,25 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
-#wrap {min-height: 100%;}
-#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+#wrap{min-height:100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
+#header{background:#000000;top:0;font-size:14px;}
+#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
+.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
#top,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top{color:#000000;height:230px;
+background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
.bottom{padding-top:50px;}
-#left{width:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#left{width:350px;float:left;padding-right:25px;}
+#right{width:350px;float:right;padding-left:25px;}
.align-left{text-align:left;}
.align-right{text-align:right;}
.align-center{text-align:center;}
@@ -67,7 +58,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{}
+input[type=text]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
-body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/pyramid/paster_templates/starter/+package+/static/pyramid.png b/pyramid/paster_templates/starter/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/transparent.gif b/pyramid/paster_templates/starter/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt
index 6ad23d44f..02fc00eeb 100644
--- a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt
+++ b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt
@@ -7,21 +7,21 @@
<meta name="description" content="pyramid web application" />
<link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
<link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" rel="stylesheet" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.application_url}/static/ie6.css" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.application_url}/static/pyramid.png" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
diff --git a/pyramid/settings.py b/pyramid/settings.py
index 64b108421..928dd1915 100644
--- a/pyramid/settings.py
+++ b/pyramid/settings.py
@@ -88,7 +88,7 @@ def get_settings():
deprecated(
'get_settings',
'(pyramid.settings.get_settings is deprecated as of Pyramid 1.0. Use'
- '``pyramid.threadlocals.get_current_registry().settings`` instead or use '
+ '``pyramid.threadlocal.get_current_registry().settings`` instead or use '
'the ``settings`` attribute of the registry available from the request '
'(``request.registry.settings``)).')
diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py
index 5113d1f4e..938db3c31 100644
--- a/pyramid/tests/test_configuration.py
+++ b/pyramid/tests/test_configuration.py
@@ -4286,181 +4286,6 @@ class TestMakeApp(unittest.TestCase):
Configurator=DummyConfigurator)
self.assertEqual(app.zcml_file, '2.zcml')
-class TestDottedNameResolver(unittest.TestCase):
- def _makeOne(self, package=None):
- from pyramid.configuration import DottedNameResolver
- return DottedNameResolver(package)
-
- def config_exc(self, func, *arg, **kw):
- from pyramid.exceptions import ConfigurationError
- try:
- func(*arg, **kw)
- except ConfigurationError, e:
- return e
- else:
- raise AssertionError('Invalid not raised') # pragma: no cover
-
- def test_zope_dottedname_style_resolve_absolute(self):
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- 'pyramid.tests.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_zope_dottedname_style_irrresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(ImportError, typ._zope_dottedname_style,
- 'pyramid.test_configuration.nonexisting_name')
-
- def test__zope_dottedname_style_resolve_relative(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._zope_dottedname_style(
- '.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__zope_dottedname_style_resolve_relative_leading_dots(self):
- import pyramid.tests.test_configuration
- typ = self._makeOne(package=pyramid.tests)
- result = typ._zope_dottedname_style(
- '..tests.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__zope_dottedname_style_resolve_relative_is_dot(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._zope_dottedname_style('.')
- self.assertEqual(result, pyramid.tests)
-
- def test__zope_dottedname_style_irresolveable_relative_is_dot(self):
- typ = self._makeOne()
- e = self.config_exc(typ._zope_dottedname_style, '.')
- self.assertEqual(
- e.args[0],
- "relative name '.' irresolveable without package")
-
- def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self):
- typ = self._makeOne()
- e = self.config_exc(typ._zope_dottedname_style, '.whatever')
- self.assertEqual(
- e.args[0],
- "relative name '.whatever' irresolveable without package")
-
- def test_zope_dottedname_style_irrresolveable_relative(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- self.assertRaises(ImportError, typ._zope_dottedname_style,
- '.notexisting')
-
- def test__zope_dottedname_style_resolveable_relative(self):
- import pyramid
- typ = self._makeOne(package=pyramid)
- result = typ._zope_dottedname_style('.tests')
- from pyramid import tests
- self.assertEqual(result, tests)
-
- def test__zope_dottedname_style_irresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(
- ImportError,
- typ._zope_dottedname_style, 'pyramid.fudge.bar')
-
- def test__zope_dottedname_style_resolveable_absolute(self):
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- 'pyramid.tests.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_resolve_absolute(self):
- typ = self._makeOne()
- result = typ._pkg_resources_style(
- 'pyramid.tests.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_irrresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(ImportError, typ._pkg_resources_style,
- 'pyramid.tests:nonexisting')
-
- def test__pkg_resources_style_resolve_relative(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._pkg_resources_style(
- '.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_resolve_relative_is_dot(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._pkg_resources_style('.')
- self.assertEqual(result, pyramid.tests)
-
- def test__pkg_resources_style_resolve_relative_nocurrentpackage(self):
- typ = self._makeOne()
- from pyramid.exceptions import ConfigurationError
- self.assertRaises(ConfigurationError, typ._pkg_resources_style,
- '.whatever')
-
- def test__pkg_resources_style_irrresolveable_relative(self):
- import pyramid
- typ = self._makeOne(package=pyramid)
- self.assertRaises(ImportError, typ._pkg_resources_style,
- ':notexisting')
-
- def test_resolve_not_a_string(self):
- typ = self._makeOne()
- e = self.config_exc(typ.resolve, None)
- self.assertEqual(e.args[0], 'None is not a string')
-
- def test_resolve_using_pkgresources_style(self):
- typ = self._makeOne()
- result = typ.resolve(
- 'pyramid.tests.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_resolve_using_zope_dottedname_style(self):
- typ = self._makeOne()
- result = typ.resolve(
- 'pyramid.tests.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_resolve_missing_raises(self):
- typ = self._makeOne()
- self.assertRaises(ImportError, typ.resolve, 'cant.be.found')
-
- def test_ctor_string_module_resolveable(self):
- import pyramid.tests
- typ = self._makeOne('pyramid.tests.test_configuration')
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_string_package_resolveable(self):
- import pyramid.tests
- typ = self._makeOne('pyramid.tests')
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_string_irresolveable(self):
- from pyramid.configuration import ConfigurationError
- self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found')
-
- def test_ctor_module(self):
- import pyramid.tests
- import pyramid.tests.test_configuration
- typ = self._makeOne(pyramid.tests.test_configuration)
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_package(self):
- import pyramid.tests
- typ = self._makeOne(pyramid.tests)
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_None(self):
- typ = self._makeOne(None)
- self.assertEqual(typ.package, None)
- self.assertEqual(typ.package_name, None)
-
class Test_isexception(unittest.TestCase):
def _callFUT(self, ob):
from pyramid.configuration import isexception
diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py
index e0c02550b..506dc18cd 100644
--- a/pyramid/tests/test_mako_templating.py
+++ b/pyramid/tests/test_mako_templating.py
@@ -47,7 +47,7 @@ class Test_renderer_factory(Base, unittest.TestCase):
def test_composite_directories_path(self):
from pyramid.mako_templating import IMakoLookup
- twice = self.templates_dir + '\n' + self.templates_dir
+ twice = '\n' + self.templates_dir + '\n' + self.templates_dir + '\n'
settings = {'mako.directories':twice}
info = DummyRendererInfo({
'name':'helloworld.mak',
@@ -59,6 +59,165 @@ class Test_renderer_factory(Base, unittest.TestCase):
lookup = self.config.registry.getUtility(IMakoLookup)
self.assertEqual(lookup.directories, [self.templates_dir]*2)
+ def test_directories_list(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':['a', 'b']}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.directories, ['a', 'b'])
+
+ def test_with_module_directory_resource_spec(self):
+ import os
+ from pyramid.mako_templating import IMakoLookup
+ module_directory = 'pyramid.tests:fixtures'
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.module_directory':module_directory}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ fixtures = os.path.join(os.path.dirname(__file__), 'fixtures')
+ self.assertEqual(lookup.module_directory, fixtures)
+
+ def test_with_module_directory_resource_abspath(self):
+ import os
+ from pyramid.mako_templating import IMakoLookup
+ fixtures = os.path.join(os.path.dirname(__file__), 'fixtures')
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.module_directory':fixtures}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.module_directory, fixtures)
+
+ def test_with_input_encoding(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.input_encoding':'utf-16'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['input_encoding'], 'utf-16')
+
+ def test_with_error_handler(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.error_handler':'pyramid.tests'}
+ import pyramid.tests
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['error_handler'], pyramid.tests)
+
+ def test_with_default_filters(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.default_filters':'\nh\ng\n\n'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['default_filters'], ['h', 'g'])
+
+ def test_with_default_filters_list(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.default_filters':['h', 'g']}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['default_filters'], ['h', 'g'])
+
+ def test_with_imports(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.imports':'\none\ntwo\n\n'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['imports'], ['one', 'two'])
+
+ def test_with_imports_list(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.imports':['one', 'two']}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['imports'], ['one', 'two'])
+
+ def test_with_strict_undefined_true(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.strict_undefined':'true'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['strict_undefined'], True)
+
+ def test_with_strict_undefined_false(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.strict_undefined':'false'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['strict_undefined'], False)
+
def test_with_lookup(self):
from pyramid.mako_templating import IMakoLookup
lookup = dict()
@@ -170,7 +329,8 @@ class TestIntegration(unittest.TestCase):
def test_render_to_response_pkg_spec(self):
from pyramid.renderers import render_to_response
- result = render_to_response('pyramid.tests:fixtures/helloworld.mak', {'a':1})
+ result = render_to_response('pyramid.tests:fixtures/helloworld.mak',
+ {'a':1})
self.assertEqual(result.ubody, u'\nHello föö\n')
def test_render_with_abs_path(self):
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
new file mode 100644
index 000000000..2929f888f
--- /dev/null
+++ b/pyramid/tests/test_util.py
@@ -0,0 +1,177 @@
+import unittest
+
+class TestDottedNameResolver(unittest.TestCase):
+ def _makeOne(self, package=None):
+ from pyramid.util import DottedNameResolver
+ return DottedNameResolver(package)
+
+ def config_exc(self, func, *arg, **kw):
+ from pyramid.exceptions import ConfigurationError
+ try:
+ func(*arg, **kw)
+ except ConfigurationError, e:
+ return e
+ else:
+ raise AssertionError('Invalid not raised') # pragma: no cover
+
+ def test_zope_dottedname_style_resolve_absolute(self):
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ 'pyramid.tests.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_zope_dottedname_style_irrresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(ImportError, typ._zope_dottedname_style,
+ 'pyramid.test_util.nonexisting_name')
+
+ def test__zope_dottedname_style_resolve_relative(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._zope_dottedname_style(
+ '.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__zope_dottedname_style_resolve_relative_leading_dots(self):
+ import pyramid.tests.test_configuration
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._zope_dottedname_style(
+ '..tests.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__zope_dottedname_style_resolve_relative_is_dot(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._zope_dottedname_style('.')
+ self.assertEqual(result, pyramid.tests)
+
+ def test__zope_dottedname_style_irresolveable_relative_is_dot(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ._zope_dottedname_style, '.')
+ self.assertEqual(
+ e.args[0],
+ "relative name '.' irresolveable without package")
+
+ def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ._zope_dottedname_style, '.whatever')
+ self.assertEqual(
+ e.args[0],
+ "relative name '.whatever' irresolveable without package")
+
+ def test_zope_dottedname_style_irrresolveable_relative(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ self.assertRaises(ImportError, typ._zope_dottedname_style,
+ '.notexisting')
+
+ def test__zope_dottedname_style_resolveable_relative(self):
+ import pyramid
+ typ = self._makeOne(package=pyramid)
+ result = typ._zope_dottedname_style('.tests')
+ from pyramid import tests
+ self.assertEqual(result, tests)
+
+ def test__zope_dottedname_style_irresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(
+ ImportError,
+ typ._zope_dottedname_style, 'pyramid.fudge.bar')
+
+ def test__zope_dottedname_style_resolveable_absolute(self):
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ 'pyramid.tests.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_resolve_absolute(self):
+ typ = self._makeOne()
+ result = typ._pkg_resources_style(
+ 'pyramid.tests.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_irrresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(ImportError, typ._pkg_resources_style,
+ 'pyramid.tests:nonexisting')
+
+ def test__pkg_resources_style_resolve_relative(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._pkg_resources_style(
+ '.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_resolve_relative_is_dot(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._pkg_resources_style('.')
+ self.assertEqual(result, pyramid.tests)
+
+ def test__pkg_resources_style_resolve_relative_nocurrentpackage(self):
+ typ = self._makeOne()
+ from pyramid.exceptions import ConfigurationError
+ self.assertRaises(ConfigurationError, typ._pkg_resources_style,
+ '.whatever')
+
+ def test__pkg_resources_style_irrresolveable_relative(self):
+ import pyramid
+ typ = self._makeOne(package=pyramid)
+ self.assertRaises(ImportError, typ._pkg_resources_style,
+ ':notexisting')
+
+ def test_resolve_not_a_string(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ.resolve, None)
+ self.assertEqual(e.args[0], 'None is not a string')
+
+ def test_resolve_using_pkgresources_style(self):
+ typ = self._makeOne()
+ result = typ.resolve(
+ 'pyramid.tests.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_resolve_using_zope_dottedname_style(self):
+ typ = self._makeOne()
+ result = typ.resolve(
+ 'pyramid.tests.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_resolve_missing_raises(self):
+ typ = self._makeOne()
+ self.assertRaises(ImportError, typ.resolve, 'cant.be.found')
+
+ def test_ctor_string_module_resolveable(self):
+ import pyramid.tests
+ typ = self._makeOne('pyramid.tests.test_util')
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_string_package_resolveable(self):
+ import pyramid.tests
+ typ = self._makeOne('pyramid.tests')
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_string_irresolveable(self):
+ from pyramid.configuration import ConfigurationError
+ self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found')
+
+ def test_ctor_module(self):
+ import pyramid.tests
+ import pyramid.tests.test_util
+ typ = self._makeOne(pyramid.tests.test_util)
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_package(self):
+ import pyramid.tests
+ typ = self._makeOne(pyramid.tests)
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_None(self):
+ typ = self._makeOne(None)
+ self.assertEqual(typ.package, None)
+ self.assertEqual(typ.package_name, None)
+
diff --git a/pyramid/util.py b/pyramid/util.py
new file mode 100644
index 000000000..84a23bb23
--- /dev/null
+++ b/pyramid/util.py
@@ -0,0 +1,144 @@
+import pkg_resources
+import sys
+
+from pyramid.exceptions import ConfigurationError
+from pyramid.path import package_of
+
+class DottedNameResolver(object):
+ """ This class resolves dotted name references to 'global' Python
+ objects (objects which can be imported) to those objects.
+
+ Two dotted name styles are supported during deserialization:
+
+ - ``pkg_resources``-style dotted names where non-module attributes
+ of a package are separated from the rest of the path using a ':'
+ e.g. ``package.module:attr``.
+
+ - ``zope.dottedname``-style dotted names where non-module
+ attributes of a package are separated from the rest of the path
+ using a '.' e.g. ``package.module.attr``.
+
+ These styles can be used interchangeably. If the serialization
+ contains a ``:`` (colon), the ``pkg_resources`` resolution
+ mechanism will be chosen, otherwise the ``zope.dottedname``
+ resolution mechanism will be chosen.
+
+ The constructor accepts a single argument named ``package`` which
+ should be a one of:
+
+ - a Python module or package object
+
+ - A fully qualified (not relative) dotted name to a module or package
+
+ - The value ``None``
+
+ The ``package`` is used when relative dotted names are supplied to
+ the resolver's ``resolve`` and ``maybe_resolve`` methods. A
+ dotted name which has a ``.`` (dot) or ``:`` (colon) as its first
+ character is treated as relative.
+
+ If the value ``None`` is supplied as the package name, the
+ resolver will only be able to resolve fully qualified (not
+ relative) names. Any attempt to resolve a relative name when the
+ ``package`` is ``None`` will result in an
+ :exc:`pyramid.configuration.ConfigurationError` exception.
+
+ If a *module* or *module name* (as opposed to a package or package
+ name) is supplied as ``package``, its containing package is
+ computed and this package used to derive the package name (all
+ names are resolved relative to packages, never to modules). For
+ example, if the ``package`` argument to this type was passed the
+ string ``xml.dom.expatbuilder``, and ``.mindom`` is supplied to
+ the ``resolve`` method, the resulting import would be for
+ ``xml.minidom``, because ``xml.dom.expatbuilder`` is a module
+ object, not a package object.
+
+ If a *package* or *package name* (as opposed to a module or module
+ name) is supplied as ``package``, this package will be used to
+ relative compute dotted names. For example, if the ``package``
+ argument to this type was passed the string ``xml.dom``, and
+ ``.minidom`` is supplied to the ``resolve`` method, the resulting
+ import would be for ``xml.minidom``.
+
+ When a dotted name cannot be resolved, a
+ :class:`pyramid.exceptions.ConfigurationError` error is raised.
+ """
+ def __init__(self, package):
+ if package is None:
+ self.package_name = None
+ self.package = None
+ else:
+ if isinstance(package, basestring):
+ try:
+ __import__(package)
+ except ImportError:
+ raise ConfigurationError(
+ 'The dotted name %r cannot be imported' % (package,))
+ package = sys.modules[package]
+ self.package = package_of(package)
+ self.package_name = self.package.__name__
+
+ def _pkg_resources_style(self, value):
+ """ package.module:attr style """
+ if value.startswith('.') or value.startswith(':'):
+ if not self.package_name:
+ raise ConfigurationError(
+ 'relative name %r irresolveable without '
+ 'package_name' % (value,))
+ if value in ['.', ':']:
+ value = self.package_name
+ else:
+ value = self.package_name + value
+ return pkg_resources.EntryPoint.parse(
+ 'x=%s' % value).load(False)
+
+ def _zope_dottedname_style(self, value):
+ """ package.module.attr style """
+ module = self.package_name and self.package_name or None
+ if value == '.':
+ if self.package_name is None:
+ raise ConfigurationError(
+ 'relative name %r irresolveable without package' % (value,)
+ )
+ name = module.split('.')
+ else:
+ name = value.split('.')
+ if not name[0]:
+ if module is None:
+ raise ConfigurationError(
+ 'relative name %r irresolveable without '
+ 'package' % (value,)
+ )
+ module = module.split('.')
+ name.pop(0)
+ while not name[0]:
+ module.pop()
+ name.pop(0)
+ name = module + name
+
+ used = name.pop(0)
+ found = __import__(used)
+ for n in name:
+ used += '.' + n
+ try:
+ found = getattr(found, n)
+ except AttributeError:
+ __import__(used)
+ found = getattr(found, n) # pragma: no cover
+
+ return found
+
+ def resolve(self, dotted):
+ if not isinstance(dotted, basestring):
+ raise ConfigurationError('%r is not a string' % (dotted,))
+ return self.maybe_resolve(dotted)
+
+ def maybe_resolve(self, dotted):
+ if isinstance(dotted, basestring):
+ if ':' in dotted:
+ return self._pkg_resources_style(dotted)
+ else:
+ return self._zope_dottedname_style(dotted)
+ return dotted
+
+
diff --git a/setup.py b/setup.py
index c1939f73c..75f5b13dd 100644
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@ except IOError:
install_requires=[
'Chameleon >= 1.2.3',
- 'Mako',
+ 'Mako >= 0.3.6', # strict_undefined
'Paste > 1.7', # temp version pin to prevent PyPi install failure :-(
'PasteDeploy',
'PasteScript',