deepnote-python-lsp-server 1.13.1rc2__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 (46) hide show
  1. deepnote_python_lsp_server-1.13.1rc2.dist-info/METADATA +264 -0
  2. deepnote_python_lsp_server-1.13.1rc2.dist-info/RECORD +46 -0
  3. deepnote_python_lsp_server-1.13.1rc2.dist-info/WHEEL +5 -0
  4. deepnote_python_lsp_server-1.13.1rc2.dist-info/entry_points.txt +25 -0
  5. deepnote_python_lsp_server-1.13.1rc2.dist-info/licenses/LICENSE +22 -0
  6. deepnote_python_lsp_server-1.13.1rc2.dist-info/top_level.txt +1 -0
  7. pylsp/__init__.py +33 -0
  8. pylsp/__main__.py +132 -0
  9. pylsp/_utils.py +418 -0
  10. pylsp/_version.py +1 -0
  11. pylsp/config/__init__.py +2 -0
  12. pylsp/config/config.py +203 -0
  13. pylsp/config/flake8_conf.py +64 -0
  14. pylsp/config/pycodestyle_conf.py +34 -0
  15. pylsp/config/source.py +88 -0
  16. pylsp/hookspecs.py +150 -0
  17. pylsp/lsp.py +115 -0
  18. pylsp/plugins/__init__.py +2 -0
  19. pylsp/plugins/_resolvers.py +132 -0
  20. pylsp/plugins/_rope_task_handle.py +107 -0
  21. pylsp/plugins/autopep8_format.py +95 -0
  22. pylsp/plugins/definition.py +84 -0
  23. pylsp/plugins/flake8_lint.py +243 -0
  24. pylsp/plugins/folding.py +210 -0
  25. pylsp/plugins/highlight.py +36 -0
  26. pylsp/plugins/hover.py +59 -0
  27. pylsp/plugins/jedi_completion.py +337 -0
  28. pylsp/plugins/jedi_rename.py +56 -0
  29. pylsp/plugins/mccabe_lint.py +56 -0
  30. pylsp/plugins/preload_imports.py +72 -0
  31. pylsp/plugins/pycodestyle_lint.py +113 -0
  32. pylsp/plugins/pydocstyle_lint.py +127 -0
  33. pylsp/plugins/pyflakes_lint.py +97 -0
  34. pylsp/plugins/pylint_lint.py +367 -0
  35. pylsp/plugins/references.py +33 -0
  36. pylsp/plugins/rope_autoimport.py +408 -0
  37. pylsp/plugins/rope_completion.py +161 -0
  38. pylsp/plugins/signature.py +81 -0
  39. pylsp/plugins/symbols.py +219 -0
  40. pylsp/plugins/type_definition.py +38 -0
  41. pylsp/plugins/yapf_format.py +199 -0
  42. pylsp/py.typed +0 -0
  43. pylsp/python_lsp.py +945 -0
  44. pylsp/text_edit.py +97 -0
  45. pylsp/uris.py +139 -0
  46. pylsp/workspace.py +791 -0
@@ -0,0 +1,219 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ import logging
5
+ import re
6
+ from pathlib import Path
7
+
8
+ from pylsp import hookimpl
9
+ from pylsp.lsp import SymbolKind
10
+
11
+ log = logging.getLogger(__name__)
12
+
13
+
14
+ @hookimpl
15
+ def pylsp_document_symbols(config, document):
16
+ symbols_settings = config.plugin_settings("jedi_symbols")
17
+ all_scopes = symbols_settings.get("all_scopes", True)
18
+ add_import_symbols = symbols_settings.get("include_import_symbols", True)
19
+ definitions = document.jedi_names(all_scopes=all_scopes)
20
+ symbols = []
21
+ exclude = set({})
22
+ redefinitions = {}
23
+ pattern_import = re.compile(
24
+ r"^\s*(?!#)\s*(from\s+[.\w]+(\.[\w]+)*\s+import\s+[\w\s,()*]+|import\s+[\w\s,.*]+)"
25
+ )
26
+
27
+ while definitions != []:
28
+ d = definitions.pop(0)
29
+
30
+ # Skip symbols imported from other modules.
31
+ if not add_import_symbols:
32
+ # Skip if there's an import in the code the symbol is defined.
33
+ code = d.get_line_code()
34
+
35
+ if pattern_import.match(code):
36
+ continue
37
+
38
+ # Skip imported symbols comparing module names.
39
+ sym_full_name = d.full_name
40
+ if sym_full_name is not None:
41
+ document_dot_path = document.dot_path
42
+
43
+ # We assume a symbol is imported from another module to start
44
+ # with.
45
+ imported_symbol = True
46
+
47
+ # The last element of sym_full_name is the symbol itself, so
48
+ # we need to discard it to do module comparisons below.
49
+ if "." in sym_full_name:
50
+ sym_module_name = sym_full_name.rpartition(".")[0]
51
+ else:
52
+ sym_module_name = sym_full_name
53
+
54
+ # This is necessary to display symbols in init files (the checks
55
+ # below fail without it).
56
+ if document_dot_path.endswith("__init__"):
57
+ document_dot_path = document_dot_path.rpartition(".")[0]
58
+
59
+ # document_dot_path is the module where the symbol is imported,
60
+ # whereas sym_module_name is the one where it was declared.
61
+ if document_dot_path in sym_module_name:
62
+ # If document_dot_path is in sym_module_name, we can safely assume
63
+ # that the symbol was declared in the document.
64
+ imported_symbol = False
65
+ elif sym_module_name.split(".")[0] in document_dot_path.split("."):
66
+ # If the first module in sym_module_name is one of the modules in
67
+ # document_dot_path, we need to check if sym_module_name starts
68
+ # with the modules in document_dot_path.
69
+ document_mods = document_dot_path.split(".")
70
+ for i in range(1, len(document_mods) + 1):
71
+ submod = ".".join(document_mods[-i:])
72
+ if sym_module_name.startswith(submod):
73
+ imported_symbol = False
74
+ break
75
+
76
+ # When there's no __init__.py next to a file or in one of its
77
+ # parents, the checks above fail. However, Jedi has a nice way
78
+ # to tell if the symbol was declared in the same file: if
79
+ # sym_module_name starts by __main__.
80
+ if imported_symbol:
81
+ if not sym_module_name.startswith("__main__"):
82
+ continue
83
+ else:
84
+ # We need to skip symbols if their definition doesn't have `full_name` info, they
85
+ # are detected as a definition, but their description (e.g. `class Foo`) doesn't
86
+ # match the code where they're detected by Jedi. This happens for relative imports.
87
+ if _include_def(d):
88
+ if d.description not in d.get_line_code():
89
+ continue
90
+ else:
91
+ continue
92
+
93
+ if _include_def(d) and Path(document.path) == Path(d.module_path):
94
+ tuple_range = _tuple_range(d)
95
+ if tuple_range in exclude:
96
+ continue
97
+
98
+ kind = redefinitions.get(tuple_range, None)
99
+ if kind is not None:
100
+ exclude |= {tuple_range}
101
+
102
+ if d.type == "statement":
103
+ if d.description.startswith("self"):
104
+ kind = "field"
105
+
106
+ symbol = {
107
+ "name": d.name,
108
+ "containerName": _container(d),
109
+ "location": {
110
+ "uri": document.uri,
111
+ "range": _range(d),
112
+ },
113
+ "kind": _kind(d) if kind is None else _SYMBOL_KIND_MAP[kind],
114
+ }
115
+ symbols.append(symbol)
116
+
117
+ if d.type == "class":
118
+ try:
119
+ defined_names = list(d.defined_names())
120
+ for method in defined_names:
121
+ if method.type == "function":
122
+ redefinitions[_tuple_range(method)] = "method"
123
+ elif method.type == "statement":
124
+ redefinitions[_tuple_range(method)] = "field"
125
+ else:
126
+ redefinitions[_tuple_range(method)] = method.type
127
+ definitions = list(defined_names) + definitions
128
+ except Exception:
129
+ pass
130
+ return symbols
131
+
132
+
133
+ def _include_def(definition):
134
+ return (
135
+ # Don't tend to include parameters as symbols
136
+ definition.type != "param"
137
+ and
138
+ # Unused vars should also be skipped
139
+ definition.name != "_"
140
+ and _kind(definition) is not None
141
+ )
142
+
143
+
144
+ def _container(definition):
145
+ try:
146
+ # Jedi sometimes fails here.
147
+ parent = definition.parent()
148
+ # Here we check that a grand-parent exists to avoid declaring symbols
149
+ # as children of the module.
150
+ if parent.parent():
151
+ return parent.name
152
+ except:
153
+ return None
154
+
155
+ return None
156
+
157
+
158
+ def _range(definition):
159
+ # This gets us more accurate end position
160
+ definition = definition._name.tree_name.get_definition()
161
+ (start_line, start_column) = definition.start_pos
162
+ (end_line, end_column) = definition.end_pos
163
+ return {
164
+ "start": {"line": start_line - 1, "character": start_column},
165
+ "end": {"line": end_line - 1, "character": end_column},
166
+ }
167
+
168
+
169
+ def _tuple_range(definition):
170
+ definition = definition._name.tree_name.get_definition()
171
+ return (definition.start_pos, definition.end_pos)
172
+
173
+
174
+ _SYMBOL_KIND_MAP = {
175
+ "none": SymbolKind.Variable,
176
+ "type": SymbolKind.Class,
177
+ "tuple": SymbolKind.Class,
178
+ "dict": SymbolKind.Class,
179
+ "dictionary": SymbolKind.Class,
180
+ "function": SymbolKind.Function,
181
+ "lambda": SymbolKind.Function,
182
+ "generator": SymbolKind.Function,
183
+ "class": SymbolKind.Class,
184
+ "instance": SymbolKind.Class,
185
+ "method": SymbolKind.Method,
186
+ "builtin": SymbolKind.Class,
187
+ "builtinfunction": SymbolKind.Function,
188
+ "module": SymbolKind.Module,
189
+ "file": SymbolKind.File,
190
+ "xrange": SymbolKind.Array,
191
+ "slice": SymbolKind.Class,
192
+ "traceback": SymbolKind.Class,
193
+ "frame": SymbolKind.Class,
194
+ "buffer": SymbolKind.Array,
195
+ "dictproxy": SymbolKind.Class,
196
+ "funcdef": SymbolKind.Function,
197
+ "property": SymbolKind.Property,
198
+ "import": SymbolKind.Module,
199
+ "keyword": SymbolKind.Variable,
200
+ "constant": SymbolKind.Constant,
201
+ "variable": SymbolKind.Variable,
202
+ "value": SymbolKind.Variable,
203
+ "param": SymbolKind.Variable,
204
+ "statement": SymbolKind.Variable,
205
+ "boolean": SymbolKind.Boolean,
206
+ "int": SymbolKind.Number,
207
+ "longlean": SymbolKind.Number,
208
+ "float": SymbolKind.Number,
209
+ "complex": SymbolKind.Number,
210
+ "string": SymbolKind.String,
211
+ "unicode": SymbolKind.String,
212
+ "list": SymbolKind.Array,
213
+ "field": SymbolKind.Field,
214
+ }
215
+
216
+
217
+ def _kind(d):
218
+ """Return the VSCode Symbol Type"""
219
+ return _SYMBOL_KIND_MAP.get(d.type)
@@ -0,0 +1,38 @@
1
+ # Copyright 2021- Python Language Server Contributors.
2
+
3
+ import logging
4
+
5
+ from pylsp import _utils, hookimpl
6
+
7
+ log = logging.getLogger(__name__)
8
+
9
+
10
+ def lsp_location(name):
11
+ module_path = name.module_path
12
+ if module_path is None or name.line is None or name.column is None:
13
+ return None
14
+ uri = module_path.as_uri()
15
+ return {
16
+ "uri": str(uri),
17
+ "range": {
18
+ "start": {"line": name.line - 1, "character": name.column},
19
+ "end": {"line": name.line - 1, "character": name.column + len(name.name)},
20
+ },
21
+ }
22
+
23
+
24
+ @hookimpl
25
+ def pylsp_type_definition(config, document, position):
26
+ try:
27
+ kwargs = _utils.position_to_jedi_linecolumn(document, position)
28
+ script = document.jedi_script()
29
+ names = script.infer(**kwargs)
30
+ definitions = [
31
+ definition
32
+ for definition in [lsp_location(name) for name in names]
33
+ if definition is not None
34
+ ]
35
+ return definitions
36
+ except Exception as e:
37
+ log.debug("Failed to run type_definition: %s", e)
38
+ return []
@@ -0,0 +1,199 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ import logging
5
+ import os
6
+
7
+ import whatthepatch
8
+ from yapf.yapflib import file_resources, style
9
+ from yapf.yapflib.yapf_api import FormatCode
10
+
11
+ from pylsp import hookimpl
12
+ from pylsp._utils import get_eol_chars
13
+
14
+ log = logging.getLogger(__name__)
15
+
16
+
17
+ @hookimpl
18
+ def pylsp_format_document(workspace, document, options):
19
+ log.info("Formatting document %s with yapf", document)
20
+ with workspace.report_progress("format: yapf"):
21
+ return _format(document, options=options)
22
+
23
+
24
+ @hookimpl
25
+ def pylsp_format_range(document, range, options):
26
+ log.info("Formatting document %s in range %s with yapf", document, range)
27
+ # First we 'round' the range up/down to full lines only
28
+ range["start"]["character"] = 0
29
+ range["end"]["line"] += 1
30
+ range["end"]["character"] = 0
31
+
32
+ # From Yapf docs:
33
+ # lines: (list of tuples of integers) A list of tuples of lines, [start, end],
34
+ # that we want to format. The lines are 1-based indexed. It can be used by
35
+ # third-party code (e.g., IDEs) when reformatting a snippet of code rather
36
+ # than a whole file.
37
+
38
+ # Add 1 for 1-indexing vs LSP's 0-indexing
39
+ lines = [(range["start"]["line"] + 1, range["end"]["line"] + 1)]
40
+ return _format(document, lines=lines, options=options)
41
+
42
+
43
+ def get_style_config(document_path, options=None):
44
+ # Exclude file if it follows the patterns for that
45
+ exclude_patterns_from_ignore_file = file_resources.GetExcludePatternsForDir(
46
+ os.getcwd()
47
+ )
48
+ if file_resources.IsIgnored(document_path, exclude_patterns_from_ignore_file):
49
+ return []
50
+
51
+ # Get the default styles as a string
52
+ # for a preset configuration, i.e. "pep8"
53
+ style_config = file_resources.GetDefaultStyleForDir(os.path.dirname(document_path))
54
+ if options is None:
55
+ return style_config
56
+
57
+ # We have options passed from LSP format request
58
+ # let's pass them to the formatter.
59
+ # First we want to get a dictionary of the preset style
60
+ # to pass instead of a string so that we can modify it
61
+ style_config = style.CreateStyleFromConfig(style_config)
62
+
63
+ use_tabs = style_config["USE_TABS"]
64
+ indent_width = style_config["INDENT_WIDTH"]
65
+
66
+ if options.get("tabSize") is not None:
67
+ indent_width = max(int(options.get("tabSize")), 1)
68
+
69
+ if options.get("insertSpaces") is not None:
70
+ # TODO is it guaranteed to be a boolean, or can it be a string?
71
+ use_tabs = not options.get("insertSpaces")
72
+
73
+ if use_tabs:
74
+ # Indent width doesn't make sense when using tabs
75
+ # the specifications state: "Size of a tab in spaces"
76
+ indent_width = 1
77
+
78
+ style_config["USE_TABS"] = use_tabs
79
+ style_config["INDENT_WIDTH"] = indent_width
80
+ style_config["CONTINUATION_INDENT_WIDTH"] = indent_width
81
+
82
+ for style_option, value in options.items():
83
+ # Apply arbitrary options passed as formatter options
84
+ if style_option not in style_config:
85
+ # ignore if it's not a known yapf config
86
+ continue
87
+
88
+ style_config[style_option] = value
89
+
90
+ return style_config
91
+
92
+
93
+ def diff_to_text_edits(diff, eol_chars):
94
+ # To keep things simple our text edits will be line based.
95
+ # We will also return the edits uncompacted, meaning a
96
+ # line replacement will come in as a line remove followed
97
+ # by a line add instead of a line replace.
98
+ text_edits = []
99
+ # keep track of line number since additions
100
+ # don't include the line number it's being added
101
+ # to in diffs. lsp is 0-indexed so we'll start with -1
102
+ prev_line_no = -1
103
+
104
+ for change in diff.changes:
105
+ if change.old and change.new:
106
+ # old and new are the same line, no change
107
+ # diffs are 1-indexed
108
+ prev_line_no = change.old - 1
109
+ elif change.new:
110
+ # addition
111
+ text_edits.append(
112
+ {
113
+ "range": {
114
+ "start": {"line": prev_line_no + 1, "character": 0},
115
+ "end": {"line": prev_line_no + 1, "character": 0},
116
+ },
117
+ "newText": change.line + eol_chars,
118
+ }
119
+ )
120
+ elif change.old:
121
+ # remove
122
+ lsp_line_no = change.old - 1
123
+ text_edits.append(
124
+ {
125
+ "range": {
126
+ "start": {"line": lsp_line_no, "character": 0},
127
+ "end": {
128
+ # From LSP spec:
129
+ # If you want to specify a range that contains a line
130
+ # including the line ending character(s) then use an
131
+ # end position denoting the start of the next line.
132
+ "line": lsp_line_no + 1,
133
+ "character": 0,
134
+ },
135
+ },
136
+ "newText": "",
137
+ }
138
+ )
139
+ prev_line_no = lsp_line_no
140
+
141
+ return text_edits
142
+
143
+
144
+ def ensure_eof_new_line(document, eol_chars, text_edits):
145
+ # diffs don't include EOF newline https://github.com/google/yapf/issues/1008
146
+ # we'll add it ourselves if our document doesn't already have it and the diff
147
+ # does not change the last line.
148
+ if document.source.endswith(eol_chars):
149
+ return
150
+
151
+ lines = document.lines
152
+ last_line_number = len(lines) - 1
153
+
154
+ if text_edits and text_edits[-1]["range"]["start"]["line"] >= last_line_number:
155
+ return
156
+
157
+ text_edits.append(
158
+ {
159
+ "range": {
160
+ "start": {"line": last_line_number, "character": 0},
161
+ "end": {"line": last_line_number + 1, "character": 0},
162
+ },
163
+ "newText": lines[-1] + eol_chars,
164
+ }
165
+ )
166
+
167
+
168
+ def _format(document, lines=None, options=None):
169
+ source = document.source
170
+ # Yapf doesn't work with CRLF/CR line endings, so we replace them by '\n'
171
+ # and restore them below when adding new lines
172
+ eol_chars = get_eol_chars(source)
173
+ if eol_chars in ["\r", "\r\n"]:
174
+ source = source.replace(eol_chars, "\n")
175
+ else:
176
+ eol_chars = "\n"
177
+
178
+ style_config = get_style_config(document_path=document.path, options=options)
179
+
180
+ diff_txt, changed = FormatCode(
181
+ source,
182
+ lines=lines,
183
+ filename=document.filename,
184
+ print_diff=True,
185
+ style_config=style_config,
186
+ )
187
+
188
+ if not changed:
189
+ return []
190
+
191
+ patch_generator = whatthepatch.parse_patch(diff_txt)
192
+ diff = next(patch_generator)
193
+ patch_generator.close()
194
+
195
+ text_edits = diff_to_text_edits(diff=diff, eol_chars=eol_chars)
196
+
197
+ ensure_eof_new_line(document=document, eol_chars=eol_chars, text_edits=text_edits)
198
+
199
+ return text_edits
pylsp/py.typed ADDED
File without changes