summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Bangert <ben@groovie.org>2010-11-18 14:13:14 -0800
committerBen Bangert <ben@groovie.org>2010-11-18 14:13:14 -0800
commitc1a3d2687cd27e85af95110f72c068a877cb662c (patch)
tree7359e63f728e342d93e227950764a6288aa7f626
parente84116c068f45c68752f89062fa545dd40acd63f (diff)
parenta66593d25e77f1a0e749f5590b45498bbaa66755 (diff)
downloadpyramid-c1a3d2687cd27e85af95110f72c068a877cb662c.tar.gz
pyramid-c1a3d2687cd27e85af95110f72c068a877cb662c.tar.bz2
pyramid-c1a3d2687cd27e85af95110f72c068a877cb662c.zip
Merge branch 'master' of github.com:Pylons/pyramid
-rw-r--r--.gitignore9
-rw-r--r--CHANGES.txt15
-rw-r--r--TODO.txt141
-rw-r--r--pyramid/authentication.py6
-rw-r--r--pyramid/configuration.py18
-rw-r--r--pyramid/request.py3
-rw-r--r--pyramid/tests/test_authentication.py6
-rw-r--r--pyramid/tests/test_configuration.py9
-rw-r--r--pyramid/tests/test_traversal.py26
-rw-r--r--pyramid/traversal.py13
-rw-r--r--setup.py2
11 files changed, 163 insertions, 85 deletions
diff --git a/.gitignore b/.gitignore
index 5d46de01f..706f6493d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,9 +5,10 @@
*.pt.py
*.txt.py
.coverage
-env26
-env24
-env27
-jyenv
+env26/
+env24/
+env27/
+jyenv/
+pypyenv/
build/
dist/
diff --git a/CHANGES.txt b/CHANGES.txt
index b418565fa..4d51b89f5 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -17,6 +17,9 @@ Features
- Add a ``pyramid.request.Request.route_path`` API. This is a convenience
method of the request which calls ``pyramid.url.route_url``.
+- Make test suite pass on Jython (requires PasteScript trunk, presumably to
+ be 1.7.4).
+
Bug Fixes
---------
@@ -30,6 +33,18 @@ Bug Fixes
- The ``pyramid_alchemy`` paster template had a typo, preventing an import
from working.
+- Fix apparent failures when calling ``pyramid.traversal.find_model(root,
+ path)`` or ``pyramid.traversal.traverse(path)`` when ``path`` is
+ (erroneously) a Unicode object. The user is meant to pass these APIs a
+ string object, never a Unicode object. In practice, however, users indeed
+ pass Unicode. Because the string that is passed must be ASCII encodeable,
+ now, if they pass a Unicode object, its data is eagerly converted to an
+ 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
+ ``traversal_path``).
+
Backwards Incompatibilities
---------------------------
diff --git a/TODO.txt b/TODO.txt
index def6fe687..fe411f749 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,5 +1,80 @@
-:mod:`repoze.bfg` TODOs
-=======================
+Pyramid TODOs
+=============
+
+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.
+
+- 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".
+
+- Remove calls to config.begin()/config.end() from startup config code in
+ tutorials and paster templates (no longer required).
+
+- 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.
+
+Should-Have
+-----------
+
+- Try to make test suite pass on PyPy, IronPython.
+
+- 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.
+
+- Add CRSF token creation/checking machinery (only "should have" vs. "must
+ have" because I'm not sure it belongs in Pyramid.. it definitely must exist
+ in formgen libraries, and *might* belong in Pyramid).
+
+- Change "Cleaning up After a Request" in the urldispatch chapter to
+ use ``request.add_response_callback``.
+
+Nice-to-Have
+------------
- Supply ``X-Vhm-Host`` support.
@@ -50,65 +125,7 @@
- Raise an exception when a value in response_headerlist is not a
string or decide to encode.
-- Change "Cleaning up After a Request" in the urldispatch chapter to
- use ``request.add_response_callback``.
-
-- Update App engine chapter.
-
-- Browser id?
-
-- .flash API on session.
-
-- CRSF token machinery
-
-- ``add_handler`` documentation.
-
-- ``handler`` ZCML directive.
-
-- ``docs`` directory for each paster template.
-
-- "BFG" in environ variables.
-
-- Test on GAE, Jython, PyPy, IronPython.
+- Update App engine chapter with less creaky directions.
-- Add docs for httpexceptions.
+- Add functionality that mocks the behavior of ``repoze.browserid``.
-- RendererHelper -> RendererInfo?
-
-- translationdir ZCML directive use of ``path_spec`` should maybe die.
-
-- 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
-
-- Default renderer.
-
-- Non-bwcompat use of threadlocals:
-
- 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.
-
-- Use ``@register_view`` instead of ``@view_config`` and change view docs to
- use "view registration" instead of "view configuration".
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 4849d2c41..86d725bcf 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -286,6 +286,7 @@ EXPIRE = object()
class AuthTktCookieHelper(object):
auth_tkt = auth_tkt # for tests
+ now = None # for tests
userid_type_decoders = {
'int':int,
@@ -373,7 +374,10 @@ class AuthTktCookieHelper(object):
except self.auth_tkt.BadTicket:
return None
- now = time.time()
+ now = self.now # service tests
+
+ if now is None:
+ now = time.time()
if self.timeout and ( (timestamp + self.timeout) < now ):
return None
diff --git a/pyramid/configuration.py b/pyramid/configuration.py
index 41b774e65..543f8f82d 100644
--- a/pyramid/configuration.py
+++ b/pyramid/configuration.py
@@ -24,6 +24,8 @@ from pyramid.interfaces import IChameleonTranslate
from pyramid.interfaces import IDebugLogger
from pyramid.interfaces import IDefaultPermission
from pyramid.interfaces import IDefaultRootFactory
+from pyramid.interfaces import IException
+from pyramid.interfaces import IExceptionResponse
from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import ILocaleNegotiator
from pyramid.interfaces import IMultiView
@@ -36,34 +38,32 @@ from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import ISecuredView
+from pyramid.interfaces import ISessionFactory
from pyramid.interfaces import IStaticURLInfo
from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
-from pyramid.interfaces import IExceptionResponse
-from pyramid.interfaces import IException
-from pyramid.interfaces import ISessionFactory
from pyramid import chameleon_text
from pyramid import chameleon_zpt
-from pyramid.mako_templating import renderer_factory as mako_renderer_factory
from pyramid import renderers
-from pyramid.renderers import RendererHelper
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.compat import all
from pyramid.compat import md5
from pyramid.events import ApplicationCreated
+from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import Forbidden
from pyramid.exceptions import NotFound
from pyramid.exceptions import PredicateMismatch
-from pyramid.exceptions import ConfigurationError
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_path
from pyramid.path import package_of
+from pyramid.path import package_path
from pyramid.registry import Registry
+from pyramid.renderers import RendererHelper
from pyramid.request import route_request_iface
from pyramid.resource import PackageOverrides
from pyramid.resource import resolve_resource_spec
@@ -72,12 +72,12 @@ from pyramid.static import StaticURLInfo
from pyramid.threadlocal import get_current_registry
from pyramid.threadlocal import get_current_request
from pyramid.threadlocal import manager
-from pyramid.traversal import traversal_path
from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import find_interface
+from pyramid.traversal import traversal_path
from pyramid.urldispatch import RoutesMapper
-from pyramid.view import render_view_to_response
from pyramid.view import default_exceptionresponse_view
+from pyramid.view import render_view_to_response
MAX_ORDER = 1 << 30
DEFAULT_PHASH = md5().hexdigest()
diff --git a/pyramid/request.py b/pyramid/request.py
index 43a4a3aa2..2f9ca5819 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -259,9 +259,6 @@ class Request(WebobRequest):
scheme, and port) for a named :app:`Pyramid`
:term:`route configuration`.
- .. note:: Calling :meth:`pyramid.Request.route_path` can be used to
- achieve the same result as :func:`pyramid.url.route_path`.
-
This is a convenience method. The result of calling
:meth:`pyramid.request.Request.route_path` is the same as calling
:func:`pyramid.url.route_path` with an explicit ``request``
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index 69762fdb0..d9d0c2c97 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -411,8 +411,10 @@ class TestAuthTktCookieHelper(unittest.TestCase):
def test_identify_cookie_reissue(self):
import time
- plugin = self._makeOne('secret', timeout=5000, reissue_time=0)
- plugin.auth_tkt.timestamp = time.time()
+ plugin = self._makeOne('secret', timeout=10, reissue_time=0)
+ now = time.time()
+ plugin.auth_tkt.timestamp = now
+ plugin.now = now + 1
request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'})
result = plugin.identify(request)
self.failUnless(result)
diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py
index ded17cb33..0d8905f7c 100644
--- a/pyramid/tests/test_configuration.py
+++ b/pyramid/tests/test_configuration.py
@@ -2940,6 +2940,7 @@ class ConfiguratorTests(unittest.TestCase):
pyramid.tests)
def test_scan_integration(self):
+ import os
from zope.interface import alsoProvides
from pyramid.interfaces import IRequest
from pyramid.view import render_view_to_response
@@ -3011,8 +3012,12 @@ class ConfiguratorTests(unittest.TestCase):
result = render_view_to_response(ctx, req, 'another_stacked_class2')
self.assertEqual(result, 'another_stacked_class')
- self.assertRaises(TypeError,
- render_view_to_response, ctx, req, 'basemethod')
+ if not os.name.startswith('java'):
+ # on Jython, a class without an __init__ apparently accepts
+ # any number of arguments without raising a TypeError.
+
+ self.assertRaises(TypeError,
+ render_view_to_response, ctx, req, 'basemethod')
result = render_view_to_response(ctx, req, 'method1')
self.assertEqual(result, 'method1')
diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py
index 2deb5982c..3245d6302 100644
--- a/pyramid/tests/test_traversal.py
+++ b/pyramid/tests/test_traversal.py
@@ -522,6 +522,32 @@ class FindModelTests(unittest.TestCase):
self.assertEqual(root.wascontext, True)
self.assertEqual(root.request.environ['PATH_INFO'], '/')
+ def test_absolute_unicode_found(self):
+ # test for bug wiggy found in wild, traceback stack:
+ # root = u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF'
+ # wiggy's code: section=find_model(page, root)
+ # find_model L76: D = traverse(model, path)
+ # traverse L291: return traverser(request)
+ # __call__ line 568: vpath_tuple = traversal_path(vpath)
+ # lru_cached line 91: f(*arg)
+ # traversal_path line 443: path.encode('ascii')
+ # UnicodeEncodeError: 'ascii' codec can't encode characters in
+ # position 1-12: ordinal not in range(128)
+ #
+ # solution: encode string to ascii in pyramid.traversal.traverse
+ # before passing it along to webob as path_info
+ from pyramid.traversal import ModelGraphTraverser
+ unprintable = DummyContext()
+ root = DummyContext(unprintable)
+ unprintable.__parent__ = root
+ unprintable.__name__ = unicode(
+ '/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8')
+ root.__parent__ = None
+ root.__name__ = None
+ traverser = ModelGraphTraverser
+ self._registerTraverser(traverser)
+ result = self._callFUT(root, u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF')
+ self.assertEqual(result, unprintable)
class ModelPathTests(unittest.TestCase):
def _callFUT(self, model, *elements):
diff --git a/pyramid/traversal.py b/pyramid/traversal.py
index e928c33f7..fb73ad906 100644
--- a/pyramid/traversal.py
+++ b/pyramid/traversal.py
@@ -228,7 +228,7 @@ def traverse(model, path):
object supplied to the function as the ``model`` argument. If an
empty string is passed as ``path``, the ``model`` passed in will
be returned. Model path strings must be escaped in the following
- manner: each Unicode path segment must be encoded as UTF-8 and as
+ manner: each Unicode path segment must be encoded as UTF-8 and
each path segment must escaped via Python's :mod:`urllib.quote`.
For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or
``to%20the/La%20Pe%C3%B1a`` (relative). The
@@ -272,6 +272,17 @@ def traverse(model, path):
else:
path = ''
+ # The user is supposed to pass us a string object, never Unicode. In
+ # practice, however, users indeed pass Unicode to this API. If they do
+ # pass a Unicode object, its data *must* be entirely encodeable to ASCII,
+ # so we encode it here as a convenience to the user and to prevent
+ # second-order failures from cropping up (all failures will occur at this
+ # step rather than later down the line as the result of calling
+ # ``traversal_path``).
+
+ if isinstance(path, unicode):
+ path = path.encode('ascii')
+
if path and path[0] == '/':
model = find_root(model)
diff --git a/setup.py b/setup.py
index c2d11384f..c1939f73c 100644
--- a/setup.py
+++ b/setup.py
@@ -47,7 +47,7 @@ install_requires=[
if platform.system() == 'Java':
tests_require = install_requires + ['WebTest']
else:
- tests_require= install_requires + ['Sphinx', 'docutils', 'coverage',
+ tests_require= install_requires + ['Sphinx', 'docutils',
'WebTest', 'repoze.sphinx.autointerface']
if sys.version_info[:2] < (2, 6):