summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Cramer <dcramer@gmail.com>2016-09-13 12:04:10 -0700
committerDavid Cramer <dcramer@gmail.com>2016-09-13 12:04:10 -0700
commit241a3bf7e952d834657b67ee3895c14611a6c86b (patch)
tree50e39bee064befd055fa1e73528653777acd7b6c
parent737b48d8e6e5ba53a9a53b4aaef86a2366a6c58b (diff)
downloadraven-transactions.tar.gz
Handle included patternstransactions
-rw-r--r--raven/contrib/django/resolver.py76
-rw-r--r--tests/contrib/django/test_resolver.py27
2 files changed, 69 insertions, 34 deletions
diff --git a/raven/contrib/django/resolver.py b/raven/contrib/django/resolver.py
index cd5c5c0..12d22e4 100644
--- a/raven/contrib/django/resolver.py
+++ b/raven/contrib/django/resolver.py
@@ -3,9 +3,9 @@ from __future__ import absolute_import
import re
try:
- from django.urls import get_resolver, Resolver404
+ from django.urls import get_resolver
except ImportError:
- from django.core.urlresolvers import get_resolver, Resolver404
+ from django.core.urlresolvers import get_resolver
class RouteResolver(object):
@@ -32,44 +32,58 @@ class RouteResolver(object):
# TODO(dcramer): it'd be nice to change these into [%s] but it currently
# conflicts with the other rules because we're doing regexp matches
# rather than parsing tokens
- pattern = self._optional_group_matcher.sub(lambda m: '%s' % m.group(1), pattern)
+ result = self._optional_group_matcher.sub(lambda m: '%s' % m.group(1), pattern)
# handle named groups first
- pattern = self._named_group_matcher.sub(lambda m: '{%s}' % m.group(1), pattern)
+ result = self._named_group_matcher.sub(lambda m: '{%s}' % m.group(1), result)
# handle non-named groups
- pattern = self._non_named_group_matcher.sub("{var}", pattern)
+ result = self._non_named_group_matcher.sub('{var}', result)
# handle optional params
- pattern = self._either_option_matcher.sub(lambda m: m.group(1), pattern)
+ result = self._either_option_matcher.sub(lambda m: m.group(1), result)
# clean up any outstanding regex-y characters.
- pattern = pattern.replace('^', '').replace('$', '') \
+ result = result.replace('^', '').replace('$', '') \
.replace('?', '').replace('//', '/').replace('\\', '')
- if not pattern.startswith('/'):
- pattern = '/' + pattern
- return pattern
+
+ return result
+
+ def _resolve(self, resolver, path, parents=None):
+ match = resolver.regex.search(path)
+ if not match:
+ return
+
+ if parents is None:
+ parents = [resolver]
+ else:
+ parents.append(resolver)
+
+ new_path = path[match.end():]
+ for pattern in resolver.url_patterns:
+ # this is an include()
+ if not pattern.callback:
+ match = self._resolve(pattern, new_path, parents)
+ if match:
+ return match
+ continue
+
+ elif not pattern.regex.search(new_path):
+ continue
+
+ try:
+ return self._cache[pattern]
+ except KeyError:
+ pass
+
+ prefix = ''.join(self._simplify(p.regex.pattern) for p in parents)
+ result = prefix + self._simplify(pattern.regex.pattern)
+ if not result.startswith('/'):
+ result = '/' + result
+ self._cache[pattern] = result
+ return result
def resolve(self, path, urlconf=None):
- # TODO(dcramer): it'd be nice to pull out parameters
- # and make this a normalized path
resolver = get_resolver(urlconf)
- match = resolver.regex.search(path)
- if match:
- new_path = path[match.end():]
- for pattern in resolver.url_patterns:
- try:
- sub_match = pattern.resolve(new_path)
- except Resolver404:
- continue
- if sub_match:
- pattern = pattern.regex.pattern
- try:
- return self._cache[pattern]
- except KeyError:
- pass
-
- pattern_name = self._simplify(pattern)
- self._cache[pattern] = pattern
- return pattern_name
- return path
+ match = self._resolve(resolver, path)
+ return match or path
diff --git a/tests/contrib/django/test_resolver.py b/tests/contrib/django/test_resolver.py
index 930d287..3960161 100644
--- a/tests/contrib/django/test_resolver.py
+++ b/tests/contrib/django/test_resolver.py
@@ -1,21 +1,42 @@
from __future__ import absolute_import
+try:
+ from django.conf.urls import url, include
+except ImportError:
+ # for Django version less than 1.4
+ from django.conf.urls.defaults import url, include # NOQA
+
from raven.contrib.django.resolver import RouteResolver
+included_url_conf = (
+ url(r'^foo/bar/(?P<param>[\w]+)', lambda x: ''),
+), '', ''
+
+example_url_conf = (
+ url(r'^api/(?P<project_id>[\w_-]+)/store/$', lambda x: ''),
+ url(r'^example/', include(included_url_conf)),
+)
+
def test_no_match():
resolver = RouteResolver()
- result = resolver.resolve('/foo/bar', 'raven.contrib.django.urls')
+ result = resolver.resolve('/foo/bar', example_url_conf)
assert result == '/foo/bar'
def test_simple_match():
resolver = RouteResolver()
- result = resolver.resolve('/report/', 'raven.contrib.django.urls')
+ result = resolver.resolve('/report/', example_url_conf)
assert result == '/report/'
def test_complex_match():
resolver = RouteResolver()
- result = resolver.resolve('/api/1234/store/', 'raven.contrib.django.urls')
+ result = resolver.resolve('/api/1234/store/', example_url_conf)
assert result == '/api/{project_id}/store/'
+
+
+def test_included_match():
+ resolver = RouteResolver()
+ result = resolver.resolve('/example/foo/bar/baz', example_url_conf)
+ assert result == '/example/foo/bar/{param}'