File: uris.py

package info (click to toggle)
python-lsp-server 1.12.0-3
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 796 kB
  • sloc: python: 7,791; sh: 12; makefile: 4
file content (139 lines) | stat: -rw-r--r-- 3,834 bytes parent folder | download
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
# 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 = "//{}{}".format(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 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