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,34 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ import pycodestyle
5
+
6
+ from pylsp._utils import find_parents
7
+
8
+ from .source import ConfigSource
9
+
10
+ CONFIG_KEY = "pycodestyle"
11
+ USER_CONFIGS = [pycodestyle.USER_CONFIG] if pycodestyle.USER_CONFIG else []
12
+ PROJECT_CONFIGS = ["pycodestyle.cfg", "setup.cfg", "tox.ini"]
13
+
14
+ OPTIONS = [
15
+ ("exclude", "plugins.pycodestyle.exclude", list),
16
+ ("filename", "plugins.pycodestyle.filename", list),
17
+ ("hang-closing", "plugins.pycodestyle.hangClosing", bool),
18
+ ("ignore", "plugins.pycodestyle.ignore", list),
19
+ ("max-line-length", "plugins.pycodestyle.maxLineLength", int),
20
+ ("indent-size", "plugins.pycodestyle.indentSize", int),
21
+ ("select", "plugins.pycodestyle.select", list),
22
+ ("aggressive", "plugins.pycodestyle.aggressive", int),
23
+ ]
24
+
25
+
26
+ class PyCodeStyleConfig(ConfigSource):
27
+ def user_config(self):
28
+ config = self.read_config_from_files(USER_CONFIGS)
29
+ return self.parse_config(config, CONFIG_KEY, OPTIONS)
30
+
31
+ def project_config(self, document_path):
32
+ files = find_parents(self.root_path, document_path, PROJECT_CONFIGS)
33
+ config = self.read_config_from_files(files)
34
+ return self.parse_config(config, CONFIG_KEY, OPTIONS)
pylsp/config/source.py ADDED
@@ -0,0 +1,88 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ import configparser
5
+ import logging
6
+ import os
7
+ import sys
8
+
9
+ log = logging.getLogger(__name__)
10
+
11
+
12
+ class ConfigSource:
13
+ """Base class for implementing a config source."""
14
+
15
+ def __init__(self, root_path) -> None:
16
+ self.root_path = root_path
17
+ self.is_windows = sys.platform == "win32"
18
+ self.xdg_home = os.environ.get(
19
+ "XDG_CONFIG_HOME", os.path.expanduser("~/.config")
20
+ )
21
+
22
+ def user_config(self) -> None:
23
+ """Return user-level (i.e. home directory) configuration."""
24
+ raise NotImplementedError()
25
+
26
+ def project_config(self, document_path) -> None:
27
+ """Return project-level (i.e. workspace directory) configuration."""
28
+ raise NotImplementedError()
29
+
30
+ @classmethod
31
+ def read_config_from_files(cls, files):
32
+ config = configparser.RawConfigParser()
33
+ for filename in files:
34
+ if os.path.exists(filename) and not os.path.isdir(filename):
35
+ config.read(filename)
36
+
37
+ return config
38
+
39
+ @classmethod
40
+ def parse_config(cls, config, key, options):
41
+ """Parse the config with the given options."""
42
+ conf = {}
43
+ for source, destination, opt_type in options:
44
+ opt_value = cls._get_opt(config, key, source, opt_type)
45
+ if opt_value is not None:
46
+ cls._set_opt(conf, destination, opt_value)
47
+ return conf
48
+
49
+ @classmethod
50
+ def _get_opt(cls, config, key, option, opt_type):
51
+ """Get an option from a configparser with the given type."""
52
+ for opt_key in [option, option.replace("-", "_")]:
53
+ if not config.has_option(key, opt_key):
54
+ continue
55
+
56
+ if opt_type is bool:
57
+ return config.getboolean(key, opt_key)
58
+
59
+ if opt_type is int:
60
+ return config.getint(key, opt_key)
61
+
62
+ if opt_type is str:
63
+ return config.get(key, opt_key)
64
+
65
+ if opt_type is list:
66
+ return cls._parse_list_opt(config.get(key, opt_key))
67
+
68
+ raise ValueError("Unknown option type: %s" % opt_type)
69
+
70
+ @classmethod
71
+ def _parse_list_opt(cls, string):
72
+ return [s.strip() for s in string.split(",") if s.strip()]
73
+
74
+ @classmethod
75
+ def _set_opt(cls, config_dict, path, value):
76
+ """Set the value in the dictionary at the given path if the value is not None."""
77
+ if value is None:
78
+ return
79
+
80
+ if "." not in path:
81
+ config_dict[path] = value
82
+ return
83
+
84
+ key, rest = path.split(".", 1)
85
+ if key not in config_dict:
86
+ config_dict[key] = {}
87
+
88
+ cls._set_opt(config_dict[key], rest, value)
pylsp/hookspecs.py ADDED
@@ -0,0 +1,150 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ from pylsp import hookspec
5
+
6
+
7
+ @hookspec
8
+ def pylsp_code_actions(config, workspace, document, range, context):
9
+ pass
10
+
11
+
12
+ @hookspec
13
+ def pylsp_code_lens(config, workspace, document) -> None:
14
+ pass
15
+
16
+
17
+ @hookspec
18
+ def pylsp_commands(config, workspace) -> None:
19
+ """The list of command strings supported by the server.
20
+
21
+ Returns:
22
+ List[str]: The supported commands.
23
+ """
24
+
25
+
26
+ @hookspec
27
+ def pylsp_completions(config, workspace, document, position, ignored_names) -> None:
28
+ pass
29
+
30
+
31
+ @hookspec
32
+ def pylsp_completion_detail(config, item) -> None:
33
+ pass
34
+
35
+
36
+ @hookspec(firstresult=True)
37
+ def pylsp_completion_item_resolve(config, workspace, document, completion_item) -> None:
38
+ pass
39
+
40
+
41
+ @hookspec
42
+ def pylsp_definitions(config, workspace, document, position) -> None:
43
+ pass
44
+
45
+
46
+ @hookspec(firstresult=True)
47
+ def pylsp_type_definition(config, document, position):
48
+ pass
49
+
50
+
51
+ @hookspec
52
+ def pylsp_dispatchers(config, workspace) -> None:
53
+ pass
54
+
55
+
56
+ @hookspec
57
+ def pylsp_document_did_open(config, workspace, document) -> None:
58
+ pass
59
+
60
+
61
+ @hookspec
62
+ def pylsp_document_did_save(config, workspace, document) -> None:
63
+ pass
64
+
65
+
66
+ @hookspec
67
+ def pylsp_document_highlight(config, workspace, document, position) -> None:
68
+ pass
69
+
70
+
71
+ @hookspec
72
+ def pylsp_document_symbols(config, workspace, document) -> None:
73
+ pass
74
+
75
+
76
+ @hookspec(firstresult=True)
77
+ def pylsp_execute_command(config, workspace, command, arguments) -> None:
78
+ pass
79
+
80
+
81
+ @hookspec
82
+ def pylsp_experimental_capabilities(config, workspace) -> None:
83
+ pass
84
+
85
+
86
+ @hookspec
87
+ def pylsp_folding_range(config, workspace, document) -> None:
88
+ pass
89
+
90
+
91
+ @hookspec(firstresult=True)
92
+ def pylsp_format_document(config, workspace, document, options) -> None:
93
+ pass
94
+
95
+
96
+ @hookspec(firstresult=True)
97
+ def pylsp_format_range(config, workspace, document, range, options) -> None:
98
+ pass
99
+
100
+
101
+ @hookspec(firstresult=True)
102
+ def pylsp_hover(config, workspace, document, position) -> None:
103
+ pass
104
+
105
+
106
+ @hookspec
107
+ def pylsp_initialize(config, workspace) -> None:
108
+ pass
109
+
110
+
111
+ @hookspec
112
+ def pylsp_initialized() -> None:
113
+ pass
114
+
115
+
116
+ @hookspec
117
+ def pylsp_lint(config, workspace, document, is_saved) -> None:
118
+ pass
119
+
120
+
121
+ @hookspec
122
+ def pylsp_references(
123
+ config, workspace, document, position, exclude_declaration
124
+ ) -> None:
125
+ pass
126
+
127
+
128
+ @hookspec(firstresult=True)
129
+ def pylsp_rename(config, workspace, document, position, new_name) -> None:
130
+ pass
131
+
132
+
133
+ @hookspec
134
+ def pylsp_settings(config) -> None:
135
+ pass
136
+
137
+
138
+ @hookspec(firstresult=True)
139
+ def pylsp_signature_help(config, workspace, document, position) -> None:
140
+ pass
141
+
142
+
143
+ @hookspec
144
+ def pylsp_workspace_configuration_changed(config, workspace) -> None:
145
+ pass
146
+
147
+
148
+ @hookspec
149
+ def pylsp_shutdown(config, workspace) -> None:
150
+ pass
pylsp/lsp.py ADDED
@@ -0,0 +1,115 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ """Some Language Server Protocol constants
5
+
6
+ https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md
7
+ """
8
+
9
+
10
+ class CompletionItemKind:
11
+ Text = 1
12
+ Method = 2
13
+ Function = 3
14
+ Constructor = 4
15
+ Field = 5
16
+ Variable = 6
17
+ Class = 7
18
+ Interface = 8
19
+ Module = 9
20
+ Property = 10
21
+ Unit = 11
22
+ Value = 12
23
+ Enum = 13
24
+ Keyword = 14
25
+ Snippet = 15
26
+ Color = 16
27
+ File = 17
28
+ Reference = 18
29
+ Folder = 19
30
+ EnumMember = 20
31
+ Constant = 21
32
+ Struct = 22
33
+ Event = 23
34
+ Operator = 24
35
+ TypeParameter = 25
36
+
37
+
38
+ class DocumentHighlightKind:
39
+ Text = 1
40
+ Read = 2
41
+ Write = 3
42
+
43
+
44
+ class DiagnosticSeverity:
45
+ Error = 1
46
+ Warning = 2
47
+ Information = 3
48
+ Hint = 4
49
+
50
+
51
+ class DiagnosticTag:
52
+ Unnecessary = 1
53
+ Deprecated = 2
54
+
55
+
56
+ class InsertTextFormat:
57
+ PlainText = 1
58
+ Snippet = 2
59
+
60
+
61
+ class MessageType:
62
+ Error = 1
63
+ Warning = 2
64
+ Info = 3
65
+ Log = 4
66
+
67
+
68
+ class SymbolKind:
69
+ File = 1
70
+ Module = 2
71
+ Namespace = 3
72
+ Package = 4
73
+ Class = 5
74
+ Method = 6
75
+ Property = 7
76
+ Field = 8
77
+ Constructor = 9
78
+ Enum = 10
79
+ Interface = 11
80
+ Function = 12
81
+ Variable = 13
82
+ Constant = 14
83
+ String = 15
84
+ Number = 16
85
+ Boolean = 17
86
+ Array = 18
87
+
88
+
89
+ class TextDocumentSyncKind:
90
+ NONE = 0
91
+ FULL = 1
92
+ INCREMENTAL = 2
93
+
94
+
95
+ class NotebookCellKind:
96
+ Markup = 1
97
+ Code = 2
98
+
99
+
100
+ # https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#errorCodes
101
+ class ErrorCodes:
102
+ ParseError = -32700
103
+ InvalidRequest = -32600
104
+ MethodNotFound = -32601
105
+ InvalidParams = -32602
106
+ InternalError = -32603
107
+ jsonrpcReservedErrorRangeStart = -32099
108
+ ServerNotInitialized = -32002
109
+ UnknownErrorCode = -32001
110
+ jsonrpcReservedErrorRangeEnd = -32000
111
+ lspReservedErrorRangeStart = -32899
112
+ ServerCancelled = -32802
113
+ ContentModified = -32801
114
+ RequestCancelled = -32800
115
+ lspReservedErrorRangeEnd = -32800
@@ -0,0 +1,2 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
@@ -0,0 +1,132 @@
1
+ # Copyright 2017-2020 Palantir Technologies, Inc.
2
+ # Copyright 2021- Python Language Server Contributors.
3
+
4
+ import logging
5
+ from collections import defaultdict
6
+ from time import time
7
+
8
+ from jedi.api.classes import Completion
9
+
10
+ from pylsp import lsp
11
+
12
+ log = logging.getLogger(__name__)
13
+
14
+
15
+ # ---- Base class
16
+ # -----------------------------------------------------------------------------
17
+ class Resolver:
18
+ def __init__(self, callback, resolve_on_error, time_to_live=60 * 30) -> None:
19
+ self.callback = callback
20
+ self.resolve_on_error = resolve_on_error
21
+ self._cache = {}
22
+ self._time_to_live = time_to_live
23
+ self._cache_ttl = defaultdict(set)
24
+ self._clear_every = 2
25
+ # see https://github.com/davidhalter/jedi/blob/master/jedi/inference/helpers.py#L194-L202
26
+ self._cached_modules = {"pandas", "numpy", "tensorflow", "matplotlib"}
27
+
28
+ @property
29
+ def cached_modules(self):
30
+ return self._cached_modules
31
+
32
+ @cached_modules.setter
33
+ def cached_modules(self, new_value):
34
+ self._cached_modules = set(new_value)
35
+
36
+ def clear_outdated(self) -> None:
37
+ now = self.time_key()
38
+ to_clear = [timestamp for timestamp in self._cache_ttl if timestamp < now]
39
+ for time_key in to_clear:
40
+ for key in self._cache_ttl[time_key]:
41
+ del self._cache[key]
42
+ del self._cache_ttl[time_key]
43
+
44
+ def time_key(self):
45
+ return int(time() / self._time_to_live)
46
+
47
+ def get_or_create(self, completion: Completion):
48
+ if not completion.full_name:
49
+ use_cache = False
50
+ else:
51
+ module_parts = completion.full_name.split(".")
52
+ use_cache = module_parts and module_parts[0] in self._cached_modules
53
+
54
+ if use_cache:
55
+ key = self._create_completion_id(completion)
56
+ if key not in self._cache:
57
+ if self.time_key() % self._clear_every == 0:
58
+ self.clear_outdated()
59
+
60
+ self._cache[key] = self.resolve(completion)
61
+ self._cache_ttl[self.time_key()].add(key)
62
+ return self._cache[key]
63
+
64
+ return self.resolve(completion)
65
+
66
+ def _create_completion_id(self, completion: Completion):
67
+ return (
68
+ completion.full_name,
69
+ completion.module_path,
70
+ completion.line,
71
+ completion.column,
72
+ self.time_key(),
73
+ )
74
+
75
+ def resolve(self, completion):
76
+ try:
77
+ sig = completion.get_signatures()
78
+ return self.callback(completion, sig)
79
+ except Exception as e:
80
+ log.warning(
81
+ f"Something went wrong when resolving label for {completion}: {e}"
82
+ )
83
+ return self.resolve_on_error
84
+
85
+
86
+ # ---- Label resolver
87
+ # -----------------------------------------------------------------------------
88
+ def format_label(completion, sig):
89
+ if sig and completion.type in ("function", "method"):
90
+ params = ", ".join(param.name for param in sig[0].params)
91
+ label = f"{completion.name}({params})"
92
+ return label
93
+ return completion.name
94
+
95
+
96
+ LABEL_RESOLVER = Resolver(callback=format_label, resolve_on_error="")
97
+
98
+
99
+ # ---- Snippets resolver
100
+ # -----------------------------------------------------------------------------
101
+ def format_snippet(completion, sig):
102
+ if not sig:
103
+ return {}
104
+
105
+ snippet_completion = {}
106
+
107
+ positional_args = [
108
+ param
109
+ for param in sig[0].params
110
+ if "=" not in param.description and param.name not in {"/", "*"}
111
+ ]
112
+
113
+ if len(positional_args) > 1:
114
+ # For completions with params, we can generate a snippet instead
115
+ snippet_completion["insertTextFormat"] = lsp.InsertTextFormat.Snippet
116
+ snippet = completion.name + "("
117
+ for i, param in enumerate(positional_args):
118
+ snippet += "${{{}:{}}}".format(i + 1, param.name)
119
+ if i < len(positional_args) - 1:
120
+ snippet += ", "
121
+ snippet += ")$0"
122
+ snippet_completion["insertText"] = snippet
123
+ elif len(positional_args) == 1:
124
+ snippet_completion["insertTextFormat"] = lsp.InsertTextFormat.Snippet
125
+ snippet_completion["insertText"] = completion.name + "($0)"
126
+ else:
127
+ snippet_completion["insertText"] = completion.name + "()"
128
+
129
+ return snippet_completion
130
+
131
+
132
+ SNIPPET_RESOLVER = Resolver(callback=format_snippet, resolve_on_error={})
@@ -0,0 +1,107 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from collections.abc import Sequence
5
+ from typing import Callable, ContextManager
6
+
7
+ from rope.base.taskhandle import BaseJobSet, BaseTaskHandle
8
+
9
+ from pylsp._utils import throttle
10
+ from pylsp.workspace import Workspace
11
+
12
+ log = logging.getLogger(__name__)
13
+ Report = Callable[[str, int], None]
14
+
15
+
16
+ class PylspJobSet(BaseJobSet):
17
+ count: int = 0
18
+ done: int = 0
19
+ _reporter: Report
20
+ _report_iter: ContextManager
21
+ job_name: str = ""
22
+
23
+ def __init__(self, count: int | None, report_iter: ContextManager) -> None:
24
+ if count is not None:
25
+ self.count = count
26
+ self._reporter = report_iter.__enter__()
27
+ self._report_iter = report_iter
28
+
29
+ def started_job(self, name: str | None) -> None:
30
+ if name:
31
+ self.job_name = name
32
+
33
+ def finished_job(self) -> None:
34
+ self.done += 1
35
+ if self.get_percent_done() is not None and int(self.get_percent_done()) >= 100:
36
+ if self._report_iter is None:
37
+ return
38
+ self._report_iter.__exit__(None, None, None)
39
+ self._report_iter = None
40
+ else:
41
+ self._report()
42
+
43
+ def check_status(self) -> None:
44
+ pass
45
+
46
+ def get_percent_done(self) -> float | None:
47
+ if self.count == 0:
48
+ return 0
49
+ return (self.done / self.count) * 100
50
+
51
+ def increment(self) -> None:
52
+ """
53
+ Increment the number of tasks to complete.
54
+
55
+ This is used if the number is not known ahead of time.
56
+ """
57
+ self.count += 1
58
+ self._report()
59
+
60
+ @throttle(0.5)
61
+ def _report(self) -> None:
62
+ percent = int(self.get_percent_done())
63
+ message = f"{self.job_name} {self.done}/{self.count}"
64
+ log.debug(f"Reporting {message} {percent}%")
65
+ self._reporter(message, percent)
66
+
67
+
68
+ class PylspTaskHandle(BaseTaskHandle):
69
+ name: str
70
+ observers: list
71
+ job_sets: list[PylspJobSet]
72
+ stopped: bool
73
+ workspace: Workspace
74
+ _report: Callable[[str, str], None]
75
+
76
+ def __init__(self, workspace: Workspace) -> None:
77
+ self.workspace = workspace
78
+ self.job_sets = []
79
+ self.observers = []
80
+
81
+ def create_jobset(self, name="JobSet", count: int | None = None):
82
+ report_iter = self.workspace.report_progress(
83
+ name, None, None, skip_token_initialization=True
84
+ )
85
+ result = PylspJobSet(count, report_iter)
86
+ self.job_sets.append(result)
87
+ self._inform_observers()
88
+ return result
89
+
90
+ def stop(self) -> None:
91
+ pass
92
+
93
+ def current_jobset(self) -> BaseJobSet | None:
94
+ pass
95
+
96
+ def add_observer(self) -> None:
97
+ pass
98
+
99
+ def is_stopped(self) -> bool:
100
+ pass
101
+
102
+ def get_jobsets(self) -> Sequence[BaseJobSet]:
103
+ pass
104
+
105
+ def _inform_observers(self) -> None:
106
+ for observer in self.observers:
107
+ observer()