summaryrefslogtreecommitdiff
path: root/repoze
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-09-17 19:58:01 +0000
committerChris McDonough <chrism@agendaless.com>2009-09-17 19:58:01 +0000
commit750ce41f217cd7b638ad5b69fcb9df1b49841b58 (patch)
treea0a8edbc60694d3ae16b70570b0184a8ef28ee65 /repoze
parent19473e78e61ad084f07a0f7820a75b6c64d93dcd (diff)
downloadpyramid-750ce41f217cd7b638ad5b69fcb9df1b49841b58.tar.gz
pyramid-750ce41f217cd7b638ad5b69fcb9df1b49841b58.tar.bz2
pyramid-750ce41f217cd7b638ad5b69fcb9df1b49841b58.zip
- Add a ``repoze.bfg.url.static_url`` API which is capable of
generating URLs to static resources defined by the ``<static>`` ZCML directive. See the "Views" narrative chapter's section titled "Generating Static Resource URLs" for more information.
Diffstat (limited to 'repoze')
-rw-r--r--repoze/bfg/static.py7
-rw-r--r--repoze/bfg/tests/test_static.py7
-rw-r--r--repoze/bfg/tests/test_url.py65
-rw-r--r--repoze/bfg/tests/test_urldispatch.py9
-rw-r--r--repoze/bfg/tests/test_zcml.py11
-rw-r--r--repoze/bfg/url.py56
-rw-r--r--repoze/bfg/urldispatch.py3
-rw-r--r--repoze/bfg/zcml.py8
8 files changed, 150 insertions, 16 deletions
diff --git a/repoze/bfg/static.py b/repoze/bfg/static.py
index 01ce98a30..ef893875f 100644
--- a/repoze/bfg/static.py
+++ b/repoze/bfg/static.py
@@ -76,3 +76,10 @@ class PackageURLParser(StaticURLParser):
return '<%s %s:%s at %s>' % (self.__class__.__name__, self.package_name,
self.root_resource, id(self))
+class StaticRootFactory:
+ def __init__(self, spec):
+ self.spec = spec
+
+ def __call__(self, environ):
+ return self
+
diff --git a/repoze/bfg/tests/test_static.py b/repoze/bfg/tests/test_static.py
index 7c7b5627c..355afac2a 100644
--- a/repoze/bfg/tests/test_static.py
+++ b/repoze/bfg/tests/test_static.py
@@ -148,6 +148,13 @@ class TestPackageURLParser(unittest.TestCase):
self.failUnless('404 Not Found' in body)
self.assertEqual(sr.status, '404 Not Found')
+class TestStaticRootFactory(unittest.TestCase):
+ def test_it(self):
+ from repoze.bfg.static import StaticRootFactory
+ factory = StaticRootFactory('abc')
+ self.assertEqual(factory.spec, 'abc')
+ self.assertEqual(factory({}), factory)
+
class DummyStartResponse:
def __call__(self, status, headerlist, exc_info=None):
self.status = status
diff --git a/repoze/bfg/tests/test_url.py b/repoze/bfg/tests/test_url.py
index 5f0fde872..5833b8880 100644
--- a/repoze/bfg/tests/test_url.py
+++ b/repoze/bfg/tests/test_url.py
@@ -202,7 +202,62 @@ class TestRouteUrl(unittest.TestCase):
mapper.raise_exc = KeyError
request = DummyRequest()
self.assertRaises(KeyError, self._callFUT, 'flub', request, a=1)
+
+class TestStaticUrl(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+ def _callFUT(self, *arg, **kw):
+ from repoze.bfg.url import static_url
+ return static_url(*arg, **kw)
+
+ def test_notfound(self):
+ from repoze.bfg.interfaces import IRoutesMapper
+ from zope.component import getSiteManager
+ mapper = DummyRoutesMapper(result='/1/2/3')
+ sm = getSiteManager()
+ sm.registerUtility(mapper, IRoutesMapper)
+ request = DummyRequest()
+ self.assertRaises(ValueError, self._callFUT, 'static/foo.css', request)
+
+ def test_abspath(self):
+ from repoze.bfg.interfaces import IRoutesMapper
+ from zope.component import getSiteManager
+ mapper = DummyRoutesMapper(result='/1/2/3')
+ sm = getSiteManager()
+ sm.registerUtility(mapper, IRoutesMapper)
+ request = DummyRequest()
+ self.assertRaises(ValueError, self._callFUT, '/static/foo.css', request)
+
+ def test_found_rel(self):
+ from repoze.bfg.interfaces import IRoutesMapper
+ from repoze.bfg.static import StaticRootFactory
+ from zope.component import getSiteManager
+ factory = StaticRootFactory('repoze.bfg.tests:fixtures')
+ routes = [DummyRoute('name', factory=factory)]
+ mapper = DummyRoutesMapper(result='/1/2/3', routes = routes)
+ sm = getSiteManager()
+ sm.registerUtility(mapper, IRoutesMapper)
+ request = DummyRequest()
+ url = self._callFUT('fixtures/minimal.pt', request)
+ self.assertEqual(url, 'http://example.com:5432/1/2/3')
+
+ def test_found_abs(self):
+ from repoze.bfg.interfaces import IRoutesMapper
+ from repoze.bfg.static import StaticRootFactory
+ from zope.component import getSiteManager
+ factory = StaticRootFactory('repoze.bfg.tests:fixtures')
+ routes = [DummyRoute('name', factory=factory)]
+ mapper = DummyRoutesMapper(result='/1/2/3', routes = routes)
+ sm = getSiteManager()
+ sm.registerUtility(mapper, IRoutesMapper)
+ request = DummyRequest()
+ url = self._callFUT('repoze.bfg.tests:fixtures/minimal.pt', request)
+ self.assertEqual(url, 'http://example.com:5432/1/2/3')
+
class DummyContext(object):
def __init__(self, next=None):
self.next = next
@@ -216,11 +271,19 @@ class DummyRequest:
class DummyRoutesMapper:
raise_exc = None
- def __init__(self, result='/1/2/3', raise_exc=False):
+ def __init__(self, result='/1/2/3', raise_exc=False, routes=()):
self.result = result
+ self.routes = routes
+
+ def get_routes(self):
+ return self.routes
def generate(self, *route_args, **newargs):
if self.raise_exc:
raise self.raise_exc
return self.result
+class DummyRoute:
+ def __init__(self, name, factory=None):
+ self.name = name
+ self.factory = factory
diff --git a/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py
index 1de3d6f3b..faa375dbd 100644
--- a/repoze/bfg/tests/test_urldispatch.py
+++ b/repoze/bfg/tests/test_urldispatch.py
@@ -120,6 +120,15 @@ class RoutesRootFactoryTests(unittest.TestCase):
mapper.connect('whatever', 'archives/:action/:article')
self.assertEqual(mapper.has_routes(), True)
+ def test_get_routes(self):
+ from repoze.bfg.urldispatch import Route
+ mapper = self._makeOne(None)
+ self.assertEqual(mapper.get_routes(), [])
+ mapper.connect('whatever', 'archives/:action/:article')
+ routes = mapper.get_routes()
+ self.assertEqual(len(routes), 1)
+ self.assertEqual(routes[0].__class__, Route)
+
def test_generate(self):
mapper = self._makeOne(None)
def generator(kw):
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 7af9ab193..6161f6567 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -1848,7 +1848,7 @@ class TestStaticDirective(unittest.TestCase):
from zope.interface import implementedBy
from zope.component import getSiteManager
from repoze.bfg.zcml import connect_route
- from repoze.bfg.zcml import StaticRootFactory
+ from repoze.bfg.static import StaticRootFactory
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
import os
@@ -1887,7 +1887,7 @@ class TestStaticDirective(unittest.TestCase):
from zope.component import getSiteManager
from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
- from repoze.bfg.zcml import StaticRootFactory
+ from repoze.bfg.static import StaticRootFactory
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
context = DummyContext()
@@ -1922,7 +1922,7 @@ class TestStaticDirective(unittest.TestCase):
from zope.component import getSiteManager
from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
- from repoze.bfg.zcml import StaticRootFactory
+ from repoze.bfg.static import StaticRootFactory
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
import repoze.bfg.tests
@@ -2226,11 +2226,6 @@ class TestAll(unittest.TestCase):
self.assertEqual(all([False, False]), False)
self.assertEqual(all([False, True]), False)
-class TestStaticRootFactory(unittest.TestCase):
- def test_it(self):
- from repoze.bfg.zcml import StaticRootFactory
- StaticRootFactory({}) # it just needs construction
-
class DummyModule:
__path__ = "foo"
__name__ = "dummy"
diff --git a/repoze/bfg/url.py b/repoze/bfg/url.py
index dcaea67ff..f21334191 100644
--- a/repoze/bfg/url.py
+++ b/repoze/bfg/url.py
@@ -1,5 +1,6 @@
""" Utility functions for dealing with URLs in repoze.bfg """
+import os
import urllib
from zope.component import queryMultiAdapter
@@ -7,6 +8,8 @@ from zope.component import getUtility
from repoze.bfg.interfaces import IContextURL
from repoze.bfg.interfaces import IRoutesMapper
+from repoze.bfg.path import caller_package
+from repoze.bfg.static import StaticRootFactory
from repoze.bfg.traversal import TraversalContextURL
from repoze.bfg.traversal import quote_path_segment
@@ -29,9 +32,9 @@ def route_url(route_name, request, *elements, **kw):
route_url('foobar', request, foo='1') => <KeyError exception>
route_url('foobar', request, foo='1', bar='2') => <KeyError exception>
route_url('foobar', request, foo='1', bar='2',
- 'traverse=('a','b') => http://e.com/1/2/a/b
+ 'traverse=('a','b')) => http://e.com/1/2/a/b
route_url('foobar', request, foo='1', bar='2',
- 'traverse=('/a/b') => http://e.com/1/2/a/b
+ 'traverse=('/a/b')) => http://e.com/1/2/a/b
Values replacing ``:segment`` arguments can be passed as strings
or Unicode objects. They will be encoded to UTF-8 and URL-quoted
@@ -197,6 +200,55 @@ def model_url(model, request, *elements, **kw):
return model_url + suffix + qs + anchor
+def static_url(path, request, **kw):
+ """
+ Generates a fully qualified URL for a static resource. The
+ resource must live within a location defined via the ``<static>``
+ ZCML directive.
+
+ The ``path`` argument points at a file or directory on disk which
+ a URL should be generated for. The ``path`` may be either a
+ relative path (e.g. ``static/foo.css``) or a :term:`resource
+ specification` (e.g. ``mypackage:static/foo.css``). A ``path``
+ may not be an absolute filesystem path (a ValueError will be
+ raised if this function is supplied with an absolute path).
+
+ The ``request`` argument should be a WebOb request.
+
+ The purpose of the ``**kw`` argument is the same as the purpose of
+ the ``route_url`` ``*kw`` argument. See the documentation for
+ that function to understand the arguments which you can provide to
+ it. However, typically, you don't need to pass anything as
+ ``*kw`` when generating a static resource URL.
+
+ This function raises a ValueError if a ``<static>`` ZCML
+ definition cannot be found which matches the path specification.
+
+ .. note:: This feature is new in :mod:`repoze.bfg` 1.1.
+ """
+ mapper = getUtility(IRoutesMapper)
+ routes = mapper.get_routes()
+ if os.path.isabs(path):
+ raise ValueError('Absolute paths cannot be used to generate static '
+ 'urls (use a package-relative path or a resource '
+ 'specification).')
+ if not ':' in path:
+ # if it's not a package:relative/name and it's not an
+ # /absolute/path it's a relative/path; this means its relative
+ # to the package in which the caller's module is defined.
+ package = caller_package(level=2)
+ path = '%s:%s' % (package.__name__, path)
+
+ for route in routes:
+ factory = route.factory
+ if factory.__class__ is StaticRootFactory:
+ if path.startswith(factory.spec):
+ subpath = path[len(factory.spec):]
+ kw['subpath'] = subpath
+ return route_url(route.name, request, **kw)
+
+ raise ValueError('No static URL definition matching %s' % path)
+
def urlencode(query, doseq=False):
"""
A wrapper around Python's stdlib `urllib.urlencode function
diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py
index 747faace7..dfb7c9979 100644
--- a/repoze/bfg/urldispatch.py
+++ b/repoze/bfg/urldispatch.py
@@ -29,6 +29,9 @@ class RoutesRootFactory(object):
def has_routes(self):
return bool(self.routelist)
+ def get_routes(self):
+ return self.routelist
+
def connect(self, path, name, factory=None):
route = Route(path, name, factory)
self.routelist.append(route)
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index fd8e086a4..8ad0c3ec9 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -51,6 +51,8 @@ from repoze.bfg.security import Unauthorized
from repoze.bfg.settings import get_settings
+from repoze.bfg.static import StaticRootFactory
+
from repoze.bfg.traversal import find_interface
from repoze.bfg.view import static as static_view
@@ -611,10 +613,6 @@ class IStaticDirective(Interface):
required=False,
default=None)
-class StaticRootFactory:
- def __init__(self, environ):
- pass
-
def static(_context, name, path, cache_max_age=3600):
""" Handle ``static`` ZCML directives
"""
@@ -626,7 +624,7 @@ def static(_context, name, path, cache_max_age=3600):
view = static_view(path, cache_max_age=cache_max_age)
route(_context, name, "%s*subpath" % name, view=view,
- view_for=StaticRootFactory, factory=StaticRootFactory)
+ view_for=StaticRootFactory, factory=StaticRootFactory(path))
class IViewDirective(Interface):
for_ = GlobalObject(