deepnote-python-lsp-server 0.0.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. deepnote_python_lsp_server-0.0.0.dist-info/METADATA +263 -0
  2. deepnote_python_lsp_server-0.0.0.dist-info/RECORD +43 -0
  3. deepnote_python_lsp_server-0.0.0.dist-info/WHEEL +6 -0
  4. deepnote_python_lsp_server-0.0.0.dist-info/entry_points.txt +24 -0
  5. deepnote_python_lsp_server-0.0.0.dist-info/licenses/LICENSE +22 -0
  6. deepnote_python_lsp_server-0.0.0.dist-info/top_level.txt +1 -0
  7. pylsp/__init__.py +33 -0
  8. pylsp/__main__.py +132 -0
  9. pylsp/_utils.py +325 -0
  10. pylsp/config/__init__.py +2 -0
  11. pylsp/config/config.py +202 -0
  12. pylsp/config/flake8_conf.py +64 -0
  13. pylsp/config/pycodestyle_conf.py +34 -0
  14. pylsp/config/source.py +88 -0
  15. pylsp/hookspecs.py +140 -0
  16. pylsp/lsp.py +115 -0
  17. pylsp/plugins/__init__.py +2 -0
  18. pylsp/plugins/_resolvers.py +132 -0
  19. pylsp/plugins/_rope_task_handle.py +106 -0
  20. pylsp/plugins/autopep8_format.py +95 -0
  21. pylsp/plugins/definition.py +84 -0
  22. pylsp/plugins/flake8_lint.py +243 -0
  23. pylsp/plugins/folding.py +210 -0
  24. pylsp/plugins/highlight.py +36 -0
  25. pylsp/plugins/hover.py +57 -0
  26. pylsp/plugins/jedi_completion.py +318 -0
  27. pylsp/plugins/jedi_rename.py +56 -0
  28. pylsp/plugins/mccabe_lint.py +56 -0
  29. pylsp/plugins/preload_imports.py +38 -0
  30. pylsp/plugins/pycodestyle_lint.py +113 -0
  31. pylsp/plugins/pydocstyle_lint.py +127 -0
  32. pylsp/plugins/pyflakes_lint.py +97 -0
  33. pylsp/plugins/pylint_lint.py +367 -0
  34. pylsp/plugins/references.py +33 -0
  35. pylsp/plugins/rope_autoimport.py +407 -0
  36. pylsp/plugins/rope_completion.py +161 -0
  37. pylsp/plugins/signature.py +81 -0
  38. pylsp/plugins/symbols.py +214 -0
  39. pylsp/plugins/yapf_format.py +199 -0
  40. pylsp/python_lsp.py +921 -0
  41. pylsp/text_edit.py +97 -0
  42. pylsp/uris.py +139 -0
  43. pylsp/workspace.py +730 -0
@@ -0,0 +1,127 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ import contextlib
5
+ import logging
6
+ import os
7
+ import re
8
+ import sys
9
+
10
+ import pydocstyle
11
+
12
+ from pylsp import hookimpl, lsp
13
+
14
+ log = logging.getLogger(__name__)
15
+
16
+ # PyDocstyle is a little verbose in debug message
17
+ pydocstyle_logger = logging.getLogger(pydocstyle.utils.__name__)
18
+ pydocstyle_logger.setLevel(logging.INFO)
19
+
20
+ DEFAULT_MATCH_RE = pydocstyle.config.ConfigurationParser.DEFAULT_MATCH_RE
21
+ DEFAULT_MATCH_DIR_RE = pydocstyle.config.ConfigurationParser.DEFAULT_MATCH_DIR_RE
22
+
23
+
24
+ @hookimpl
25
+ def pylsp_settings():
26
+ # Default pydocstyle to disabled
27
+ return {"plugins": {"pydocstyle": {"enabled": False}}}
28
+
29
+
30
+ @hookimpl
31
+ def pylsp_lint(config, workspace, document):
32
+ with workspace.report_progress("lint: pydocstyle"):
33
+ settings = config.plugin_settings("pydocstyle", document_path=document.path)
34
+ log.debug("Got pydocstyle settings: %s", settings)
35
+
36
+ # Explicitly passing a path to pydocstyle means it doesn't respect the --match flag, so do it ourselves
37
+ filename_match_re = re.compile(settings.get("match", DEFAULT_MATCH_RE) + "$")
38
+ if not filename_match_re.match(os.path.basename(document.path)):
39
+ return []
40
+
41
+ # Likewise with --match-dir
42
+ dir_match_re = re.compile(settings.get("matchDir", DEFAULT_MATCH_DIR_RE) + "$")
43
+ if not dir_match_re.match(os.path.basename(os.path.dirname(document.path))):
44
+ return []
45
+
46
+ args = [document.path]
47
+
48
+ if settings.get("convention"):
49
+ args.append("--convention=" + settings["convention"])
50
+
51
+ if settings.get("addSelect"):
52
+ args.append("--add-select=" + ",".join(settings["addSelect"]))
53
+ if settings.get("addIgnore"):
54
+ args.append("--add-ignore=" + ",".join(settings["addIgnore"]))
55
+
56
+ elif settings.get("select"):
57
+ args.append("--select=" + ",".join(settings["select"]))
58
+ elif settings.get("ignore"):
59
+ args.append("--ignore=" + ",".join(settings["ignore"]))
60
+
61
+ log.info("Using pydocstyle args: %s", args)
62
+
63
+ conf = pydocstyle.config.ConfigurationParser()
64
+ with _patch_sys_argv(args):
65
+ # TODO(gatesn): We can add more pydocstyle args here from our pylsp config
66
+ conf.parse()
67
+
68
+ # Will only yield a single filename, the document path
69
+ diags = []
70
+ for (
71
+ filename,
72
+ checked_codes,
73
+ ignore_decorators,
74
+ property_decorators,
75
+ ignore_self_only_init,
76
+ ) in conf.get_files_to_check():
77
+ errors = pydocstyle.checker.ConventionChecker().check_source(
78
+ document.source,
79
+ filename,
80
+ ignore_decorators=ignore_decorators,
81
+ property_decorators=property_decorators,
82
+ ignore_self_only_init=ignore_self_only_init,
83
+ )
84
+
85
+ try:
86
+ for error in errors:
87
+ if error.code not in checked_codes:
88
+ continue
89
+ diags.append(_parse_diagnostic(document, error))
90
+ except pydocstyle.parser.ParseError:
91
+ # In the case we cannot parse the Python file, just continue
92
+ pass
93
+
94
+ log.debug("Got pydocstyle errors: %s", diags)
95
+ return diags
96
+
97
+
98
+ def _parse_diagnostic(document, error):
99
+ lineno = error.definition.start - 1
100
+ line = document.lines[0] if document.lines else ""
101
+
102
+ start_character = len(line) - len(line.lstrip())
103
+ end_character = len(line)
104
+
105
+ return {
106
+ "source": "pydocstyle",
107
+ "code": error.code,
108
+ "message": error.message,
109
+ "severity": lsp.DiagnosticSeverity.Warning,
110
+ "range": {
111
+ "start": {"line": lineno, "character": start_character},
112
+ "end": {"line": lineno, "character": end_character},
113
+ },
114
+ }
115
+
116
+
117
+ @contextlib.contextmanager
118
+ def _patch_sys_argv(arguments) -> None:
119
+ old_args = sys.argv
120
+
121
+ # Preserve argv[0] since it's the executable
122
+ sys.argv = old_args[0:1] + arguments
123
+
124
+ try:
125
+ yield
126
+ finally:
127
+ sys.argv = old_args
@@ -0,0 +1,97 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ from pyflakes import api as pyflakes_api
5
+ from pyflakes import messages
6
+
7
+ from pylsp import hookimpl, lsp
8
+
9
+ # Pyflakes messages that should be reported as Errors instead of Warns
10
+ PYFLAKES_ERROR_MESSAGES = (
11
+ messages.UndefinedName,
12
+ messages.UndefinedExport,
13
+ messages.UndefinedLocal,
14
+ messages.DuplicateArgument,
15
+ messages.FutureFeatureNotDefined,
16
+ messages.ReturnOutsideFunction,
17
+ messages.YieldOutsideFunction,
18
+ messages.ContinueOutsideLoop,
19
+ messages.BreakOutsideLoop,
20
+ messages.TwoStarredExpressions,
21
+ )
22
+
23
+
24
+ @hookimpl
25
+ def pylsp_lint(workspace, document):
26
+ with workspace.report_progress("lint: pyflakes"):
27
+ reporter = PyflakesDiagnosticReport(document.lines)
28
+ pyflakes_api.check(
29
+ document.source.encode("utf-8"), document.path, reporter=reporter
30
+ )
31
+ return reporter.diagnostics
32
+
33
+
34
+ class PyflakesDiagnosticReport:
35
+ def __init__(self, lines) -> None:
36
+ self.lines = lines
37
+ self.diagnostics = []
38
+
39
+ def unexpectedError(self, _filename, msg) -> None: # pragma: no cover
40
+ err_range = {
41
+ "start": {"line": 0, "character": 0},
42
+ "end": {"line": 0, "character": 0},
43
+ }
44
+ self.diagnostics.append(
45
+ {
46
+ "source": "pyflakes",
47
+ "range": err_range,
48
+ "message": msg,
49
+ "severity": lsp.DiagnosticSeverity.Error,
50
+ }
51
+ )
52
+
53
+ def syntaxError(self, _filename, msg, lineno, offset, text) -> None:
54
+ # We've seen that lineno and offset can sometimes be None
55
+ lineno = lineno or 1
56
+ offset = offset or 0
57
+ # could be None if the error is due to an invalid encoding
58
+ # see e.g. https://github.com/python-lsp/python-lsp-server/issues/429
59
+ text = text or ""
60
+
61
+ err_range = {
62
+ "start": {"line": lineno - 1, "character": offset},
63
+ "end": {"line": lineno - 1, "character": offset + len(text)},
64
+ }
65
+ self.diagnostics.append(
66
+ {
67
+ "source": "pyflakes",
68
+ "range": err_range,
69
+ "message": msg,
70
+ "severity": lsp.DiagnosticSeverity.Error,
71
+ }
72
+ )
73
+
74
+ def flake(self, message) -> None:
75
+ """Get message like <filename>:<lineno>: <msg>"""
76
+ err_range = {
77
+ "start": {"line": message.lineno - 1, "character": message.col},
78
+ "end": {
79
+ "line": message.lineno - 1,
80
+ "character": len(self.lines[message.lineno - 1]),
81
+ },
82
+ }
83
+
84
+ severity = lsp.DiagnosticSeverity.Warning
85
+ for message_type in PYFLAKES_ERROR_MESSAGES:
86
+ if isinstance(message, message_type):
87
+ severity = lsp.DiagnosticSeverity.Error
88
+ break
89
+
90
+ self.diagnostics.append(
91
+ {
92
+ "source": "pyflakes",
93
+ "range": err_range,
94
+ "message": message.message % message.message_args,
95
+ "severity": severity,
96
+ }
97
+ )
@@ -0,0 +1,367 @@
1
+ # Copyright 2018 Google LLC.
2
+ # Copyright 2017-2020 Palantir Technologies, Inc.
3
+ # Copyright 2021- Python Language Server Contributors.
4
+
5
+ """Linter plugin for pylint."""
6
+
7
+ import collections
8
+ import logging
9
+ import os
10
+ import re
11
+ import shlex
12
+ import sys
13
+ from subprocess import PIPE, Popen
14
+
15
+ from pylsp import hookimpl, lsp
16
+
17
+ try:
18
+ import ujson as json
19
+ except Exception:
20
+ import json
21
+
22
+ log = logging.getLogger(__name__)
23
+
24
+ # Pylint fails to suppress STDOUT when importing whitelisted C
25
+ # extensions, mangling their output into the expected JSON which breaks the
26
+ # parser. The most prominent example (and maybe the only one out there) is
27
+ # pygame - we work around that by asking pygame to NOT display the message upon
28
+ # import via an (otherwise harmless) environment variable. This is an ad-hoc
29
+ # fix for a very specific upstream issue.
30
+ # Related: https://github.com/PyCQA/pylint/issues/3518
31
+ os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide"
32
+ DEPRECATION_CODES = {
33
+ "W0402", # Uses of a deprecated module %r
34
+ "W1505", # Using deprecated method %s()
35
+ "W1511", # Using deprecated argument %s of method %s()
36
+ "W1512", # Using deprecated class %s of module %s
37
+ "W1513", # Using deprecated decorator %s()
38
+ }
39
+ UNNECESSITY_CODES = {
40
+ "W0611", # Unused import %s
41
+ "W0612", # Unused variable %r
42
+ "W0613", # Unused argument %r
43
+ "W0614", # Unused import %s from wildcard import
44
+ "W1304", # Unused-format-string-argument
45
+ }
46
+
47
+
48
+ class PylintLinter:
49
+ last_diags = collections.defaultdict(list)
50
+
51
+ @classmethod
52
+ def lint(cls, document, is_saved, flags=""):
53
+ """Plugin interface to pylsp linter.
54
+
55
+ Args:
56
+ document: The document to be linted.
57
+ is_saved: Whether or not the file has been saved to disk.
58
+ flags: Additional flags to pass to pylint. Not exposed to
59
+ pylsp_lint, but used for testing.
60
+
61
+ Returns:
62
+ A list of dicts with the following format:
63
+
64
+ {
65
+ 'source': 'pylint',
66
+ 'range': {
67
+ 'start': {
68
+ 'line': start_line,
69
+ 'character': start_column,
70
+ },
71
+ 'end': {
72
+ 'line': end_line,
73
+ 'character': end_column,
74
+ },
75
+ }
76
+ 'message': msg,
77
+ 'severity': lsp.DiagnosticSeverity.*,
78
+ }
79
+ """
80
+ if not is_saved:
81
+ # Pylint can only be run on files that have been saved to disk.
82
+ # Rather than return nothing, return the previous list of
83
+ # diagnostics. If we return an empty list, any diagnostics we'd
84
+ # previously shown will be cleared until the next save. Instead,
85
+ # continue showing (possibly stale) diagnostics until the next
86
+ # save.
87
+ return cls.last_diags[document.path]
88
+
89
+ cmd = [
90
+ sys.executable,
91
+ "-c",
92
+ "import sys; from pylint.lint import Run; Run(sys.argv[1:])",
93
+ "-f",
94
+ "json",
95
+ document.path,
96
+ ] + (shlex.split(str(flags)) if flags else [])
97
+ log.debug("Calling pylint with '%s'", " ".join(cmd))
98
+
99
+ cwd = document._workspace.root_path
100
+ if not cwd:
101
+ cwd = os.path.dirname(__file__)
102
+
103
+ with Popen(
104
+ cmd, stdout=PIPE, stderr=PIPE, cwd=cwd, universal_newlines=True
105
+ ) as process:
106
+ json_out, err = process.communicate()
107
+
108
+ if err != "":
109
+ log.error("Error calling pylint: '%s'", err)
110
+
111
+ # pylint prints nothing rather than [] when there are no diagnostics.
112
+ # json.loads will not parse an empty string, so just return.
113
+ if not json_out.strip():
114
+ cls.last_diags[document.path] = []
115
+ return []
116
+
117
+ # Pylint's JSON output is a list of objects with the following format.
118
+ #
119
+ # {
120
+ # "obj": "main",
121
+ # "path": "foo.py",
122
+ # "message": "Missing function docstring",
123
+ # "message-id": "C0111",
124
+ # "symbol": "missing-docstring",
125
+ # "column": 0,
126
+ # "type": "convention",
127
+ # "line": 5,
128
+ # "module": "foo"
129
+ # }
130
+ #
131
+ # The type can be any of:
132
+ #
133
+ # * convention
134
+ # * information
135
+ # * error
136
+ # * fatal
137
+ # * refactor
138
+ # * warning
139
+ diagnostics = []
140
+ for diag in json.loads(json_out):
141
+ # pylint lines index from 1, pylsp lines index from 0
142
+ line = diag["line"] - 1
143
+
144
+ err_range = {
145
+ "start": {
146
+ "line": line,
147
+ # Index columns start from 0
148
+ "character": diag["column"],
149
+ },
150
+ "end": {
151
+ "line": line,
152
+ # It's possible that we're linting an empty file. Even an empty
153
+ # file might fail linting if it isn't named properly.
154
+ "character": (
155
+ _find_end_of_identifier(document.lines[line], diag["column"])
156
+ if document.lines
157
+ else 0
158
+ ),
159
+ },
160
+ }
161
+
162
+ if diag["type"] == "convention":
163
+ severity = lsp.DiagnosticSeverity.Information
164
+ elif diag["type"] == "information":
165
+ severity = lsp.DiagnosticSeverity.Information
166
+ elif diag["type"] == "error":
167
+ severity = lsp.DiagnosticSeverity.Error
168
+ elif diag["type"] == "fatal":
169
+ severity = lsp.DiagnosticSeverity.Error
170
+ elif diag["type"] == "refactor":
171
+ severity = lsp.DiagnosticSeverity.Hint
172
+ elif diag["type"] == "warning":
173
+ severity = lsp.DiagnosticSeverity.Warning
174
+
175
+ code = diag["message-id"]
176
+
177
+ diagnostic = {
178
+ "source": "pylint",
179
+ "range": err_range,
180
+ "message": "[{}] {}".format(diag["symbol"], diag["message"]),
181
+ "severity": severity,
182
+ "code": code,
183
+ }
184
+
185
+ if code in UNNECESSITY_CODES:
186
+ diagnostic["tags"] = [lsp.DiagnosticTag.Unnecessary]
187
+ if code in DEPRECATION_CODES:
188
+ diagnostic["tags"] = [lsp.DiagnosticTag.Deprecated]
189
+
190
+ diagnostics.append(diagnostic)
191
+ cls.last_diags[document.path] = diagnostics
192
+ return diagnostics
193
+
194
+
195
+ def _build_pylint_flags(settings):
196
+ """Build arguments for calling pylint."""
197
+ pylint_args = settings.get("args")
198
+ if pylint_args is None:
199
+ return ""
200
+ return " ".join(pylint_args)
201
+
202
+
203
+ @hookimpl
204
+ def pylsp_settings():
205
+ # Default pylint to disabled because it requires a config
206
+ # file to be useful.
207
+ return {
208
+ "plugins": {
209
+ "pylint": {
210
+ "enabled": False,
211
+ "args": [],
212
+ # disabled by default as it can slow down the workflow
213
+ "executable": None,
214
+ }
215
+ }
216
+ }
217
+
218
+
219
+ @hookimpl
220
+ def pylsp_lint(config, workspace, document, is_saved):
221
+ """Run pylint linter."""
222
+ with workspace.report_progress("lint: pylint"):
223
+ settings = config.plugin_settings("pylint")
224
+ log.debug("Got pylint settings: %s", settings)
225
+ # pylint >= 2.5.0 is required for working through stdin and only
226
+ # available with python3
227
+ if settings.get("executable") and sys.version_info[0] >= 3:
228
+ flags = build_args_stdio(settings)
229
+ pylint_executable = settings.get("executable", "pylint")
230
+ return pylint_lint_stdin(pylint_executable, document, flags)
231
+ flags = _build_pylint_flags(settings)
232
+ return PylintLinter.lint(document, is_saved, flags=flags)
233
+
234
+
235
+ def build_args_stdio(settings):
236
+ """Build arguments for calling pylint.
237
+
238
+ :param settings: client settings
239
+ :type settings: dict
240
+
241
+ :return: arguments to path to pylint
242
+ :rtype: list
243
+ """
244
+ pylint_args = settings.get("args")
245
+ if pylint_args is None:
246
+ return []
247
+ return pylint_args
248
+
249
+
250
+ def pylint_lint_stdin(pylint_executable, document, flags):
251
+ """Run pylint linter from stdin.
252
+
253
+ This runs pylint in a subprocess with popen.
254
+ This allows passing the file from stdin and as a result
255
+ run pylint on unsaved files. Can slowdown the workflow.
256
+
257
+ :param pylint_executable: path to pylint executable
258
+ :type pylint_executable: string
259
+ :param document: document to run pylint on
260
+ :type document: pylsp.workspace.Document
261
+ :param flags: arguments to path to pylint
262
+ :type flags: list
263
+
264
+ :return: linting diagnostics
265
+ :rtype: list
266
+ """
267
+ pylint_result = _run_pylint_stdio(pylint_executable, document, flags)
268
+ return _parse_pylint_stdio_result(document, pylint_result)
269
+
270
+
271
+ def _run_pylint_stdio(pylint_executable, document, flags):
272
+ """Run pylint in popen.
273
+
274
+ :param pylint_executable: path to pylint executable
275
+ :type pylint_executable: string
276
+ :param document: document to run pylint on
277
+ :type document: pylsp.workspace.Document
278
+ :param flags: arguments to path to pylint
279
+ :type flags: list
280
+
281
+ :return: result of calling pylint
282
+ :rtype: string
283
+ """
284
+ log.debug("Calling %s with args: '%s'", pylint_executable, flags)
285
+ try:
286
+ cmd = [pylint_executable]
287
+ cmd.extend(flags)
288
+ cmd.extend(["--from-stdin", document.path])
289
+ p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
290
+ except IOError:
291
+ log.debug("Can't execute %s. Trying with 'python -m pylint'", pylint_executable)
292
+ cmd = [sys.executable, "-m", "pylint"]
293
+ cmd.extend(flags)
294
+ cmd.extend(["--from-stdin", document.path])
295
+ p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
296
+ (stdout, stderr) = p.communicate(document.source.encode())
297
+ if stderr:
298
+ log.error("Error while running pylint '%s'", stderr.decode())
299
+ return stdout.decode()
300
+
301
+
302
+ def _parse_pylint_stdio_result(document, stdout):
303
+ """Parse pylint results.
304
+
305
+ :param document: document to run pylint on
306
+ :type document: pylsp.workspace.Document
307
+ :param stdout: pylint results to parse
308
+ :type stdout: string
309
+
310
+ :return: linting diagnostics
311
+ :rtype: list
312
+ """
313
+ diagnostics = []
314
+ lines = stdout.splitlines()
315
+ for raw_line in lines:
316
+ parsed_line = re.match(r"(.*):(\d*):(\d*): (\w*): (.*)", raw_line)
317
+ if not parsed_line:
318
+ log.debug("Pylint output parser can't parse line '%s'", raw_line)
319
+ continue
320
+
321
+ parsed_line = parsed_line.groups()
322
+ if len(parsed_line) != 5:
323
+ log.debug("Pylint output parser can't parse line '%s'", raw_line)
324
+ continue
325
+
326
+ _, line, character, code, msg = parsed_line
327
+ line = int(line) - 1
328
+ character = int(character)
329
+ severity_map = {
330
+ "C": lsp.DiagnosticSeverity.Information,
331
+ "E": lsp.DiagnosticSeverity.Error,
332
+ "F": lsp.DiagnosticSeverity.Error,
333
+ "I": lsp.DiagnosticSeverity.Information,
334
+ "R": lsp.DiagnosticSeverity.Hint,
335
+ "W": lsp.DiagnosticSeverity.Warning,
336
+ }
337
+ severity = severity_map[code[0]]
338
+ diagnostic = {
339
+ "source": "pylint",
340
+ "code": code,
341
+ "range": {
342
+ "start": {"line": line, "character": character},
343
+ "end": {
344
+ "line": line,
345
+ "character": _find_end_of_identifier(
346
+ document.lines[line], character
347
+ ),
348
+ },
349
+ },
350
+ "message": msg,
351
+ "severity": severity,
352
+ }
353
+ if code in UNNECESSITY_CODES:
354
+ diagnostic["tags"] = [lsp.DiagnosticTag.Unnecessary]
355
+ if code in DEPRECATION_CODES:
356
+ diagnostic["tags"] = [lsp.DiagnosticTag.Deprecated]
357
+ diagnostics.append(diagnostic)
358
+
359
+ return diagnostics
360
+
361
+
362
+ def _find_end_of_identifier(string, start):
363
+ """Find the end of the identifier starting at the given position."""
364
+ for i in range(len(string), start, -1):
365
+ if string[start:i].isidentifier():
366
+ return i
367
+ return len(string) - 1
@@ -0,0 +1,33 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ import logging
5
+
6
+ from pylsp import _utils, hookimpl, uris
7
+
8
+ log = logging.getLogger(__name__)
9
+
10
+
11
+ @hookimpl
12
+ def pylsp_references(document, position, exclude_declaration):
13
+ code_position = _utils.position_to_jedi_linecolumn(document, position)
14
+ usages = document.jedi_script().get_references(**code_position)
15
+
16
+ if exclude_declaration:
17
+ # Filter out if the usage is the actual declaration of the thing
18
+ usages = [d for d in usages if not d.is_definition()]
19
+
20
+ # Filter out builtin modules
21
+ return [
22
+ {
23
+ "uri": uris.uri_with(document.uri, path=str(d.module_path))
24
+ if d.module_path
25
+ else document.uri,
26
+ "range": {
27
+ "start": {"line": d.line - 1, "character": d.column},
28
+ "end": {"line": d.line - 1, "character": d.column + len(d.name)},
29
+ },
30
+ }
31
+ for d in usages
32
+ if not d.in_builtin_module()
33
+ ]