Commits

Andriy Kornatskyy committed cd88cb6

Optimizing PathRouter.path_for

Comments (0)

Files changed (2)

src/wheezy/routing/router.py

 class PathRouter(object):
     """
     """
-    __slots__ = ('mapping', 'route_map', 'routers', 'route_builders')
+    __slots__ = ('mapping', 'route_map', 'inner_route_map', 'route_builders')
 
     def __init__(self, route_builders=None):
         """
         """
         self.mapping = []
         self.route_map = {}
-        self.routers = []
+        self.inner_route_map = {}
         self.route_builders = route_builders or default_route_builders
 
     def add_route(self, pattern, handler, kwargs=None, name=None):
             >>> route, inner = r.mapping[0]
             >>> assert isinstance(inner, PathRouter)
             >>> r = PathRouter()
-            >>> r.include(r'admin/', PathRouter())
-            >>> assert r.routers
+            >>> inner = PathRouter()
+            >>> inner.add_routes([(r'login', Login)])
+            >>> r.include(r'admin/', inner)
+            >>> assert r.inner_route_map
+
+            If ``name`` already mapped to some route allow
+            override it but show a warning.
+
+            >>> import warnings
+            >>> warnings.simplefilter('ignore')
+            >>> r.include(r'admin/', admin_routes)
+            >>> top_router = PathRouter()
+            >>> r.include(r'x/', r)
+            >>> warnings.simplefilter('default')
         """
         # try build intermediate route
         route = build_route(pattern, False, kwargs, self.route_builders)
             inner = PathRouter(self.route_builders)
             inner.add_routes(included)
         self.mapping.append((route.match, inner))
-        self.routers.append((inner.path_for, route.path))
+        route_path = route.path
+        for name, path in inner.route_map.items():
+            if name in self.inner_route_map:
+                warn('PathRouter: overriding route: %s.' % name)
+            self.inner_route_map[name] = [route_path, path]
+        for name, paths in inner.inner_route_map.items():
+            if name in self.inner_route_map:
+                warn('PathRouter: overriding route: %s.' % name)
+            self.inner_route_map[name] = [route_path] + paths
 
     def add_routes(self, mapping):
         """ Adds routes represented as a list of tuple
             >>> r.add_routes([
             ...     (r'admin/', admin_routes)
             ... ])
-            >>> len(r.routers)
+            >>> len(r.inner_route_map)
             1
             >>> len(r.mapping)
             1
             ...     (r'{lang}/', admin_routes, {'lang': 'en'})
             ... ])
             >>> r.path_for(r'signin')
-            'en/'
+            'en'
 
             Otherwise None
 
         """
         if name in self.route_map:
             return self.route_map[name](kwargs).rstrip('/')
-        for inner_path_for, route_path in self.routers:
-            inner_path = inner_path_for(name, **kwargs)
-            if inner_path is not None:
-                return route_path(kwargs) + inner_path
-        return None
+        else:
+            try:
+                return ''.join([path(kwargs) for path
+                                in self.inner_route_map[name]]).rstrip('/')
+            except KeyError:
+                return None

src/wheezy/routing/tests/test_router.py

 
         assert isinstance(r.mapping, list)
         assert isinstance(r.route_map, dict)
-        assert isinstance(r.routers, list)
+        assert isinstance(r.inner_route_map, dict)
         assert r.route_builders is config.route_builders
 
     def test_init(self):
 
         assert isinstance(r.mapping, list)
         assert isinstance(r.route_map, dict)
-        assert isinstance(r.routers, list)
+        assert isinstance(r.inner_route_map, dict)
         assert r.route_builders is route_builders
 
 
 
         self.r.include('abc', [])
 
-        assert self.r.routers[0][0]
+        assert self.r.mapping[0]
 
 
 class PathRouterAddRoutesTestCase(unittest.TestCase):
         """ ``include`` call.
         """
         from wheezy.routing.router import PathRouter
-        for h in ([], (), PathRouter()):
+        router = PathRouter()
+        router.include('pattern', [('pattern', 'handler')])
+        for h in ([('pattern', 'handler')], (('pattern', 'handler'),), router):
             r = PathRouter()
             r.add_routes([('pattern', h, {})])
             assert r.mapping
-            assert r.routers
+            assert r.inner_route_map
 
 
 class PathRouterMatchTestCase(unittest.TestCase):
     def test_no_match(self):
         """ no match
         """
-        expect(self.mock_inner.path_for('n')).result(None)
+        expect(self.mock_inner.route_map).result({})
+        expect(self.mock_inner.inner_route_map).result({})
         self.m.replay()
 
         self.r.include('abc/', [])
     def test_match(self):
         """ match inner router
         """
-        expect(self.mock_inner.path_for('n')).result('de')
+        expect(self.mock_inner.route_map).result({'n': lambda kwargs: 'de'})
+        expect(self.mock_inner.inner_route_map).result({})
         self.m.replay()
 
         self.r.include('abc/', [])
 
         self.assertEquals('abc/de', p)
 
-    def test_match_first(self):
+    def test_match_last(self):
         """ match inner router
         """
         from wheezy.routing import router
         ).result(mock_inner2)
         expect(mock_inner2.add_routes([]))
 
-        expect(self.mock_inner.path_for('n')).result('de')
+        expect(self.mock_inner.route_map).result({'n': lambda kwargs: 'de'})
+        expect(self.mock_inner.inner_route_map).result({})
+        expect(mock_inner2.route_map).result({'n': lambda kwargs: 'de'})
+        expect(mock_inner2.inner_route_map).result({})
         self.m.replay()
 
         self.r.include('abc/', [])
         self.r.include('cbd/', [])
         p = self.r.path_for('n')
 
-        self.assertEquals('abc/de', p)
+        self.assertEquals('cbd/de', p)