Skip to content

Instantly share code, notes, and snippets.

@DamienGarrido
Last active January 7, 2020 17:55
Show Gist options
  • Save DamienGarrido/457d0a3a6d4f0a5f80081ca4262260c4 to your computer and use it in GitHub Desktop.
Save DamienGarrido/457d0a3a6d4f0a5f80081ca4262260c4 to your computer and use it in GitHub Desktop.
Python URL path join
#!/usr/bin/env python3
# coding: utf8
import re
import urllib.parse
def url_path_join(base_url, *paths):
parsed_url = urllib.parse.urlsplit(base_url)
base_path = re.sub('/+', '/', parsed_url.path)
if paths:
if len(base_path) and base_path[-1] != '/':
base_path += '/'
for path in paths[:-1]:
path = re.sub('/+', '/', path).lstrip('/') + '/'
base_path = urllib.parse.urljoin(base_path, path)
path = paths[-1]
path = re.sub('/+', '/', path).lstrip('/')
base_path = urllib.parse.urljoin(base_path, path)
return urllib.parse.urlunsplit(parsed_url._replace(path=base_path))
# Some random tests
urls = [
('http://example.com/relative/url/to/file', 'http://example.com', 'relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com/', 'relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com//', 'relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com/', '/relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com/', '../relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com/', '/../relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com/base', '../relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com/base/', '../relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com//base/', '../relative/url/to/file' ),
('http://example.com/relative/url/to/file/', 'http://example.com/base//', '../relative/url/to/file/' ),
('http://example.com/relative/url/to/file', 'http://example.com/base', '/../relative/url/to/file' ),
('http://example.com/relative/url/to/file', 'http://example.com/base/', '/../relative/url/to/file' ),
('http://example.com/relative/url/to/file/', 'http://example.com/base/', '/../relative/url/to/file/' ),
('http://example.com/base/relative/url/to/file', 'http://example.com/base', '/relative/url/to/file' ),
('http://example.com/base/relative/url/to/file', 'http://example.com/base/', '/relative/url/to/file' ),
('http://example.com/base/relative/url/to/file', 'http://example.com/base', 'relative/url/to/file' ),
('http://example.com/base/relative/url/to/file', 'http://example.com/base/', '//relative//url//', '/to/file' ),
('http://example.com/base/relative/url/to/my/file', 'http://example.com/base/', 'relative/url', '/to/', 'my/file' ),
('http://example.com/base/relative/url/to/my/file/', 'http://example.com/base/', 'relative/url', '/to/', 'my/file/' ),
('http://example.com/base/relative/url/to/file?a=a/b/c#here', 'http://example.com/base/', 'relative//url', '//to//file?a=a/b/c#here' ),
('https://username:password@example.com:8443/base/relative/url/to/new/path/to/file/?a=a/b/c&b=//d//e//f//#here', 'https://username:password@example.com:8443/base/relative/url/to/file?a=a/b/c&b=//d//e//f//#here', '//..//new//path//', '//to//file//')
]
def test(func):
counter = 0
for joined_url, base_url, *relative_urls in urls:
print("Test {num}:".format(num=counter))
print("Base URL : '{url}'".format(url=base_url))
print("Relative URL: '{url}'".format(url=relative_urls))
computed_url = func(base_url, *relative_urls)
print("Computed URL: {url}".format(url=computed_url))
print("Joined URL : {url}".format(url=joined_url))
assert computed_url == joined_url
counter += 1
test(url_path_join)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment