Django

Code

Ticket #4566: reverse-speedup2.diff

File reverse-speedup2.diff, 4.4 kB (added by smoo, 1 year ago)

patch with above changes + cached callback lookup (reduces inititial overhead)

  • core/urlresolvers.py

    old new  
    1111from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist 
    1212import re 
    1313 
     14def cacheable(func, cache): 
     15    def wrapper(arg): 
     16        if arg in cache: 
     17            return cache[arg] 
     18         
     19        result = func(arg) 
     20        cache[arg] = result 
     21        return result 
     22    return wrapper 
     23 
     24resolver_cache = {} 
     25 
     26def get_resolver(urlconf): 
     27    if urlconf is None: 
     28        from django.conf import settings 
     29        urlconf = settings.ROOT_URLCONF 
     30    return RegexURLResolver(r'^/', urlconf) 
     31get_resolver = cacheable(get_resolver, resolver_cache) 
     32 
    1433class Resolver404(Http404): 
    1534    pass 
    1635 
     
    129148    def _get_callback(self): 
    130149        if self._callback is not None: 
    131150            return self._callback 
    132         mod_name, func_name = get_mod_func(self._callback_str) 
    133151        try: 
    134             self._callback = getattr(__import__(mod_name, {}, {}, ['']), func_name
     152            self._callback = make_callable(self._callback_str
    135153        except ImportError, e: 
    136154            raise ViewDoesNotExist, "Could not import %s. Error was: %s" % (mod_name, str(e)) 
    137155        except AttributeError, e: 
     
    160178        self.urlconf_name = urlconf_name 
    161179        self.callback = None 
    162180        self.default_kwargs = default_kwargs or {} 
     181        self.reverse_dict = {} 
     182         
     183        for pattern in reversed(self.urlconf_module.urlpatterns): 
     184            if isinstance(pattern, RegexURLResolver): 
     185                for key, value in pattern.reverse_dict.iteritems(): 
     186                    self.reverse_dict[key] = (pattern,) + value 
     187            else: 
     188                self.reverse_dict[pattern.callback] = (pattern,) 
     189                self.reverse_dict[pattern.name] = (pattern,) 
    163190 
    164191    def resolve(self, path): 
    165192        tried = [] 
     
    209236        return self._resolve_special('500') 
    210237 
    211238    def reverse(self, lookup_view, *args, **kwargs): 
    212         if not callable(lookup_view): 
    213             mod_name, func_name = get_mod_func(lookup_view) 
    214             try: 
    215                 lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name) 
    216             except (ImportError, AttributeError): 
    217                 if func_name != '': 
    218                     raise NoReverseMatch 
    219         for pattern in self.urlconf_module.urlpatterns: 
    220             if isinstance(pattern, RegexURLResolver): 
    221                 try: 
    222                     return pattern.reverse_helper(lookup_view, *args, **kwargs) 
    223                 except NoReverseMatch: 
    224                     continue 
    225             elif pattern.callback == lookup_view or pattern.name == lookup_view: 
    226                 try: 
    227                     return pattern.reverse_helper(*args, **kwargs) 
    228                 except NoReverseMatch: 
    229                     continue 
     239        try: 
     240            lookup_view = make_callable(lookup_view) 
     241        except (ImportError, AttributeError): 
     242            raise NoReverseMatch 
     243        if lookup_view in self.reverse_dict: 
     244            url_parts = (reverse_helper(part.regex, *args, **kwargs) for part in self.reverse_dict[lookup_view]) 
     245            return ''.join(url_parts) 
    230246        raise NoReverseMatch 
    231247 
    232248    def reverse_helper(self, lookup_view, *args, **kwargs): 
     
    234250        result = reverse_helper(self.regex, *args, **kwargs) 
    235251        return result + sub_match 
    236252 
     253callable_cache = {} 
     254def make_callable(lookup_view): 
     255    if not callable(lookup_view): 
     256        mod_name, func_name = get_mod_func(lookup_view) 
     257        if func_name != '': 
     258            lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name) 
     259    return lookup_view 
     260make_callable = cacheable(make_callable, callable_cache) 
     261 
    237262def resolve(path, urlconf=None): 
    238     if urlconf is None: 
    239         from django.conf import settings 
    240         urlconf = settings.ROOT_URLCONF 
    241     resolver = RegexURLResolver(r'^/', urlconf) 
     263    resolver = get_resolver(urlconf) 
    242264    return resolver.resolve(path) 
    243265 
    244266def reverse(viewname, urlconf=None, args=None, kwargs=None): 
    245267    args = args or [] 
    246268    kwargs = kwargs or {} 
    247     if urlconf is None: 
    248         from django.conf import settings 
    249         urlconf = settings.ROOT_URLCONF 
    250     resolver = RegexURLResolver(r'^/', urlconf) 
     269    resolver = get_resolver(urlconf) 
    251270    return '/' + resolver.reverse(viewname, *args, **kwargs)