Repository navigation
Expand file tree
/
Copy pathuris.py
More file actions
153 lines (119 loc) · 4.3 KB
/
Copy pathuris.py
File metadata and controls
153 lines (119 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright 2017-2020 Palantir Technologies, Inc.
# Copyright 2021- Python Language Server Contributors.
"""A collection of URI utilities with logic built on the VSCode URI library.
https://github.com/Microsoft/vscode-uri/blob/e59cab84f5df6265aed18ae5f43552d3eef13bb9/lib/index.ts
"""
import re
from urllib import parse
from pylsp import IS_WIN
RE_DRIVE_LETTER_PATH = re.compile(r"^\/[a-zA-Z]:")
def urlparse(uri):
"""Parse and decode the parts of a URI."""
scheme, netloc, path, params, query, fragment = parse.urlparse(uri)
return (
parse.unquote(scheme),
parse.unquote(netloc),
parse.unquote(path),
parse.unquote(params),
parse.unquote(query),
parse.unquote(fragment),
)
def urlunparse(parts):
"""Unparse and encode parts of a URI."""
scheme, netloc, path, params, query, fragment = parts
# Avoid encoding the windows drive letter colon
if RE_DRIVE_LETTER_PATH.match(path):
quoted_path = path[:3] + parse.quote(path[3:])
else:
quoted_path = parse.quote(path)
return parse.urlunparse(
(
parse.quote(scheme),
parse.quote(netloc),
quoted_path,
parse.quote(params),
parse.quote(query),
parse.quote(fragment),
)
)
def to_fs_path(uri):
"""Returns the filesystem path of the given URI.
Will handle UNC paths and normalize windows drive letters to lower-case. Also
uses the platform specific path separator. Will *not* validate the path for
invalid characters and semantics. Will *not* look at the scheme of this URI.
"""
# scheme://netloc/path;parameters?query#fragment
scheme, netloc, path, _params, _query, _fragment = urlparse(uri)
if netloc and path and scheme == "file":
# unc path: file://shares/c$/far/boo
value = f"//{netloc}{path}"
elif RE_DRIVE_LETTER_PATH.match(path):
# windows drive letter: file:///C:/far/boo
value = path[1].lower() + path[2:]
else:
# Other path
value = path
if IS_WIN:
value = value.replace("/", "\\")
return value
def from_fs_path(path):
"""Returns a URI for the given filesystem path."""
scheme = "file"
params, query, fragment = "", "", ""
path, netloc = _normalize_win_path(path)
return urlunparse((scheme, netloc, path, params, query, fragment))
def normalize(uri):
"""Return a canonical form of the given URI.
Windows drive letters are case-insensitive, so clients may send either
``file:///C:/foo`` or ``file:///c:/foo`` for the same file. Both forms
normalize to the lower-case one used by :func:`from_fs_path`, so that a
URI can be used as a stable dictionary key.
"""
scheme, netloc, path, params, query, fragment = urlparse(uri)
if RE_DRIVE_LETTER_PATH.match(path):
path = path[0] + path[1].lower() + path[2:]
return urlunparse((scheme, netloc, path, params, query, fragment))
def uri_with(
uri, scheme=None, netloc=None, path=None, params=None, query=None, fragment=None
):
"""Return a URI with the given part(s) replaced.
Parts are decoded / encoded.
"""
old_scheme, old_netloc, old_path, old_params, old_query, old_fragment = urlparse(
uri
)
path, _netloc = _normalize_win_path(path)
return urlunparse(
(
scheme or old_scheme,
netloc or old_netloc,
path or old_path,
params or old_params,
query or old_query,
fragment or old_fragment,
)
)
def _normalize_win_path(path):
netloc = ""
# normalize to fwd-slashes on windows,
# on other systems bwd-slaches are valid
# filename character, eg /f\oo/ba\r.txt
if IS_WIN:
path = path.replace("\\", "/")
# check for authority as used in UNC shares
# or use the path as given
if path[:2] == "//":
idx = path.index("/", 2)
if idx == -1:
netloc = path[2:]
else:
netloc = path[2:idx]
path = path[idx:]
# Ensure that path starts with a slash
# or that it is at least a slash
if not path.startswith("/"):
path = "/" + path
# Normalize drive paths to lower case
if RE_DRIVE_LETTER_PATH.match(path):
path = path[0] + path[1].lower() + path[2:]
return path, netloc