How to join components of a path when you are constructing a URL in Python How to join components of a path when you are constructing a URL in Python python python

How to join components of a path when you are constructing a URL in Python


You can use urllib.parse.urljoin:

>>> from urllib.parse import urljoin>>> urljoin('/media/path/', 'js/foo.js')'/media/path/js/foo.js'

But beware:

>>> urljoin('/media/path', 'js/foo.js')'/media/js/foo.js'>>> urljoin('/media/path', '/js/foo.js')'/js/foo.js'

The reason you get different results from /js/foo.js and js/foo.js is because the former begins with a slash which signifies that it already begins at the website root.

On Python 2, you have to do

from urlparse import urljoin


Since, from the comments the OP posted, it seems he doesn't want to preserve "absolute URLs" in the join (which is one of the key jobs of urlparse.urljoin;-), I'd recommend avoiding that. os.path.join would also be bad, for exactly the same reason.

So, I'd use something like '/'.join(s.strip('/') for s in pieces) (if the leading / must also be ignored -- if the leading piece must be special-cased, that's also feasible of course;-).


Like you say, os.path.join joins paths based on the current os. posixpath is the underlying module that is used on posix systems under the namespace os.path:

>>> os.path.join is posixpath.joinTrue>>> posixpath.join('/media/', 'js/foo.js')'/media/js/foo.js'

So you can just import and use posixpath.join instead for urls, which is available and will work on any platform.

Edit: @Pete's suggestion is a good one, you can alias the import for increased readability

from posixpath import join as urljoin

Edit: I think this is made clearer, or at least helped me understand, if you look into the source of os.py (the code here is from Python 2.7.11, plus I've trimmed some bits). There's conditional imports in os.py that picks which path module to use in the namespace os.path. All the underlying modules (posixpath, ntpath, os2emxpath, riscospath) that may be imported in os.py, aliased as path, are there and exist to be used on all systems. os.py is just picking one of the modules to use in the namespace os.path at run time based on the current OS.

# os.pyimport sys, errno_names = sys.builtin_module_namesif 'posix' in _names:    # ...    from posix import *    # ...    import posixpath as path    # ...elif 'nt' in _names:    # ...    from nt import *    # ...    import ntpath as path    # ...elif 'os2' in _names:    # ...    from os2 import *    # ...    if sys.version.find('EMX GCC') == -1:        import ntpath as path    else:        import os2emxpath as path        from _emx_link import link    # ...elif 'ce' in _names:    # ...    from ce import *    # ...    # We can use the standard Windows path.    import ntpath as pathelif 'riscos' in _names:    # ...    from riscos import *    # ...    import riscospath as path    # ...else:    raise ImportError, 'no os specific module found'