1import os
2import string
3import urllib.parse
4import urllib.request
5
6from .compat import WINDOWS
7
8
9def path_to_url(path: str) -> str:
10 """
11 Convert a path to a file: URL. The path will be made absolute and have
12 quoted path parts.
13 """
14 path = os.path.normpath(os.path.abspath(path))
15 url = urllib.parse.urljoin("file://", urllib.request.pathname2url(path))
16 return url
17
18
19def url_to_path(url: str) -> str:
20 """
21 Convert a file: URL to a path.
22 """
23 assert url.startswith(
24 "file:"
25 ), f"You can only turn file: urls into filenames (not {url!r})"
26
27 _, netloc, path, _, _ = urllib.parse.urlsplit(url)
28
29 if not netloc or netloc == "localhost":
30 # According to RFC 8089, same as empty authority.
31 netloc = ""
32 elif WINDOWS:
33 # If we have a UNC path, prepend UNC share notation.
34 netloc = "\\\\" + netloc
35 else:
36 raise ValueError(
37 f"non-local file URIs are not supported on this platform: {url!r}"
38 )
39
40 path = urllib.request.url2pathname(netloc + path)
41
42 # On Windows, urlsplit parses the path as something like "/C:/Users/foo".
43 # This creates issues for path-related functions like io.open(), so we try
44 # to detect and strip the leading slash.
45 if (
46 WINDOWS
47 and not netloc # Not UNC.
48 and len(path) >= 3
49 and path[0] == "/" # Leading slash to strip.
50 and path[1] in string.ascii_letters # Drive letter.
51 and path[2:4] in (":", ":/") # Colon + end of string, or colon + absolute path.
52 ):
53 path = path[1:]
54
55 return path