prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Incorrect parsing of complex urls in django
  Sentry is parsing a complex URL as `/api/{version})/log` instead of `/api/{version}/log`.
  
  <img width="207" alt="Screenshot 2020-10-17 at 10 40 47 AM" src="https://user-images.githubusercontent.com/4463796/96328987-70cb1c80-1066-11eb-94a4-ff8e15fb81ed.png">
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `sentry_sdk/integrations/django/transactions.py`
  Content:
  ```
  1 """
  2 Copied from raven-python. Used for
  3 `DjangoIntegration(transaction_fron="raven_legacy")`.
  4 """
  5 
  6 from __future__ import absolute_import
  7 
  8 import re
  9 
  10 from sentry_sdk._types import MYPY
  11 
  12 if MYPY:
  13     from django.urls.resolvers import URLResolver
  14     from typing import Dict
  15     from typing import List
  16     from typing import Optional
  17     from django.urls.resolvers import URLPattern
  18     from typing import Tuple
  19     from typing import Union
  20     from re import Pattern
  21 
  22 try:
  23     from django.urls import get_resolver
  24 except ImportError:
  25     from django.core.urlresolvers import get_resolver
  26 
  27 
  28 def get_regex(resolver_or_pattern):
  29     # type: (Union[URLPattern, URLResolver]) -> Pattern[str]
  30     """Utility method for django's deprecated resolver.regex"""
  31     try:
  32         regex = resolver_or_pattern.regex
  33     except AttributeError:
  34         regex = resolver_or_pattern.pattern.regex
  35     return regex
  36 
  37 
  38 class RavenResolver(object):
  39     _optional_group_matcher = re.compile(r"\(\?\:([^\)]+)\)")
  40     _named_group_matcher = re.compile(r"\(\?P<(\w+)>[^\)]+\)")
  41     _non_named_group_matcher = re.compile(r"\([^\)]+\)")
  42     # [foo|bar|baz]
  43     _either_option_matcher = re.compile(r"\[([^\]]+)\|([^\]]+)\]")
  44     _camel_re = re.compile(r"([A-Z]+)([a-z])")
  45 
  46     _cache = {}  # type: Dict[URLPattern, str]
  47 
  48     def _simplify(self, pattern):
  49         # type: (str) -> str
  50         r"""
  51         Clean up urlpattern regexes into something readable by humans:
  52 
  53         From:
  54         > "^(?P<sport_slug>\w+)/athletes/(?P<athlete_slug>\w+)/$"
  55 
  56         To:
  57         > "{sport_slug}/athletes/{athlete_slug}/"
  58         """
  59         # remove optional params
  60         # TODO(dcramer): it'd be nice to change these into [%s] but it currently
  61         # conflicts with the other rules because we're doing regexp matches
  62         # rather than parsing tokens
  63         result = self._optional_group_matcher.sub(lambda m: "%s" % m.group(1), pattern)
  64 
  65         # handle named groups first
  66         result = self._named_group_matcher.sub(lambda m: "{%s}" % m.group(1), result)
  67 
  68         # handle non-named groups
  69         result = self._non_named_group_matcher.sub("{var}", result)
  70 
  71         # handle optional params
  72         result = self._either_option_matcher.sub(lambda m: m.group(1), result)
  73 
  74         # clean up any outstanding regex-y characters.
  75         result = (
  76             result.replace("^", "")
  77             .replace("$", "")
  78             .replace("?", "")
  79             .replace("//", "/")
  80             .replace("\\", "")
  81         )
  82 
  83         return result
  84 
  85     def _resolve(self, resolver, path, parents=None):
  86         # type: (URLResolver, str, Optional[List[URLResolver]]) -> Optional[str]
  87 
  88         match = get_regex(resolver).search(path)  # Django < 2.0
  89 
  90         if not match:
  91             return None
  92 
  93         if parents is None:
  94             parents = [resolver]
  95         elif resolver not in parents:
  96             parents = parents + [resolver]
  97 
  98         new_path = path[match.end() :]
  99         for pattern in resolver.url_patterns:
  100             # this is an include()
  101             if not pattern.callback:
  102                 match_ = self._resolve(pattern, new_path, parents)
  103                 if match_:
  104                     return match_
  105                 continue
  106             elif not get_regex(pattern).search(new_path):
  107                 continue
  108 
  109             try:
  110                 return self._cache[pattern]
  111             except KeyError:
  112                 pass
  113 
  114             prefix = "".join(self._simplify(get_regex(p).pattern) for p in parents)
  115             result = prefix + self._simplify(get_regex(pattern).pattern)
  116             if not result.startswith("/"):
  117                 result = "/" + result
  118             self._cache[pattern] = result
  119             return result
  120 
  121         return None
  122 
  123     def resolve(
  124         self,
  125         path,  # type: str
  126         urlconf=None,  # type: Union[None, Tuple[URLPattern, URLPattern, URLResolver], Tuple[URLPattern]]
  127     ):
  128         # type: (...) -> str
  129         resolver = get_resolver(urlconf)
  130         match = self._resolve(resolver, path)
  131         return match or path
  132 
  133 
  134 LEGACY_RESOLVER = RavenResolver()
  135
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/sentry_sdk/integrations/django/transactions.py b/sentry_sdk/integrations/django/transactions.py
  --- a/sentry_sdk/integrations/django/transactions.py
  +++ b/sentry_sdk/integrations/django/transactions.py
  @@ -37,7 +37,7 @@
   
   class RavenResolver(object):
       _optional_group_matcher = re.compile(r"\(\?\:([^\)]+)\)")
  -    _named_group_matcher = re.compile(r"\(\?P<(\w+)>[^\)]+\)")
  +    _named_group_matcher = re.compile(r"\(\?P<(\w+)>[^\)]+\)+")
       _non_named_group_matcher = re.compile(r"\([^\)]+\)")
       # [foo|bar|baz]
       _either_option_matcher = re.compile(r"\[([^\]]+)\|([^\]]+)\]")
  
verification_info: {"golden_diff": "diff --git a/sentry_sdk/integrations/django/transactions.py b/sentry_sdk/integrations/django/transactions.py\n--- a/sentry_sdk/integrations/django/transactions.py\n+++ b/sentry_sdk/integrations/django/transactions.py\n@@ -37,7 +37,7 @@\n \n class RavenResolver(object):\n     _optional_group_matcher = re.compile(r\"\\(\\?\\:([^\\)]+)\\)\")\n-    _named_group_matcher = re.compile(r\"\\(\\?P<(\\w+)>[^\\)]+\\)\")\n+    _named_group_matcher = re.compile(r\"\\(\\?P<(\\w+)>[^\\)]+\\)+\")\n     _non_named_group_matcher = re.compile(r\"\\([^\\)]+\\)\")\n     # [foo|bar|baz]\n     _either_option_matcher = re.compile(r\"\\[([^\\]]+)\\|([^\\]]+)\\]\")\n", "issue": "Incorrect parsing of complex urls in django\nSentry is parsing a complex URL as `/api/{version})/log` instead of `/api/{version}/log`.\r\n\r\n<img width=\"207\" alt=\"Screenshot 2020-10-17 at 10 40 47 AM\" src=\"https://user-images.githubusercontent.com/4463796/96328987-70cb1c80-1066-11eb-94a4-ff8e15fb81ed.png\">\r\n\n", "before_files": [{"content": "\"\"\"\nCopied from raven-python. Used for\n`DjangoIntegration(transaction_fron=\"raven_legacy\")`.\n\"\"\"\n\nfrom __future__ import absolute_import\n\nimport re\n\nfrom sentry_sdk._types import MYPY\n\nif MYPY:\n    from django.urls.resolvers import URLResolver\n    from typing import Dict\n    from typing import List\n    from typing import Optional\n    from django.urls.resolvers import URLPattern\n    from typing import Tuple\n    from typing import Union\n    from re import Pattern\n\ntry:\n    from django.urls import get_resolver\nexcept ImportError:\n    from django.core.urlresolvers import get_resolver\n\n\ndef get_regex(resolver_or_pattern):\n    # type: (Union[URLPattern, URLResolver]) -> Pattern[str]\n    \"\"\"Utility method for django's deprecated resolver.regex\"\"\"\n    try:\n        regex = resolver_or_pattern.regex\n    except AttributeError:\n        regex = resolver_or_pattern.pattern.regex\n    return regex\n\n\nclass RavenResolver(object):\n    _optional_group_matcher = re.compile(r\"\\(\\?\\:([^\\)]+)\\)\")\n    _named_group_matcher = re.compile(r\"\\(\\?P<(\\w+)>[^\\)]+\\)\")\n    _non_named_group_matcher = re.compile(r\"\\([^\\)]+\\)\")\n    # [foo|bar|baz]\n    _either_option_matcher = re.compile(r\"\\[([^\\]]+)\\|([^\\]]+)\\]\")\n    _camel_re = re.compile(r\"([A-Z]+)([a-z])\")\n\n    _cache = {}  # type: Dict[URLPattern, str]\n\n    def _simplify(self, pattern):\n        # type: (str) -> str\n        r\"\"\"\n        Clean up urlpattern regexes into something readable by humans:\n\n        From:\n        > \"^(?P<sport_slug>\\w+)/athletes/(?P<athlete_slug>\\w+)/$\"\n\n        To:\n        > \"{sport_slug}/athletes/{athlete_slug}/\"\n        \"\"\"\n        # remove optional params\n        # TODO(dcramer): it'd be nice to change these into [%s] but it currently\n        # conflicts with the other rules because we're doing regexp matches\n        # rather than parsing tokens\n        result = self._optional_group_matcher.sub(lambda m: \"%s\" % m.group(1), pattern)\n\n        # handle named groups first\n        result = self._named_group_matcher.sub(lambda m: \"{%s}\" % m.group(1), result)\n\n        # handle non-named groups\n        result = self._non_named_group_matcher.sub(\"{var}\", result)\n\n        # handle optional params\n        result = self._either_option_matcher.sub(lambda m: m.group(1), result)\n\n        # clean up any outstanding regex-y characters.\n        result = (\n            result.replace(\"^\", \"\")\n            .replace(\"$\", \"\")\n            .replace(\"?\", \"\")\n            .replace(\"//\", \"/\")\n            .replace(\"\\\\\", \"\")\n        )\n\n        return result\n\n    def _resolve(self, resolver, path, parents=None):\n        # type: (URLResolver, str, Optional[List[URLResolver]]) -> Optional[str]\n\n        match = get_regex(resolver).search(path)  # Django < 2.0\n\n        if not match:\n            return None\n\n        if parents is None:\n            parents = [resolver]\n        elif resolver not in parents:\n            parents = parents + [resolver]\n\n        new_path = path[match.end() :]\n        for pattern in resolver.url_patterns:\n            # this is an include()\n            if not pattern.callback:\n                match_ = self._resolve(pattern, new_path, parents)\n                if match_:\n                    return match_\n                continue\n            elif not get_regex(pattern).search(new_path):\n                continue\n\n            try:\n                return self._cache[pattern]\n            except KeyError:\n                pass\n\n            prefix = \"\".join(self._simplify(get_regex(p).pattern) for p in parents)\n            result = prefix + self._simplify(get_regex(pattern).pattern)\n            if not result.startswith(\"/\"):\n                result = \"/\" + result\n            self._cache[pattern] = result\n            return result\n\n        return None\n\n    def resolve(\n        self,\n        path,  # type: str\n        urlconf=None,  # type: Union[None, Tuple[URLPattern, URLPattern, URLResolver], Tuple[URLPattern]]\n    ):\n        # type: (...) -> str\n        resolver = get_resolver(urlconf)\n        match = self._resolve(resolver, path)\n        return match or path\n\n\nLEGACY_RESOLVER = RavenResolver()\n", "path": "sentry_sdk/integrations/django/transactions.py"}], "after_files": [{"content": "\"\"\"\nCopied from raven-python. Used for\n`DjangoIntegration(transaction_fron=\"raven_legacy\")`.\n\"\"\"\n\nfrom __future__ import absolute_import\n\nimport re\n\nfrom sentry_sdk._types import MYPY\n\nif MYPY:\n    from django.urls.resolvers import URLResolver\n    from typing import Dict\n    from typing import List\n    from typing import Optional\n    from django.urls.resolvers import URLPattern\n    from typing import Tuple\n    from typing import Union\n    from re import Pattern\n\ntry:\n    from django.urls import get_resolver\nexcept ImportError:\n    from django.core.urlresolvers import get_resolver\n\n\ndef get_regex(resolver_or_pattern):\n    # type: (Union[URLPattern, URLResolver]) -> Pattern[str]\n    \"\"\"Utility method for django's deprecated resolver.regex\"\"\"\n    try:\n        regex = resolver_or_pattern.regex\n    except AttributeError:\n        regex = resolver_or_pattern.pattern.regex\n    return regex\n\n\nclass RavenResolver(object):\n    _optional_group_matcher = re.compile(r\"\\(\\?\\:([^\\)]+)\\)\")\n    _named_group_matcher = re.compile(r\"\\(\\?P<(\\w+)>[^\\)]+\\)+\")\n    _non_named_group_matcher = re.compile(r\"\\([^\\)]+\\)\")\n    # [foo|bar|baz]\n    _either_option_matcher = re.compile(r\"\\[([^\\]]+)\\|([^\\]]+)\\]\")\n    _camel_re = re.compile(r\"([A-Z]+)([a-z])\")\n\n    _cache = {}  # type: Dict[URLPattern, str]\n\n    def _simplify(self, pattern):\n        # type: (str) -> str\n        r\"\"\"\n        Clean up urlpattern regexes into something readable by humans:\n\n        From:\n        > \"^(?P<sport_slug>\\w+)/athletes/(?P<athlete_slug>\\w+)/$\"\n\n        To:\n        > \"{sport_slug}/athletes/{athlete_slug}/\"\n        \"\"\"\n        # remove optional params\n        # TODO(dcramer): it'd be nice to change these into [%s] but it currently\n        # conflicts with the other rules because we're doing regexp matches\n        # rather than parsing tokens\n        result = self._optional_group_matcher.sub(lambda m: \"%s\" % m.group(1), pattern)\n\n        # handle named groups first\n        result = self._named_group_matcher.sub(lambda m: \"{%s}\" % m.group(1), result)\n\n        # handle non-named groups\n        result = self._non_named_group_matcher.sub(\"{var}\", result)\n\n        # handle optional params\n        result = self._either_option_matcher.sub(lambda m: m.group(1), result)\n\n        # clean up any outstanding regex-y characters.\n        result = (\n            result.replace(\"^\", \"\")\n            .replace(\"$\", \"\")\n            .replace(\"?\", \"\")\n            .replace(\"//\", \"/\")\n            .replace(\"\\\\\", \"\")\n        )\n\n        return result\n\n    def _resolve(self, resolver, path, parents=None):\n        # type: (URLResolver, str, Optional[List[URLResolver]]) -> Optional[str]\n\n        match = get_regex(resolver).search(path)  # Django < 2.0\n\n        if not match:\n            return None\n\n        if parents is None:\n            parents = [resolver]\n        elif resolver not in parents:\n            parents = parents + [resolver]\n\n        new_path = path[match.end() :]\n        for pattern in resolver.url_patterns:\n            # this is an include()\n            if not pattern.callback:\n                match_ = self._resolve(pattern, new_path, parents)\n                if match_:\n                    return match_\n                continue\n            elif not get_regex(pattern).search(new_path):\n                continue\n\n            try:\n                return self._cache[pattern]\n            except KeyError:\n                pass\n\n            prefix = \"\".join(self._simplify(get_regex(p).pattern) for p in parents)\n            result = prefix + self._simplify(get_regex(pattern).pattern)\n            if not result.startswith(\"/\"):\n                result = \"/\" + result\n            self._cache[pattern] = result\n            return result\n\n        return None\n\n    def resolve(\n        self,\n        path,  # type: str\n        urlconf=None,  # type: Union[None, Tuple[URLPattern, URLPattern, URLResolver], Tuple[URLPattern]]\n    ):\n        # type: (...) -> str\n        resolver = get_resolver(urlconf)\n        match = self._resolve(resolver, path)\n        return match or path\n\n\nLEGACY_RESOLVER = RavenResolver()\n", "path": "sentry_sdk/integrations/django/transactions.py"}]}