def _populate(self): lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for pattern in reversed(self.url_patterns): p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: parent = normalize(pattern.regex.pattern) for name in pattern.reverse_dict: for matches, pat, defaults in pattern.reverse_dict.getlist(name): new_matches = [] for piece, p_args in parent: new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) lookups.appendlist(name, (new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs))) for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) if pattern.name is not None: lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) self._reverse_dict[language_code] = lookups self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps
def _populate(self): # Short-circuit if called recursively in this thread to prevent # infinite recursion. Concurrent threads may call this at the same # time and will need to continue, so set 'populating' on a # thread-local variable. if getattr(self._local, 'populating', False): return try: self._local.populating = True lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for url_pattern in reversed(self.url_patterns): p_pattern = url_pattern.pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(url_pattern, URLPattern): self._callback_strs.add(url_pattern.lookup_str) bits = normalize(url_pattern.pattern.regex.pattern) lookups.appendlist( url_pattern.callback, (bits, p_pattern, url_pattern.default_args, url_pattern.pattern.converters) ) if url_pattern.name is not None: lookups.appendlist( url_pattern.name, (bits, p_pattern, url_pattern.default_args, url_pattern.pattern.converters) ) else: # url_pattern is a URLResolver. url_pattern._populate() if url_pattern.app_name: apps.setdefault(url_pattern.app_name, []).append(url_pattern.namespace) namespaces[url_pattern.namespace] = (p_pattern, url_pattern) else: for name in url_pattern.reverse_dict: for matches, pat, defaults, converters in url_pattern.reverse_dict.getlist(name): new_matches = normalize(p_pattern + pat) lookups.appendlist( name, ( new_matches, p_pattern + pat, {**defaults, **url_pattern.default_kwargs}, {**self.pattern.converters, **url_pattern.pattern.converters, **converters} ) ) for namespace, (prefix, sub_pattern) in url_pattern.namespace_dict.items(): current_converters = url_pattern.pattern.converters sub_pattern.pattern.converters.update(current_converters) namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in url_pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) self._callback_strs.update(url_pattern._callback_strs) self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps self._reverse_dict[language_code] = lookups self._populated = True finally: self._local.populating = False
def _get_reverse_dict(self): if not self._reverse_dict and hasattr(self.urlconf_module, 'urlpatterns'): lookups = MultiValueDict() for pattern in reversed(self.urlconf_module.urlpatterns): p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): parent = normalize(pattern.regex.pattern) for name in pattern.reverse_dict: for matches, pat in pattern.reverse_dict.getlist(name): new_matches = [] for piece, p_args in parent: new_matches.extend([ (piece + suffix, p_args + args) for (suffix, args) in matches ]) lookups.appendlist(name, (new_matches, p_pattern + pat)) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern)) lookups.appendlist(pattern.name, (bits, p_pattern)) self._reverse_dict = lookups return self._reverse_dict
def _populate(self): lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for pattern in reversed(self.url_patterns): if hasattr(pattern, '_callback_str'): self._callback_strs.add(pattern._callback_str) elif hasattr(pattern, '_callback'): callback = pattern._callback if isinstance(callback, functools.partial): callback = callback.func if not hasattr(callback, '__name__'): lookup_str = callback.__module__ + "." + callback.__class__.__name__ else: lookup_str = callback.__module__ + "." + callback.__name__ self._callback_strs.add(lookup_str) p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: parent_pat = pattern.regex.pattern for name in pattern.reverse_dict: for matches, pat, defaults in pattern.reverse_dict.getlist( name): new_matches = normalize(parent_pat + pat) lookups.appendlist(name, ( new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs), )) for namespace, ( prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) self._callback_strs.update(pattern._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) if pattern.name is not None: lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) self._reverse_dict[language_code] = lookups self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps self._populated = True
def _populate(self): if self._populating: return self._populating = True lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for pattern in reversed(self.url_patterns): if isinstance(pattern, RegexURLPattern): self._callback_strs.add(pattern.lookup_str) p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: parent_pat = pattern.regex.pattern for name in pattern.reverse_dict: for matches, pat, defaults in pattern.reverse_dict.getlist( name): new_matches = normalize(parent_pat + pat) lookups.appendlist(name, ( new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs), )) for namespace, ( prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) if not pattern._populating: pattern._populate() self._callback_strs.update(pattern._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) if pattern.name is not None: lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) self._reverse_dict[language_code] = lookups self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps self._populated = True self._populating = False
def _populate(self): lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for pattern in reversed(self.url_patterns): if hasattr(pattern, '_callback_str'): self._callback_strs.add(pattern._callback_str) elif hasattr(pattern, '_callback'): callback = pattern._callback if isinstance(callback, functools.partial): callback = callback.func if not hasattr(callback, '__name__'): lookup_str = callback.__module__ + "." + callback.__class__.__name__ else: lookup_str = callback.__module__ + "." + callback.__name__ self._callback_strs.add(lookup_str) p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: parent_pat = pattern.regex.pattern for name in pattern.reverse_dict: for matches, pat, defaults in pattern.reverse_dict.getlist(name): new_matches = normalize(parent_pat + pat) lookups.appendlist( name, ( new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs), ) ) for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) self._callback_strs.update(pattern._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) if pattern.name is not None: lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) self._reverse_dict[language_code] = lookups self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps self._populated = True
def _populate(self): if self._populating: return self._populating = True lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for pattern in reversed(self.url_patterns): if isinstance(pattern, RegexURLPattern): self._callback_strs.add(pattern.lookup_str) p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: parent_pat = pattern.regex.pattern for name in pattern.reverse_dict: for matches, pat, defaults in pattern.reverse_dict.getlist(name): new_matches = normalize(parent_pat + pat) lookups.appendlist( name, ( new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs), ) ) for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) if not pattern._populating: pattern._populate() self._callback_strs.update(pattern._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) if pattern.name is not None: lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) self._reverse_dict[language_code] = lookups self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps self._populated = True self._populating = False
def _populate(self): # Short-circuit if called recursively in this thread to prevent # infinite recursion. Concurrent threads may call this at the same # time and will need to continue, so set 'populating' on a # thread-local variable. if getattr(self._local, "populating", False): return self._local.populating = True lookups = MultiValueDict() namespaces = {} apps = {} language_code = get_language() for pattern in reversed(self.url_patterns): if isinstance(pattern, RegexURLPattern): self._callback_strs.add(pattern.lookup_str) p_pattern = pattern.regex.pattern if p_pattern.startswith("^"): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: parent_pat = pattern.regex.pattern for name in pattern.reverse_dict: for matches, pat, defaults in pattern.reverse_dict.getlist(name): new_matches = normalize(parent_pat + pat) lookups.appendlist( name, (new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs)) ) for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) if not getattr(pattern._local, "populating", False): pattern._populate() self._callback_strs.update(pattern._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) if pattern.name is not None: lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) self._reverse_dict[language_code] = lookups self._namespace_dict[language_code] = namespaces self._app_dict[language_code] = apps self._populated = True self._local.populating = False
def _get_named_urls(resolver, ns="",pattern="",parts=None): if resolver.namespace: if not include_admin \ and ns=="" \ and resolver.namespace=='admin':return [] if not ns=='': ns += "__" ns += resolver.namespace if not parts: parts = [] norml = normalize(resolver.regex.pattern) if len(norml)>1: raise Exception('jsurls currently can only deal with a single path') norml = norml[0] _pattern,_parts = norml pattern += _pattern parts.extend(_parts) rslts=[] for r in resolver.url_patterns: if r.__class__==RegexURLResolver: rslts.extend(_get_named_urls(r,ns=ns,pattern=pattern,parts=parts)) for p in resolver.url_patterns: if p.__class__==RegexURLPattern and p.name and p.name!="": name = ns and ns or "" if not name=="": name += '__' name += p.name norml = normalize(p.regex.pattern) if len(norml)>1: raise Exception('jsurls currently can only deal with a single path') norml = norml[0] _pattern,_parts = norml _pat = pattern + _pattern _par = [] _par.extend(parts) _par.extend(_parts) rslts.append(JSUrl(name,_pat,_par)) return rslts
def walk_urlpatterns(obj, parents): if isinstance(obj, list): for item in obj: walk_urlpatterns(item, parents) elif isinstance(obj, RegexURLResolver): if obj.namespace is None: parents += (obj, ) walk_urlpatterns(obj.url_patterns, parents) elif isinstance(obj, RegexURLPattern): for name in names: if name == obj.name: pattern = '^/' for parent in parents: pattern += parent.regex.pattern.lstrip('^') pattern += obj.regex.pattern.lstrip('^') [(replace_pattern, kwargs)] = normalize(pattern) test_pattern = pattern for kwarg in kwargs: test_pattern = test_pattern.replace( '?P<{}>'.format(kwarg), '') test_pattern = test_pattern.replace('\\', '\\\\') data[name] = { 'replace_pattern': replace_pattern, 'test_pattern': test_pattern, 'kwargs': kwargs, }
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") text_args = [force_text(v) for v in args] text_kwargs = dict((k, force_text(v)) for (k, v) in kwargs.items()) if not self._populated: self._populate() try: if lookup_view in self._callback_strs: lookup_view = get_callable(lookup_view, True) except (ImportError, AttributeError) as e: raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) possibilities = self.reverse_dict.getlist(lookup_view) prefix_norm, prefix_args = normalize(urlquote(_prefix))[0] for possibility, pattern, defaults in possibilities: for result, params in possibility: if args: if len(args) != len(params) + len(prefix_args): continue candidate_subs = dict(zip(prefix_args + params, text_args)) else: if set(kwargs.keys()) | set(defaults.keys()) != set(params) | set(defaults.keys()) | set( prefix_args ): continue matches = True for k, v in defaults.items(): if kwargs.get(k, v) != v: matches = False break if not matches: continue candidate_subs = text_kwargs # WSGI provides decoded URLs, without %xx escapes, and the URL # resolver operates on such URLs. First substitute arguments # without quoting to build a decoded URL and look for a match. # Then, if we have a match, redo the substitution with quoted # arguments in order to return a properly encoded URL. candidate_pat = prefix_norm.replace("%", "%%") + result if re.search("^%s%s" % (prefix_norm, pattern), candidate_pat % candidate_subs, re.UNICODE): candidate_subs = dict((k, urlquote(v)) for (k, v) in candidate_subs.items()) return candidate_pat % candidate_subs # lookup_view can be URL label, or dotted path, or callable, Any of # these can be passed in at the top, but callables are not friendly in # error messages. m = getattr(lookup_view, "__module__", None) n = getattr(lookup_view, "__name__", None) if m is not None and n is not None: lookup_view_s = "%s.%s" % (m, n) else: lookup_view_s = lookup_view patterns = [pattern for (possibility, pattern, defaults) in possibilities] raise NoReverseMatch( "Reverse for '%s' with arguments '%s' and keyword " "arguments '%s' not found. %d pattern(s) tried: %s" % (lookup_view_s, args, kwargs, len(patterns), patterns) )
def subscriber_url(channel): push_server = getattr(settings, 'PUSH_SERVER', {}) subscriber_host = push_server.get('subscriber_host', {}) subscriber_host_location = subscriber_host.get('location', current_host) subscriber_host_secure = subscriber_host.get('secure', False) subscriber_pattern = None for location in push_server.get('locations', ()): if location.get('type') == 'subscriber': subscriber_pattern = location.get('url') # TODO: Currently we support only the first subscriber URL break if not subscriber_host_location or not subscriber_pattern: raise ValueError("Missing required settings") subscriber = regex_helper.normalize(subscriber_pattern) if len(subscriber) != 1 or len(subscriber[0][1]) != 1: raise ValueError("Non-reversible reg-exp: '%s'" % (subscriber_pattern,)) subscriber, (arg,) = subscriber[0] subscriber = subscriber % { arg: channel, } return 'http{0}://{1}{2}'.format( ('s' if subscriber_host_secure else ''), subscriber_host_location, subscriber, )
def generate(self, **kwargs): for pattern in self.patterns: normalized_patterns = normalize(pattern.pattern) for normalized_pattern in normalized_patterns: args = kwargs.copy() if args.has_key('month'): args['week_or_month_selector'] = 'm' args['week_or_month'] = args['month'] del args['month'] if args.has_key('week'): if args.has_key('day'): raise TypeError args['week_or_month_selector'] = 'w' args['week_or_month'] = args['week'] del args['week'] keys = args.keys() keys.sort() allowed_keys = normalized_pattern[1] allowed_keys.sort() if keys == allowed_keys: self.selector = normalized_pattern[0] % args if self.is_valid(): self.populate() return self.selector raise TypeError
def reverse_subdomain(name, args=(), kwargs=None): if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") if kwargs is None: kwargs = {} try: subdomain = settings.SUBDOMAINS[name] except KeyError: raise NoReverseMatch("No subdomain called %s exists" % name) unicode_args = [force_unicode(x) for x in args] unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()]) for result, params in normalize(subdomain['regex']): if args: if len(args) != len(params): continue candidate = result % dict(zip(params, unicode_args)) else: if set(kwargs.keys()) != set(params): continue candidate = result % unicode_kwargs if re.match(subdomain['regex'], candidate, re.UNICODE): return candidate raise NoReverseMatch( "Reverse subdomain for '%s' with arguments '%s' and keyword arguments " "'%s' not found." % (name, args, kwargs))
def reverse_host(name, args=None, kwargs=None): if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") if args is None: args = () if kwargs is None: kwargs = {} unicode_args = [force_unicode(x) for x in args] unicode_kwargs = dict(((k, force_unicode(v)) for (k, v) in kwargs.iteritems())) host = get_host(name) for result, params in normalize(host.regex): if args: if len(args) != len(params): continue candidate = result % dict(zip(params, unicode_args)) else: if set(kwargs.keys()) != set(params): continue candidate = result % unicode_kwargs if re.match(host.regex, candidate, re.UNICODE): return candidate raise NoReverseMatch("Reverse host for '%s' with arguments '%s' and " "keyword arguments '%s' not found." % (name, args, kwargs))
def reverse_subdomain(name, args=(), kwargs=None): if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") if kwargs is None: kwargs = {} try: subdomain = settings.SUBDOMAINS[name] except KeyError: raise NoReverseMatch("No subdomain called %s exists" % name) unicode_args = [force_unicode(x) for x in args] unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()]) for result, params in normalize(subdomain['regex']): if args: if len(args) != len(params): continue candidate = result % dict(zip(params, unicode_args)) else: if set(kwargs.keys()) != set(params): continue candidate = result % unicode_kwargs if re.match(subdomain['regex'], candidate, re.UNICODE): return candidate raise NoReverseMatch( "Reverse subdomain for '%s' with arguments '%s' and keyword arguments " "'%s' not found." % (name, args, kwargs) )
def _populate_subresolver(self, resolver, lookups, namespaces, apps): # XXX emulate part of _populate :/ if hasattr(resolver, '_callback_str'): self._callback_strs.add(resolver._callback_str) elif hasattr(resolver, '_callback'): callback = resolver._callback if isinstance(callback, functools.partial): callback = callback.func if not hasattr(callback, '__name__'): lookup_str = callback.__module__ + "." + callback.__class__.__name__ else: lookup_str = callback.__module__ + "." + callback.__name__ self._callback_strs.add(lookup_str) p_pattern = resolver.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(resolver, RegexURLResolver): if resolver.namespace: namespaces[resolver.namespace] = (p_pattern, resolver) if resolver.app_name: apps.setdefault(resolver.app_name, []).append(resolver.namespace) else: parent_pat = resolver.regex.pattern for name in resolver.reverse_dict: for matches, pat, defaults in resolver.reverse_dict.getlist( name): new_matches = normalize(parent_pat + pat) lookups.appendlist( name, (new_matches, p_pattern + pat, dict(defaults, **resolver.default_kwargs))) for namespace, ( prefix, sub_pattern) in resolver.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in resolver.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) self._callback_strs.update(resolver._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(resolver.callback, (bits, p_pattern, resolver.default_args)) if resolver.name is not None: lookups.appendlist(resolver.name, (bits, p_pattern, resolver.default_args))
def test_group_named(self): pattern = r"(?P<first_group_name>.*)-(?P<second_group_name>.*)" expected = [( "%(first_group_name)s-%(second_group_name)s", ["first_group_name", "second_group_name"], )] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError( "Don't mix *args and **kwargs in call to reverse()!") if not self._populated: self._populate() try: if lookup_view in self._callback_strs: lookup_view = get_callable(lookup_view, True) except (ImportError, AttributeError) as e: raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) possibilities = self.reverse_dict.getlist(lookup_view) prefix_norm, prefix_args = normalize(urlquote(_prefix))[0] for possibility, pattern, defaults in possibilities: for result, params in possibility: if args: if len(args) != len(params) + len(prefix_args): continue unicode_args = [force_text(val) for val in args] candidate = (prefix_norm + result) % dict( zip(prefix_args + params, unicode_args)) else: if set(kwargs.keys()) | set( defaults.keys()) != set(params) | set( defaults.keys()) | set(prefix_args): continue matches = True for k, v in defaults.items(): if kwargs.get(k, v) != v: matches = False break if not matches: continue unicode_kwargs = dict([(k, force_text(v)) for (k, v) in kwargs.items()]) candidate = (prefix_norm.replace('%', '%%') + result) % unicode_kwargs if re.search('^%s%s' % (prefix_norm, pattern), candidate, re.UNICODE): if candidate.startswith('//'): candidate = '/%%2F%s' % candidate[2:] return candidate # lookup_view can be URL label, or dotted path, or callable, Any of # these can be passed in at the top, but callables are not friendly in # error messages. m = getattr(lookup_view, '__module__', None) n = getattr(lookup_view, '__name__', None) if m is not None and n is not None: lookup_view_s = "%s.%s" % (m, n) else: lookup_view_s = lookup_view raise NoReverseMatch( "Reverse for '%s' with arguments '%s' and keyword " "arguments '%s' not found." % (lookup_view_s, args, kwargs))
def test_group_ignored(self): pattern = r"(?i)(?L)(?m)(?s)(?u)(?#)" expected = [('', [])] with warnings.catch_warnings(record=True) as warns: warnings.simplefilter('always') result = regex_helper.normalize(pattern) self.assertEqual(result, expected) for i, char in enumerate('iLmsu#'): self.assertEqual(str(warns[i].message), 'Using (?%s) in url() patterns is deprecated.' % char)
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError( "Don't mix *args and **kwargs in call to reverse()!") text_args = [force_text(v) for v in args] text_kwargs = dict((k, force_text(v)) for (k, v) in kwargs.items()) try: lookup_view = get_callable(lookup_view, True) except (ImportError, AttributeError) as e: raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) possibilities = self.reverse_dict.getlist(lookup_view) prefix_norm, prefix_args = normalize(urlquote(_prefix))[0] for possibility, pattern, defaults in possibilities: for result, params in possibility: if args: if len(args) != len(params) + len(prefix_args): continue candidate_subs = dict(zip(prefix_args + params, text_args)) else: if set(kwargs.keys()) | set( defaults.keys()) != set(params) | set( defaults.keys()) | set(prefix_args): continue matches = True for k, v in defaults.items(): if kwargs.get(k, v) != v: matches = False break if not matches: continue candidate_subs = text_kwargs # WSGI provides decoded URLs, without %xx escapes, and the URL # resolver operates on such URLs. First substitute arguments # without quoting to build a decoded URL and look for a match. # Then, if we have a match, redo the substitution with quoted # arguments in order to return a properly encoded URL. candidate_pat = prefix_norm.replace('%', '%%') + result if re.search('^%s%s' % (prefix_norm, pattern), candidate_pat % candidate_subs, re.UNICODE): candidate_subs = dict( (k, urlquote(v)) for (k, v) in candidate_subs.items()) return candidate_pat % candidate_subs # lookup_view can be URL label, or dotted path, or callable, Any of # these can be passed in at the top, but callables are not friendly in # error messages. m = getattr(lookup_view, '__module__', None) n = getattr(lookup_view, '__name__', None) if m is not None and n is not None: lookup_view_s = "%s.%s" % (m, n) else: lookup_view_s = lookup_view raise NoReverseMatch( "Reverse for '%s' with arguments '%s' and keyword " "arguments '%s' not found." % (lookup_view_s, args, kwargs))
def _get_reverse_dict(self): if not self._reverse_dict: for pattern in reversed(self.url_patterns): p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): parent = normalize(pattern.regex.pattern) for name in pattern.reverse_dict: for matches, pat in pattern.reverse_dict.getlist(name): new_matches = [] for piece, p_args in parent: new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) self._reverse_dict.appendlist(name, (new_matches, p_pattern + pat)) else: bits = normalize(p_pattern) self._reverse_dict.appendlist(pattern.callback, (bits, p_pattern)) self._reverse_dict.appendlist(pattern.name, (bits, p_pattern)) return self._reverse_dict
def _get_reverse_dict(self): if not self._reverse_dict and hasattr(self.urlconf_module, 'urlpatterns'): for pattern in reversed(self.urlconf_module.urlpatterns): p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): parent = normalize(pattern.regex.pattern) for name, (matches, pat) in pattern.reverse_dict.items(): new_matches = [] for piece, p_args in parent: new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) self._reverse_dict[name] = new_matches, p_pattern + pat else: bits = normalize(p_pattern) self._reverse_dict[pattern.callback] = bits, p_pattern self._reverse_dict[pattern.name] = bits, p_pattern return self._reverse_dict
def _get_reverse_dict(self): if not self._reverse_dict and hasattr(self.urlconf_module, 'urlpatterns'): for pattern in reversed(self.urlconf_module.urlpatterns): p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): parent = normalize(pattern.regex.pattern) for name, (matches, pat) in pattern.reverse_dict.iteritems(): new_matches = [] for piece, p_args in parent: new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) self._reverse_dict[name] = new_matches, p_pattern + pat else: bits = normalize(p_pattern) self._reverse_dict[pattern.callback] = bits, p_pattern self._reverse_dict[pattern.name] = bits, p_pattern return self._reverse_dict
def reverse_host(host, args=None, kwargs=None): """ Given the host name and the appropriate parameters, reverses the host, e.g.:: >>> from django.conf import settings >>> settings.ROOT_HOSTCONF = 'mysite.hosts' >>> settings.PARENT_HOST = 'example.com' >>> from django_hosts.resolvers import reverse_host >>> reverse_host('with_username', args=('jezdez',)) 'jezdez.example.com' :param name: the name of the host as specified in the hostconf :param args: the host arguments to use to find a matching entry in the hostconf :param kwargs: similar to args but key value arguments :raises django.core.urlresolvers.NoReverseMatch: if no host matches :rtype: reversed hostname """ if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") args = args or () kwargs = kwargs or {} if not isinstance(host, host_cls): host = get_host(host) unicode_args = [force_text(x) for x in args] unicode_kwargs = dict(((k, force_text(v)) for (k, v) in six.iteritems(kwargs))) for result, params in normalize(host.regex): if args: if len(args) != len(params): continue candidate = result % dict(zip(params, unicode_args)) else: if set(kwargs.keys()) != set(params): continue candidate = result % unicode_kwargs if re.match(host.regex, candidate, re.UNICODE): # pragma: no cover parent_host = getattr(settings, 'PARENT_HOST', '').lstrip('.') if parent_host: # only add the parent host when needed (aka www-less domain) if candidate and candidate != parent_host: candidate = '%s.%s' % (candidate, parent_host) else: candidate = parent_host return candidate raise NoReverseMatch("Reverse host for '%s' with arguments '%s' " "and keyword arguments '%s' not found." % (host.name, args, kwargs))
def reverse_host(host, args=None, kwargs=None): """ Given the host name and the appropriate parameters, reverses the host, e.g.:: >>> from django.conf import settings >>> settings.ROOT_HOSTCONF = 'mysite.hosts' >>> settings.PARENT_HOST = 'example.com' >>> from django_hosts.resolvers import reverse_host >>> reverse_host('with_username', args=('jezdez',)) 'jezdez.example.com' :param name: the name of the host as specified in the hostconf :param args: the host arguments to use to find a matching entry in the hostconf :param kwargs: similar to args but key value arguments :raises django.core.urlresolvers.NoReverseMatch: if no host matches :rtype: reversed hostname """ if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") args = args or () kwargs = kwargs or {} if not isinstance(host, host_cls): host = get_host(host) unicode_args = [force_text(x) for x in args] unicode_kwargs = dict( ((k, force_text(v)) for (k, v) in dict.items(kwargs))) for result, params in normalize(host.regex): if args: if len(args) != len(params): continue candidate = result % dict(zip(params, unicode_args)) else: if set(kwargs.keys()) != set(params): continue candidate = result % unicode_kwargs if re.match(host.regex, candidate, re.UNICODE): # pragma: no cover parent_host = getattr(settings, 'PARENT_HOST', '').lstrip('.') if parent_host: # only add the parent host when needed (aka www-less domain) if candidate and candidate != parent_host: candidate = '%s.%s' % (candidate, parent_host) else: candidate = parent_host return candidate raise NoReverseMatch("Reverse host for '%s' with arguments '%s' " "and keyword arguments '%s' not found." % (host.name, args, kwargs))
def _build_reverse_dict_for_lang(self, lang): reverse_dict = MultiValueDict() namespaces = {} apps = {} for pattern in reversed(self.url_patterns): if hasattr(pattern, 'get_regex'): p_pattern = pattern.get_regex(lang).pattern else: p_pattern = pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(pattern, RegexURLResolver): if pattern.namespace: namespaces[pattern.namespace] = (p_pattern, pattern) if pattern.app_name: apps.setdefault(pattern.app_name, []).append(pattern.namespace) else: if hasattr(pattern, 'get_regex'): parent = normalize(pattern.get_regex(lang).pattern) else: parent = normalize(pattern.regex.pattern) if hasattr(pattern, 'get_reverse_dict'): sub_reverse_dict = pattern.get_reverse_dict(lang) else: sub_reverse_dict = pattern.reverse_dict for name in sub_reverse_dict: for matches, pat in sub_reverse_dict.getlist(name): new_matches = [] for piece, p_args in parent: new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) reverse_dict.appendlist(name, (new_matches, p_pattern + pat)) for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) else: bits = normalize(p_pattern) reverse_dict.appendlist(pattern.callback, (bits, p_pattern)) reverse_dict.appendlist(pattern.name, (bits, p_pattern)) self._namespace_dict = namespaces self._app_dict = apps return reverse_dict
def get_context_data(self, **kwargs): context = super(ScriptUrls, self).get_context_data(**kwargs) url_dict = {} from django.core.urlresolvers import reverse from django.utils.regex_helper import normalize from feedreader_api.api0 import urls as api_urls for url in api_urls.urlpatterns: url_dict[url.name] = normalize(url.regex.pattern)[0][0] context['urls'] = url_dict #context['latest_articles'] = Article.objects.all()[:5] return context
def test_group_ignored(self): pattern = r"(?i)(?L)(?m)(?s)(?u)(?#)" expected = [('', [])] with warnings.catch_warnings(record=True) as warns: warnings.simplefilter('always') result = regex_helper.normalize(pattern) self.assertEqual(result, expected) for i, char in enumerate('iLmsu#'): self.assertEqual( str(warns[i].message), 'Using (?%s) in url() patterns is deprecated.' % char)
def _populate_subresolver(self, resolver, lookups, namespaces, apps): # XXX emulate part of _populate :/ if hasattr(resolver, '_callback_str'): self._callback_strs.add(resolver._callback_str) elif hasattr(resolver, '_callback'): callback = resolver._callback if isinstance(callback, functools.partial): callback = callback.func if not hasattr(callback, '__name__'): lookup_str = callback.__module__ + "." + callback.__class__.__name__ else: lookup_str = callback.__module__ + "." + callback.__name__ self._callback_strs.add(lookup_str) p_pattern = resolver.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(resolver, RegexURLResolver): if resolver.namespace: namespaces[resolver.namespace] = (p_pattern, resolver) if resolver.app_name: apps.setdefault(resolver.app_name, []).append(resolver.namespace) else: parent_pat = resolver.regex.pattern for name in resolver.reverse_dict: for matches, pat, defaults in resolver.reverse_dict.getlist(name): new_matches = normalize(parent_pat + pat) lookups.appendlist(name, (new_matches, p_pattern + pat, dict(defaults, **resolver.default_kwargs))) for namespace, (prefix, sub_pattern) in resolver.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in resolver.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) self._callback_strs.update(resolver._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist(resolver.callback, (bits, p_pattern, resolver.default_args)) if resolver.name is not None: lookups.appendlist(resolver.name, (bits, p_pattern, resolver.default_args))
def endpoint_string_pattern(viewname, with_host=False, pattern_subdomain='www', force_secure=False): urlconf = get_urlconf() resolver = get_resolver(urlconf) args = resolver.reverse_dict[viewname][0][0][1][:] pattern = resolver.reverse_dict[viewname][0][0][0] prefix = get_script_prefix() prefix_norm, prefix_args = normalize(urlquote(prefix))[0] candidate_pat = prefix_norm.replace('%', '%%') + pattern pattern_args = {arg: '{%s}' % arg for arg in args} path = candidate_pat % pattern_args if with_host: return '%s//%s.%s%s' % (('https:' if force_secure else ''), pattern_subdomain, settings.TOP_DOMAIN, path) return path
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") if not self._populated: self._populate() try: if lookup_view in self._callback_strs: lookup_view = get_callable(lookup_view, True) except (ImportError, AttributeError) as e: raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) possibilities = self.reverse_dict.getlist(lookup_view) prefix_norm, prefix_args = normalize(urlquote(_prefix))[0] for possibility, pattern, defaults in possibilities: for result, params in possibility: if args: if len(args) != len(params) + len(prefix_args): continue unicode_args = [force_text(val) for val in args] candidate = (prefix_norm + result) % dict(zip(prefix_args + params, unicode_args)) else: if set(kwargs.keys()) | set(defaults.keys()) != set(params) | set(defaults.keys()) | set(prefix_args): continue matches = True for k, v in defaults.items(): if kwargs.get(k, v) != v: matches = False break if not matches: continue unicode_kwargs = dict([(k, force_text(v)) for (k, v) in kwargs.items()]) candidate = (prefix_norm.replace('%', '%%') + result) % unicode_kwargs if re.search('^%s%s' % (prefix_norm, pattern), candidate, re.UNICODE): if candidate.startswith('//'): candidate = '/%%2F%s' % candidate[2:] return candidate # lookup_view can be URL label, or dotted path, or callable, Any of # these can be passed in at the top, but callables are not friendly in # error messages. m = getattr(lookup_view, '__module__', None) n = getattr(lookup_view, '__name__', None) if m is not None and n is not None: lookup_view_s = "%s.%s" % (m, n) else: lookup_view_s = lookup_view raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword " "arguments '%s' not found." % (lookup_view_s, args, kwargs))
def normalize_url_pattern(url_pattern): normalized = normalize(url_pattern) try: [(url_as_str, url_params)] = normalized except ValueError: try: [(url_as_str_without_param, _), (url_as_str, url_params)] = normalized except ValueError: raise UrlStructureNotSupported if 'format' in url_params and url_as_str.endswith('.%(format)s'): # remove optional parameter provided by DRF ViewSet # eg. /items is provided as /items.%(format)s (/items.json) url_params.remove('format') url_as_str = url_as_str[:-len('.%(format)s')] return url_as_str, url_params
def reverse_route(route_name, args=None, kwargs=None): args = args or [] kwargs = kwargs or {} prefix = get_script_prefix() try: route = Route.objects.filter(name=route_name).get() except Route.DoesNotExist: msg = ("Reverse for '%s' not found." % (route_name)) raise NoReverseMatch(msg) converters = _route_to_regex(route.path)[1] for result, params in normalize(_route_to_regex(route.path)[0]): if args: if len(args) != len(params): continue candidate_subs = dict(zip(params, args)) else: if set(kwargs).symmetric_difference(params): continue candidate_subs = kwargs text_candidate_subs = {} for k, v in candidate_subs.items(): if k in converters: text_candidate_subs[k] = converters[k].to_url(v) else: text_candidate_subs[k] = str(v) candidate_pat = prefix.replace('%', '%%') + result url = urllib.parse.quote(candidate_pat % text_candidate_subs, safe=RFC3986_SUBDELIMS + '/~:@') return escape_leading_slashes(url) if args: arg_msg = "arguments '%s'" % (args, ) elif kwargs: arg_msg = "keyword arguments '%s'" % (kwargs, ) else: arg_msg = "no arguments" msg = ("Reverse for '%s' with %s not matched." % (route_name, arg_msg)) raise NoReverseMatch(msg)
def inspect_pattern(pattern, prefix=None): prefix = prefix or '' view, decorators = extract_view(pattern.callback) module = inspect.getmodule(view) if inspect.isfunction(view): argspec = inspect.getargspec(view) else: argspec = None annotations = {} group_names = {} normalized_pattern, groups = regex_helper.normalize(prefix + pattern.regex.pattern)[0] for group in groups: try: group_number = int(group.strip('_')) except ValueError: group_number = None if argspec is not None and group_number is not None: try: group_name = argspec[0][group_number + 1] except IndexError: group_name = group else: group_name = group annotations[group] = '<span class="capturegroup"><%s></span>' % group_name group_names[group] = group_name return { 'view_module': module.__name__, 'view_name': view.__name__, 'prefix': prefix, 'regex': pattern.regex.pattern, 'name': pattern.name, 'default_args': pattern.default_args, 'annotated_pattern': normalized_pattern % annotations, 'normalized_pattern': normalized_pattern % group_names, 'raw_pattern': prefix + pattern.regex.pattern, }
def subscriber_url(channel): push_server = getattr(settings, 'PUSH_SERVER', {}) port = push_server.get('port', 80) subscriber_pattern = None for location in push_server.get('locations', ()): if location.get('type') == 'subscriber': subscriber_pattern = location.get('url') # TODO: Currently we support only the first subscriber URL break if port == 80: port = '' else: port = ':%s' % (port,) address = push_server.get('address') if not (address and subscriber_pattern): raise ValueError("Missing required settings") subscriber = regex_helper.normalize(subscriber_pattern) if len(subscriber) != 1 or len(subscriber[0][1]) != 1: raise ValueError("Non-reversible reg-exp: '%s'" % (subscriber_pattern,)) subscriber, (arg,) = subscriber[0] subscriber = subscriber % { arg: channel, } return 'http://%s%s%s' % (address, port, subscriber)
def test_group_positional(self): pattern = r"(.*)-(.+)" expected = [('%(_0)s-%(_1)s', ['_0', '_1'])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def build_url_template(viewname, kwargs=[], urlconf=None, prefix=None, current_app=None): resolver = get_resolver(urlconf) if prefix is None: prefix = get_script_prefix() kwargs = list(kwargs) parts = viewname.split(':') parts.reverse() view = parts[0] path = parts[1:] resolved_path = [] ns_pattern = '' while path: ns = path.pop() # Lookup the name to see if it could be an app identifier try: app_list = resolver.app_dict[ns] # Yes! Path part matches an app in the current Resolver if current_app and current_app in app_list: # If we are reversing for a particular app, # use that namespace ns = current_app elif ns not in app_list: # The name isn't shared by one of the instances # (i.e., the default) so just pick the first instance # as the default. ns = app_list[0] except KeyError: pass try: extra, resolver = resolver.namespace_dict[ns] resolved_path.append(ns) ns_pattern = ns_pattern + extra except KeyError as key: if resolved_path: raise NoReverseMatch( "%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path))) else: raise NoReverseMatch("%s is not a registered namespace" % key) if ns_pattern: resolver = get_ns_resolver(ns_pattern, resolver) possibilities = resolver.reverse_dict.getlist(view) prefix_norm, prefix_args = normalize(prefix)[0] for entry in possibilities: if len(entry) == 3: possibility, pattern, defaults = entry else: possibility, pattern = entry defaults = {} for result, params in possibility: if set(kwargs + list(defaults)) != set(params + list(defaults) + prefix_args): continue unicode_kwargs = dict([(k, '%(' + force_text(k) + ')s') for k in kwargs]) unicode_kwargs.update(defaults) return (prefix_norm + result) % unicode_kwargs raise NoReverseMatch("Reverse for '%s' with keyword arguments '%s' not " "found." % (viewname, kwargs))
def test_group_noncapturing(self): pattern = r"(?:non-capturing)" expected = [('non-capturing', [])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def test_group_named(self): pattern = r"(?P<first_group_name>.*)-(?P<second_group_name>.*)" expected = [('%(first_group_name)s-%(second_group_name)s', ['first_group_name', 'second_group_name'])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def test_empty(self): pattern = r"" expected = [('', [])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def test_group_positional(self): pattern = r"(.*)-(.+)" expected = [("%(_0)s-%(_1)s", ["_0", "_1"])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def _populate(self): # Short-circuit if called recursively in this thread to prevent # infinite recursion. Concurrent threads may call this at the same # time and will need to continue, so set 'populating' on a # thread-local variable. #pylint:disable=protected-access,too-many-locals if getattr(self._local, 'populating', False): return try: self._local.populating = True lookups = MultiValueDict() namespaces = {} apps = {} path_prefix = self._get_path_prefix() for url_pattern in reversed(self.url_patterns): if isinstance(url_pattern, DjangoRegexURLPattern): self._callback_strs.add(url_pattern.lookup_str) # could be RegexURLPattern.regex or RegexURLResolver.regex here. p_pattern = url_pattern.regex.pattern if p_pattern.startswith('^'): p_pattern = p_pattern[1:] if isinstance(url_pattern, DjangoRegexURLResolver): if url_pattern.namespace: namespaces[url_pattern.namespace] = (p_pattern, url_pattern) if url_pattern.app_name: apps.setdefault(url_pattern.app_name, []).append(url_pattern.namespace) else: parent_pat = url_pattern.regex.pattern for name in url_pattern.reverse_dict: for _, pat, defaults \ in url_pattern.reverse_dict.getlist(name): new_matches = normalize(parent_pat + pat) lookups.appendlist(name, ( new_matches, p_pattern + pat, dict(defaults, ** url_pattern.default_kwargs), )) for namespace, (prefix, sub_pattern) \ in url_pattern.namespace_dict.items(): namespaces[namespace] = (p_pattern + prefix, sub_pattern) for app_name, namespace_list in \ url_pattern.app_dict.items(): apps.setdefault(app_name, []).extend(namespace_list) if not getattr(url_pattern._local, 'populating', False): url_pattern._populate() self._callback_strs.update(url_pattern._callback_strs) else: bits = normalize(p_pattern) lookups.appendlist( url_pattern.callback, (bits, p_pattern, url_pattern.default_args)) if url_pattern.name is not None: lookups.appendlist( url_pattern.name, (bits, p_pattern, url_pattern.default_args)) self._reverse_dict[path_prefix] = lookups self._namespace_dict[path_prefix] = namespaces self._app_dict[path_prefix] = apps self._populated = True finally: self._local.populating = False
def test_openapi_arguments(self) -> None: """This end-to-end API documentation test compares the arguments defined in the actual code using @has_request_variables and REQ(), with the arguments declared in our API documentation for every API endpoint in Zulip. First, we import the fancy-Django version of zproject/urls.py by doing this, each has_request_variables wrapper around each imported view function gets called to generate the wrapped view function and thus filling the global arguments_map variable. Basically, we're exploiting code execution during import. Then we need to import some view modules not already imported in urls.py. We use this different syntax because of the linters complaining of an unused import (which is correct, but we do this for triggering the has_request_variables decorator). At the end, we perform a reverse mapping test that verifies that every URL pattern defined in the OpenAPI documentation actually exists in code. """ from zproject import urls as urlconf # We loop through all the API patterns, looking in particular # for those using the rest_dispatch decorator; we then parse # its mapping of (HTTP_METHOD -> FUNCTION). for p in urlconf.v1_api_and_json_patterns + urlconf.v1_api_mobile_patterns: if p.callback is not rest_dispatch: # Endpoints not using rest_dispatch don't have extra data. methods_endpoints: Dict[str, Any] = dict(GET=p.callback, ) else: methods_endpoints = assert_is_not_none(p.default_args) # since the module was already imported and is now residing in # memory, we won't actually face any performance penalties here. for method, value in methods_endpoints.items(): if callable(value): function: Callable[..., HttpResponse] = value tags: Set[str] = set() else: function, tags = value if function is get_events: # Work around the fact that the registered # get_events view function isn't where we do # @has_request_variables. # # TODO: Make this configurable via an optional argument # to has_request_variables, e.g. # @has_request_variables(view_func_name="zerver.tornado.views.get_events") function = get_events_backend function_name = f"{function.__module__}.{function.__name__}" # Our accounting logic in the `has_request_variables()` # code means we have the list of all arguments # accepted by every view function in arguments_map. accepted_arguments = set(arguments_map[function_name]) regex_pattern = p.pattern.regex.pattern for url_format, url_params in regex_helper.normalize( regex_pattern): url_pattern = "/" + url_format % { param: f"{{{param}}}" for param in url_params } if "intentionally_undocumented" in tags: self.ensure_no_documentation_if_intentionally_undocumented( url_pattern, method) continue if url_pattern in self.pending_endpoints: # HACK: After all pending_endpoints have been resolved, we should remove # this segment and the "msg" part of the `ensure_no_...` method. msg = f""" We found some OpenAPI documentation for {method} {url_pattern}, so maybe we shouldn't include it in pending_endpoints. """ self.ensure_no_documentation_if_intentionally_undocumented( url_pattern, method, msg) continue try: # Don't include OpenAPI parameters that live in # the path; these are not extracted by REQ. openapi_parameters = get_openapi_parameters( url_pattern, method, include_url_parameters=False) except Exception: # nocoverage raise AssertionError( f"Could not find OpenAPI docs for {method} {url_pattern}" ) # We now have everything we need to understand the # function as defined in our urls.py: # # * method is the HTTP method, e.g. GET, POST, or PATCH # # * p.pattern.regex.pattern is the URL pattern; might require # some processing to match with OpenAPI rules # # * accepted_arguments is the full set of arguments # this method accepts (from the REQ declarations in # code). # # * The documented parameters for the endpoint as recorded in our # OpenAPI data in zerver/openapi/zulip.yaml. # # We now compare these to confirm that the documented # argument list matches what actually appears in the # codebase. openapi_parameter_names = { parameter["name"] for parameter in openapi_parameters } if len(accepted_arguments - openapi_parameter_names) > 0: # nocoverage print("Undocumented parameters for", url_pattern, method, function_name) print(" +", openapi_parameter_names) print(" -", accepted_arguments) assert url_pattern in self.buggy_documentation_endpoints elif len(openapi_parameter_names - accepted_arguments) > 0: # nocoverage print("Documented invalid parameters for", url_pattern, method, function_name) print(" -", openapi_parameter_names) print(" +", accepted_arguments) assert url_pattern in self.buggy_documentation_endpoints else: self.assertEqual(openapi_parameter_names, accepted_arguments) self.check_argument_types(function, openapi_parameters) self.checked_endpoints.add(url_pattern) self.check_for_non_existant_openapi_endpoints()
pass try: extra, resolver = resolver.namespace_dict[ns] resolved_path.append(ns) prefix = prefix + extra except KeyError, key: if resolved_path: raise NoReverseMatch("%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path))) else: raise NoReverseMatch("%s is not a registered namespace" % key) # This is a hack to get namespaced URLs to reverse when the base # has named parameters (may work for positional, but not tested at all). pattern = prefix possibility = normalize(pattern) # The magical regex reverse function! # Below copied from _populate above and slightly altered for result, params in possibility: copy_args = [v for v in args] copy_kwargs = kwargs.copy() if args: if len(args) < len(params): continue # Pop arguments to ensure the right number are passed on to the # resolver. unicode_args = [force_unicode(copy_args.pop(0)) for v in params] candidate = result % dict(zip(params, unicode_args)) else: if not set(params).issubset(set(kwargs.keys())): continue # Pop arguments to ensure the right number are passed on to the
extra, resolver = resolver.namespace_dict[ns] resolved_path.append(ns) ns_pattern = ns_pattern + extra except KeyError, key: if resolved_path: raise NoReverseMatch( "%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path))) else: raise NoReverseMatch("%s is not a registered namespace" % key) if ns_pattern: resolver = get_ns_resolver(ns_pattern, resolver) possibilities = resolver.reverse_dict.getlist(view) prefix_norm, prefix_args = normalize(prefix)[0] for entry in possibilities: if len(entry) == 3: possibility, pattern, defaults = entry else: possibility, pattern = entry defaults = {} for result, params in possibility: if set(kwargs + defaults.keys()) != set(params + defaults.keys() + prefix_args): continue unicode_kwargs = dict([(k, u'#{' + force_unicode(k) + u'}') for k in kwargs]) unicode_kwargs.update(defaults) return (prefix_norm + result) % unicode_kwargs
def test_group_backreference(self): pattern = r"(?P<first_group_name>.*)-(?P=first_group_name)" expected = [('%(first_group_name)s-%(first_group_name)s', ['first_group_name'])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def test_escape(self): pattern = r"\\\^\$\.\|\?\*\+\(\)\[" expected = [('\\^$.|?*+()[', [])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def test_group_ignored(self): pattern = r"(?i)(?L)(?m)(?s)(?u)(?#)" expected = [(u'', [])] result = regex_helper.normalize(pattern) self.assertEqual(result, expected)
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError("Don't mix *args and **kwargs in call to reverse()!") text_args = [force_text(v) for v in args] text_kwargs = {k: force_text(v) for (k, v) in kwargs.items()} if not self._populated: self._populate() original_lookup = lookup_view try: if self._is_callback(lookup_view): lookup_view = get_callable(lookup_view, True) except (ImportError, AttributeError) as e: raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) else: if not callable(original_lookup) and callable(lookup_view): warnings.warn( 'Reversing by dotted path is deprecated (%s).' % original_lookup, RemovedInDjango20Warning, stacklevel=3 ) possibilities = self.reverse_dict.getlist(lookup_view) prefix_norm, prefix_args = normalize(urlquote(_prefix))[0] for possibility, pattern, defaults in possibilities: for result, params in possibility: if args: if len(args) != len(params) + len(prefix_args): continue candidate_subs = dict(zip(prefix_args + params, text_args)) else: if (set(kwargs.keys()) | set(defaults.keys()) != set(params) | set(defaults.keys()) | set(prefix_args)): continue matches = True for k, v in defaults.items(): if kwargs.get(k, v) != v: matches = False break if not matches: continue candidate_subs = text_kwargs # WSGI provides decoded URLs, without %xx escapes, and the URL # resolver operates on such URLs. First substitute arguments # without quoting to build a decoded URL and look for a match. # Then, if we have a match, redo the substitution with quoted # arguments in order to return a properly encoded URL. candidate_pat = prefix_norm.replace('%', '%%') + result if re.search('^%s%s' % (prefix_norm, pattern), candidate_pat % candidate_subs, re.UNICODE): # safe characters from `pchar` definition of RFC 3986 candidate_subs = dict((k, urlquote(v, safe=RFC3986_SUBDELIMS + str('/~:@'))) for (k, v) in candidate_subs.items()) url = candidate_pat % candidate_subs # Don't allow construction of scheme relative urls. if url.startswith('//'): url = '/%%2F%s' % url[2:] return url # lookup_view can be URL label, or dotted path, or callable, Any of # these can be passed in at the top, but callables are not friendly in # error messages. m = getattr(lookup_view, '__module__', None) n = getattr(lookup_view, '__name__', None) if m is not None and n is not None: lookup_view_s = "%s.%s" % (m, n) else: lookup_view_s = lookup_view patterns = [pattern for (possibility, pattern, defaults) in possibilities] raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword " "arguments '%s' not found. %d pattern(s) tried: %s" % (lookup_view_s, args, kwargs, len(patterns), patterns))
def build_url_template(viewname, kwargs=[], urlconf=None, prefix=None, current_app=None): resolver = get_resolver(urlconf) if prefix is None: prefix = get_script_prefix() kwargs = list(kwargs) parts = viewname.split(':') parts.reverse() view = parts[0] path = parts[1:] resolved_path = [] ns_pattern = '' while path: ns = path.pop() # Lookup the name to see if it could be an app identifier try: app_list = resolver.app_dict[ns] # Yes! Path part matches an app in the current Resolver if current_app and current_app in app_list: # If we are reversing for a particular app, # use that namespace ns = current_app elif ns not in app_list: # The name isn't shared by one of the instances # (i.e., the default) so just pick the first instance # as the default. ns = app_list[0] except KeyError: pass try: extra, resolver = resolver.namespace_dict[ns] resolved_path.append(ns) ns_pattern = ns_pattern + extra except KeyError as key: if resolved_path: raise NoReverseMatch( "%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path))) else: raise NoReverseMatch("%s is not a registered namespace" % key) if ns_pattern: resolver = get_ns_resolver(ns_pattern, resolver) possibilities = resolver.reverse_dict.getlist(view) prefix_norm, prefix_args = normalize(prefix)[0] for entry in possibilities: if len(entry) == 3: possibility, pattern, defaults = entry else: possibility, pattern = entry defaults = {} for result, params in possibility: if set(kwargs + list(defaults)) != set(params + list(defaults) + prefix_args): continue unicode_kwargs = dict([(k, '%(' + force_text(k) + ')s') for k in kwargs]) unicode_kwargs.update(defaults) return (prefix_norm + result) % unicode_kwargs raise NoReverseMatch( "Reverse for '%s' with keyword arguments '%s' not found." % (viewname, kwargs))
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError( "Don't mix *args and **kwargs in call to reverse()!") text_args = [force_text(v) for v in args] text_kwargs = {k: force_text(v) for (k, v) in kwargs.items()} if not self._populated: self._populate() original_lookup = lookup_view try: if self._is_callback(lookup_view): lookup_view = get_callable(lookup_view, True) except (ImportError, AttributeError) as e: raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) else: if not callable(original_lookup) and callable(lookup_view): warnings.warn('Reversing by dotted path is deprecated (%s).' % original_lookup, RemovedInDjango110Warning, stacklevel=3) possibilities = self.reverse_dict.getlist(lookup_view) prefix_norm, prefix_args = normalize(urlquote(_prefix))[0] for possibility, pattern, defaults in possibilities: for result, params in possibility: if args: if len(args) != len(params) + len(prefix_args): continue candidate_subs = dict(zip(prefix_args + params, text_args)) else: if (set(kwargs.keys()) | set(defaults.keys()) != set(params) | set(defaults.keys()) | set(prefix_args)): continue matches = True for k, v in defaults.items(): if kwargs.get(k, v) != v: matches = False break if not matches: continue candidate_subs = text_kwargs # WSGI provides decoded URLs, without %xx escapes, and the URL # resolver operates on such URLs. First substitute arguments # without quoting to build a decoded URL and look for a match. # Then, if we have a match, redo the substitution with quoted # arguments in order to return a properly encoded URL. candidate_pat = prefix_norm.replace('%', '%%') + result if re.search('^%s%s' % (prefix_norm, pattern), candidate_pat % candidate_subs, re.UNICODE): # safe characters from `pchar` definition of RFC 3986 candidate_subs = dict( (k, urlquote(v, safe=RFC3986_SUBDELIMS + str('/~:@'))) for (k, v) in candidate_subs.items()) url = candidate_pat % candidate_subs # Don't allow construction of scheme relative urls. if url.startswith('//'): url = '/%%2F%s' % url[2:] return url # lookup_view can be URL label, or dotted path, or callable, Any of # these can be passed in at the top, but callables are not friendly in # error messages. m = getattr(lookup_view, '__module__', None) n = getattr(lookup_view, '__name__', None) if m is not None and n is not None: lookup_view_s = "%s.%s" % (m, n) else: lookup_view_s = lookup_view patterns = [ pattern for (possibility, pattern, defaults) in possibilities ] raise NoReverseMatch( "Reverse for '%s' with arguments '%s' and keyword " "arguments '%s' not found. %d pattern(s) tried: %s" % (lookup_view_s, args, kwargs, len(patterns), patterns))
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs): if args and kwargs: raise ValueError( "Don't mix *args and **kwargs in call to reverse()!") if not self._populated: self._populate() try: if lookup_view in self._callback_strs: lookup_view = get_callable(lookup_view, True) except (ImportError, AttributeError), e: raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) possibilities = self.reverse_dict.getlist(lookup_view) prefix_norm, prefix_args = normalize(_prefix)[0] for possibility, pattern, defaults in possibilities: for result, params in possibility: if args: if len(args) != len(params) + len(prefix_args): continue unicode_args = [force_unicode(val) for val in args] candidate = (prefix_norm + result) % dict( zip(prefix_args + params, unicode_args)) else: if set(kwargs.keys() + defaults.keys()) != set(params + defaults.keys() + prefix_args): continue matches = True for k, v in defaults.items():