summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-11-19 02:21:37 +0000
committerChris McDonough <chrism@agendaless.com>2008-11-19 02:21:37 +0000
commit65dcf305794feffb1da33c15b5af8d4964580d72 (patch)
tree0ebad53fed1b951458aabda0741e71bcaeb4a17d
parent5483348a58f8c3adcc2dec7b2e6b188a588c04cf (diff)
downloadpyramid-65dcf305794feffb1da33c15b5af8d4964580d72.tar.gz
pyramid-65dcf305794feffb1da33c15b5af8d4964580d72.tar.bz2
pyramid-65dcf305794feffb1da33c15b5af8d4964580d72.zip
- Fix ModelGraphTraverser; don't try to change the ``__name__`` or
``__parent__`` of an object that claims it implements ILocation during traversal even if the ``__name__`` or ``__parent__`` of the object traversed does not match the name used in the traversal step or the or the traversal parent . Rationale: it was insane to do so. This bug was only found due to a misconfiguration in an application that mistakenly had intermediate persistent non-ILocation objects; traversal was causing a persistent write on every request under this setup. - ``repoze.bfg.location.locate`` now unconditionally sets ``__name__`` and ``__parent__`` on objects which provide ILocation (it previously only set them conditionally if they didn't match attributes already present on the object via equality). Prep for 0.5.0.
-rw-r--r--CHANGES.txt17
-rw-r--r--docs/conf.py4
-rw-r--r--repoze/bfg/location.py16
-rw-r--r--repoze/bfg/tests/test_traversal.py43
-rw-r--r--repoze/bfg/traversal.py9
-rw-r--r--setup.py2
6 files changed, 68 insertions, 23 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 33c924c2a..7e8cacdab 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,20 @@
+0.5.0 (11/18/2008)
+
+ - Fix ModelGraphTraverser; don't try to change the ``__name__`` or
+ ``__parent__`` of an object that claims it implements ILocation
+ during traversal even if the ``__name__`` or ``__parent__`` of the
+ object traversed does not match the name used in the traversal
+ step or the or the traversal parent . Rationale: it was insane to
+ do so. This bug was only found due to a misconfiguration in an
+ application that mistakenly had intermediate persistent
+ non-ILocation objects; traversal was causing a persistent write on
+ every request under this setup.
+
+ - ``repoze.bfg.location.locate`` now unconditionally sets
+ ``__name__`` and ``__parent__`` on objects which provide ILocation
+ (it previously only set them conditionally if they didn't match
+ attributes already present on the object via equality).
+
0.4.9 (11/17/2008)
- Add chameleon text template API (chameleon ${name} renderings
diff --git a/docs/conf.py b/docs/conf.py
index 7d3991698..d4a4bcf83 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ copyright = '2008, Agendaless Consulting'
# other places throughout the built documents.
#
# The short X.Y version.
-version = '0.4.9'
+version = '0.5.0'
# The full version, including alpha/beta/rc tags.
-release = '0.4.9'
+release = '0.5.0'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
diff --git a/repoze/bfg/location.py b/repoze/bfg/location.py
index 11f08faf9..ed7d2ad1f 100644
--- a/repoze/bfg/location.py
+++ b/repoze/bfg/location.py
@@ -39,10 +39,10 @@ def inside(model1, model2):
def locate(model, parent, name=None):
"""
If ``model`` explicitly provides the
- ``repoze.bfg.interfaces.ILocation`` interface, locate ``model``
- directly set ``model`` 's ``__parent__`` attribute to the
- ``parent`` object (also a model), and its ``__name__`` to the
- supplied ``name`` argument.
+ ``repoze.bfg.interfaces.ILocation`` interface, directly set
+ ``model`` 's ``__parent__`` attribute to the ``parent`` object
+ (also a model), and its ``__name__`` to the supplied ``name``
+ argument, and return the model.
If ``model`` does *not* explicitly provide the
``repoze.bfg.interfaces.ILocation`` interface, return a
@@ -53,8 +53,8 @@ def locate(model, parent, name=None):
instances.
"""
if ILocation.providedBy(model):
- if parent is not model.__parent__ or name != model.__name__:
- _locate(model, parent, name)
+ model.__parent__ = parent
+ model.__name__ = name
return model
return LocationProxy(model, parent, name)
@@ -83,10 +83,6 @@ def lineage(model):
yield model
model = getattr(model, '__parent__', None)
-def _locate(model, parent, name=None):
- model.__parent__ = parent
- model.__name__ = name
-
class ClassAndInstanceDescr(object):
def __init__(self, *args):
diff --git a/repoze/bfg/tests/test_traversal.py b/repoze/bfg/tests/test_traversal.py
index 90e64dc97..2c02d7d63 100644
--- a/repoze/bfg/tests/test_traversal.py
+++ b/repoze/bfg/tests/test_traversal.py
@@ -101,34 +101,67 @@ class ModelGraphTraverserTests(unittest.TestCase, PlacelessSetup):
self.assertEqual(name, 'foo')
self.assertEqual(subpath, [])
- def test_call_with_ILocation_root(self):
+ def test_call_with_ILocation_root_proxies(self):
baz = DummyContext()
bar = DummyContext(baz)
foo = DummyContext(bar)
root = DummyContext(foo)
from zope.interface import directlyProvides
from repoze.bfg.interfaces import ILocation
+ from zope.proxy import isProxy
directlyProvides(root, ILocation)
root.__name__ = None
root.__parent__ = None
- # give bar a direct parent and name to mix things up a bit
- bar.__name__ = 'bar'
- bar.__parent__ = foo
policy = self._makeOne(root)
environ = self._getEnviron(PATH_INFO='/foo/bar/baz')
ctx, name, subpath = policy(environ)
- self.assertEqual(ctx, baz)
self.assertEqual(name, '')
self.assertEqual(subpath, [])
+ self.assertEqual(ctx, baz)
+ self.failUnless(isProxy(ctx))
self.assertEqual(ctx.__name__, 'baz')
self.assertEqual(ctx.__parent__, bar)
+ self.failUnless(isProxy(ctx.__parent__))
self.assertEqual(ctx.__parent__.__name__, 'bar')
self.assertEqual(ctx.__parent__.__parent__, foo)
+ self.failUnless(isProxy(ctx.__parent__.__parent__))
self.assertEqual(ctx.__parent__.__parent__.__name__, 'foo')
self.assertEqual(ctx.__parent__.__parent__.__parent__, root)
+ self.failIf(isProxy(ctx.__parent__.__parent__.__parent__))
self.assertEqual(ctx.__parent__.__parent__.__parent__.__name__, None)
self.assertEqual(ctx.__parent__.__parent__.__parent__.__parent__, None)
+ def test_call_with_ILocation_root_proxies_til_next_ILocation(self):
+ # This is a test of an insane setup; it tests the case where
+ # intermediate objects (foo and bar) do not implement
+ # ILocation, and so are returned as proxies to the traverser,
+ # but when we reach the "baz" object, it *does* implement
+ # ILocation, and its parent should be the *real* "bar" object
+ # rather than the proxied bar.
+ from zope.interface import directlyProvides
+ from repoze.bfg.interfaces import ILocation
+ baz = DummyContext()
+ directlyProvides(baz, ILocation)
+ baz.__name__ = 'baz'
+ bar = DummyContext(baz)
+ baz.__parent__ = bar
+ foo = DummyContext(bar)
+ root = DummyContext(foo)
+ from zope.proxy import isProxy
+ directlyProvides(root, ILocation)
+ root.__name__ = None
+ root.__parent__ = None
+ policy = self._makeOne(root)
+ environ = self._getEnviron(PATH_INFO='/foo/bar/baz')
+ ctx, name, subpath = policy(environ)
+ self.assertEqual(name, '')
+ self.assertEqual(subpath, [])
+ self.assertEqual(ctx, baz)
+ self.failIf(isProxy(ctx))
+ self.assertEqual(ctx.__name__, 'baz')
+ self.assertEqual(ctx.__parent__, bar)
+ self.failIf(isProxy(ctx.__parent__))
+
class FindInterfaceTests(unittest.TestCase):
def _callFUT(self, context, iface):
from repoze.bfg.traversal import find_interface
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py
index 8c2df6a41..f6a415b85 100644
--- a/repoze/bfg/traversal.py
+++ b/repoze/bfg/traversal.py
@@ -3,7 +3,7 @@ import urlparse
from zope.interface import classProvides
from zope.interface import implements
-from repoze.bfg.location import locate
+from repoze.bfg.location import LocationProxy
from repoze.bfg.location import lineage
from repoze.bfg.interfaces import ILocation
@@ -48,9 +48,8 @@ class ModelGraphTraverser(object):
def __call__(self, environ):
path = environ.get('PATH_INFO', '/')
path = split_path(path)
- root = self.root
-
ob = self.root
+
name = ''
while path:
@@ -59,8 +58,8 @@ class ModelGraphTraverser(object):
if next is _marker:
name = segment
break
- if self.locatable:
- next = locate(next, ob, segment)
+ if (self.locatable) and (not ILocation.providedBy(next)):
+ next = LocationProxy(next, ob, segment)
ob = next
return ob, name, path
diff --git a/setup.py b/setup.py
index 2f86fd249..fd559a3ca 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@
#
##############################################################################
-__version__ = '0.4.9'
+__version__ = '0.5.0'
import os