Django

Code

Ticket #2977: reverse_urlresolver.patch

File reverse_urlresolver.patch, 5.3 kB (added by SmileyChris, 2 years ago)
  • django/core/urlresolvers.py

    old new  
    1111from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist 
    1212import re 
    1313 
     14# Set up these regular expressions outside the function so they only have to 
     15# be compiled once. 
     16re_parenthesis = re.compile(r'(?<!\\)\((.*)(?<!\\)\)')  # Slashes handle the rare possibility of escaped brackets. 
     17re_named_group = re.compile(r'\?P<(\w+)>(.*)$') 
     18re_unused = re.compile(r'(?<!\\)\$|[?*+^]') 
     19re_special = re.compile(r'\\[.+*()$]')  # Characters from the IETF URL standard, RFC 1738. 
     20 
    1421class Resolver404(Http404): 
    1522    pass 
    1623 
     
    3946 
    4047    Raises NoReverseMatch if the args/kwargs aren't valid for the regex. 
    4148    """ 
    42     # TODO: Handle nested parenthesis in the following regex. 
    43     result = re.sub(r'\(([^)]+)\)', MatchChecker(args, kwargs), regex.pattern) 
    44     return result.replace('^', '').replace('$', '') 
     49    # Recursion is not necessary because the outermost matched parenthesis 
     50    # will be replaced with the given argument. 
     51    use_named_groups = bool(re_named_group.search(regex.pattern)) 
     52    match_checker = MatchChecker(args, kwargs, use_named_groups) 
     53    result = re_parenthesis.sub(match_checker, regex.pattern) 
     54    # Strip unused regular expression syntax. 
     55    result = re_unused.sub('', result) 
     56    # Unescape special characters which could possibly be used in a URL. 
     57    result = re_special.sub('', result) 
     58    return result 
    4559 
    4660class MatchChecker(object): 
    4761    "Class used in reverse RegexURLPattern lookup." 
    48     def __init__(self, args, kwargs): 
     62    def __init__(self, args, kwargs, use_named_groups): 
    4963        self.args, self.kwargs = args, kwargs 
    5064        self.current_arg = 0 
     65        self.use_named_groups = use_named_groups 
    5166 
    5267    def __call__(self, match_obj): 
    5368        # match_obj.group(1) is the contents of the parenthesis. 
    5469        # First we need to figure out whether it's a named or unnamed group. 
    5570        # 
    5671        grouped = match_obj.group(1) 
    57         m = re.search(r'^\?P<(\w+)>(.*?)$', grouped) 
    58         if m: # If this was a named group... 
    59             # m.group(1) is the name of the group 
    60             # m.group(2) is the regex. 
    61             try: 
    62                 value = self.kwargs[m.group(1)] 
    63             except KeyError: 
    64                 # It was a named group, but the arg was passed in as a 
    65                 # positional arg or not at all. 
     72 
     73        # Handle regular expression extension notation. 
     74        if grouped.startswith('?'): 
     75            grouped = grouped[1:] 
     76            if grouped.startswith(':'): 
     77                # Parse the contents of this non-grouping parenthesis. 
     78                value = re_parenthesis.sub(self, grouped[1:]) 
     79                value = str(value) # TODO: Unicode? 
     80                return handle_pipe(value) 
     81            elif grouped.startswith('P'):  
     82                # This is a named group. 
     83                pass 
     84            else: 
     85                # Ignore the all other types of extension notation. 
     86                return '' 
     87         
     88        # If there is a named group in this regex, only parse named groups 
     89        # ignoring non-named arguments. 
     90        if self.use_named_groups: 
     91            if m: # If this was a named group... 
     92                # m.group(1) is the name of the group 
     93                # m.group(2) is the regex. 
    6694                try: 
    67                     value = self.args[self.current_arg] 
    68                     self.current_arg += 1 
    69                 except IndexError: 
    70                     # The arg wasn't passed in. 
    71                     raise NoReverseMatch('Not enough positional arguments passed in') 
    72             test_regex = m.group(2) 
    73         else: # Otherwise, this was a positional (unnamed) group. 
     95                    value = self.kwargs[m.group(1)] 
     96                except KeyError: 
     97                    # It was a named group, but the arg was passed in as a 
     98                    # positional arg or not at all. 
     99                    try: 
     100                        value = self.args[self.current_arg] 
     101                        self.current_arg += 1 
     102                    except IndexError: 
     103                        # The arg wasn't passed in. 
     104                        raise NoReverseMatch('Not enough positional arguments passed in') 
     105                test_regex = m.group(2) 
     106        else: 
    74107            try: 
    75108                value = self.args[self.current_arg] 
    76109                self.current_arg += 1 
     
    82115        # to string needs to match. 
    83116        if not re.match(test_regex + '$', str(value)): # TODO: Unicode? 
    84117            raise NoReverseMatch("Value %r didn't match regular expression %r" % (value, test_regex)) 
    85         return str(value) # TODO: Unicode? 
     118        value = str(value) # TODO: Unicode? 
     119        return handle_pipe(value) 
    86120 
     121def handle_pipe(value): 
     122    # Check for pipes (used in regular expressions for alternate matches). 
     123    # Since the matched expression can not be determined, the first one will 
     124    # be always used. 
     125    pipe = value.find('|') 
     126    if pipe != -1: 
     127        value = value[:pipe] 
     128    return value 
     129 
    87130class RegexURLPattern(object): 
    88131    def __init__(self, regex, callback, default_args=None): 
    89132        # regex is a string representing a regular expression.