def url_unparse(components): """The reverse operation to :meth:`url_parse`. This accepts arbitrary as well as :class:`URL` tuples and returns a URL as a string. :param components: the parsed URL as tuple which should be converted into a URL string. """ scheme, netloc, path, query, fragment = \ normalize_string_tuple(components) s = make_literal_wrapper(scheme) url = s('') # We generally treat file:///x and file:/x the same which is also # what browsers seem to do. This also allows us to ignore a schema # register for netloc utilization or having to differenciate between # empty and missing netloc. if netloc or (scheme and path.startswith(s('/'))): if path and path[:1] != s('/'): path = s('/') + path url = s('//') + (netloc or s('')) + path elif path: url += path if scheme: url = scheme + s(':') + url if query: url = url + s('?') + query if fragment: url = url + s('#') + fragment return url
def url_join(base, url, allow_fragments=True): """Join a base URL and a possibly relative URL to form an absolute interpretation of the latter. :param base: the base URL for the join operation. :param url: the URL to join. :param allow_fragments: indicates whether fragments should be allowed. """ if isinstance(base, tuple): base = url_unparse(base) if isinstance(url, tuple): url = url_unparse(url) base, url = normalize_string_tuple((base, url)) s = make_literal_wrapper(base) if not base: return url if not url: return base bscheme, bnetloc, bpath, bquery, bfragment = \ url_parse(base, allow_fragments=allow_fragments) scheme, netloc, path, query, fragment = \ url_parse(url, bscheme, allow_fragments) if scheme != bscheme: return url if netloc: return url_unparse((scheme, netloc, path, query, fragment)) netloc = bnetloc if path[:1] == s('/'): segments = path.split(s('/')) elif not path: segments = bpath.split(s('/')) if not query: query = bquery else: segments = bpath.split(s('/'))[:-1] + path.split(s('/')) # If the rightmost part is "./" we want to keep the slash but # remove the dot. if segments[-1] == s('.'): segments[-1] = s('') # Resolve ".." and "." segments = [segment for segment in segments if segment != s('.')] while 1: i = 1 n = len(segments) - 1 while i < n: if segments[i] == s('..') and \ segments[i - 1] not in (s(''), s('..')): del segments[i - 1:i + 1] break i += 1 else: break # Remove trailing ".." if the URL is absolute unwanted_marker = [s(''), s('..')] while segments[:2] == unwanted_marker: del segments[1] path = s('/').join(segments) return url_unparse((scheme, netloc, path, query, fragment))