From fea2a69f45525d6b5e8e366e270b3be46eed90f8 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 11 Jul 2021 00:10:17 -0500 Subject: [PATCH 1/5] Avoid an error when config has not been initialized yet --- pylsp/python_lsp.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pylsp/python_lsp.py b/pylsp/python_lsp.py index ec099234..c71f08b3 100644 --- a/pylsp/python_lsp.py +++ b/pylsp/python_lsp.py @@ -383,7 +383,8 @@ def m_text_document__signature_help(self, textDocument=None, position=None, **_k return self.signature_help(textDocument['uri'], position) def m_workspace__did_change_configuration(self, settings=None): - self.config.update((settings or {}).get('pylsp', {})) + if self.config is not None: + self.config.update((settings or {}).get('pylsp', {})) for workspace in self.workspaces.values(): workspace.update_config(settings) for doc_uri in workspace.documents: From e151ee903974a7709e118c2b79b09a0871e1cfc2 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 11 Jul 2021 00:13:09 -0500 Subject: [PATCH 2/5] Avoid error when trying to compute symbols of non-existing files --- pylsp/plugins/symbols.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylsp/plugins/symbols.py b/pylsp/plugins/symbols.py index 87c4bd2c..d7e28de3 100644 --- a/pylsp/plugins/symbols.py +++ b/pylsp/plugins/symbols.py @@ -40,7 +40,7 @@ def pylsp_document_symbols(config, document): continue try: docismodule = os.path.samefile(document.path, d.module_path) - except TypeError: + except (TypeError, FileNotFoundError): # Python 2 on Windows has no .samefile, but then these are # strings for sure docismodule = document.path == d.module_path From 9383dd504b926a5b366397ae24a5f0846b7a8d49 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 11 Jul 2021 00:57:47 -0500 Subject: [PATCH 3/5] Mark test as flaky --- test/test_language_server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_language_server.py b/test/test_language_server.py index 0b5c1ae8..c6f66001 100644 --- a/test/test_language_server.py +++ b/test/test_language_server.py @@ -7,6 +7,7 @@ import sys from threading import Thread +from flaky import flaky from pylsp_jsonrpc.exceptions import JsonRpcMethodNotFound import pytest @@ -75,6 +76,7 @@ def client_exited_server(): assert client_server_pair.process.is_alive() is False +@flaky(max_runs=10, min_passes=1) @pytest.mark.skipif(sys.platform == 'darwin', reason='Too flaky on Mac') def test_initialize(client_server): # pylint: disable=redefined-outer-name response = client_server._endpoint.request('initialize', { From 9f69edc242f34e3c0ac52baf409f38aecea61e4e Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 11 Jul 2021 19:40:19 -0500 Subject: [PATCH 4/5] Fix skipping imported symbols --- pylsp/plugins/symbols.py | 40 +++++++++++++++++++++++++++++----------- pylsp/workspace.py | 4 ++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/pylsp/plugins/symbols.py b/pylsp/plugins/symbols.py index d7e28de3..4c0ac855 100644 --- a/pylsp/plugins/symbols.py +++ b/pylsp/plugins/symbols.py @@ -19,25 +19,43 @@ def pylsp_document_symbols(config, document): symbols_settings = config.plugin_settings('jedi_symbols') all_scopes = symbols_settings.get('all_scopes', True) add_import_symbols = symbols_settings.get('include_import_symbols', True) - - use_document_path = False - document_dir = os.path.normpath(os.path.dirname(document.path)) - if not os.path.isfile(os.path.join(document_dir, '__init__.py')): - use_document_path = True - - definitions = document.jedi_names(use_document_path, all_scopes=all_scopes) - module_name = document.dot_path + definitions = document.jedi_names(all_scopes=all_scopes) symbols = [] exclude = set({}) redefinitions = {} while definitions != []: d = definitions.pop(0) + + # Skip symbols imported from other modules. if not add_import_symbols: + # Skip if there's an import in the code the symbol is defined. + code = d.get_line_code() + if ' import ' in code or 'import ' in code: + continue + + # Skip comparing module names. sym_full_name = d.full_name + module_name = document.dot_path if sym_full_name is not None: - if (not sym_full_name.startswith(module_name) and - not sym_full_name.startswith('__main__')): - continue + # module_name returns where the symbol is imported, whereas + # full_name says where it really comes from. So if the parent + # modules in full_name are not in module_name, it means the + # symbol was not defined there. + # Note: The last element of sym_full_name is the symbol itself, + # so we don't need to use it below. + imported_symbol = True + for mod in sym_full_name.split('.')[:-1]: + if mod in module_name: + imported_symbol = False + + # When there's no __init__.py next to a file or in one of its + # parents, the check above fails. However, Jedi has a nice way + # to tell if the symbol was declared in the same file: if + # full_name starts by __main__. + if imported_symbol: + if not sym_full_name.startswith('__main__'): + continue + try: docismodule = os.path.samefile(document.path, d.module_path) except (TypeError, FileNotFoundError): diff --git a/pylsp/workspace.py b/pylsp/workspace.py index a063cabe..ec031b6f 100644 --- a/pylsp/workspace.py +++ b/pylsp/workspace.py @@ -239,8 +239,8 @@ def word_at_position(self, position): return m_start[0] + m_end[-1] @lock - def jedi_names(self, use_document_path, all_scopes=False, definitions=True, references=False): - script = self.jedi_script(use_document_path=use_document_path) + def jedi_names(self, all_scopes=False, definitions=True, references=False): + script = self.jedi_script() return script.get_names(all_scopes=all_scopes, definitions=definitions, references=references) From c4cfd1643437df77d3a667882bc68ff1b05e0b29 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 31 Jul 2021 19:43:10 -0500 Subject: [PATCH 5/5] Add missing jedi_symbols config option --- CONFIGURATION.md | 1 + pylsp/config/schema.json | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CONFIGURATION.md b/CONFIGURATION.md index e84c09d0..46ab4adb 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -32,6 +32,7 @@ This server can be configured using `workspace/didChangeConfiguration` method. E | `pylsp.plugins.jedi_signature_help.enabled` | `boolean` | Enable or disable the plugin. | `true` | | `pylsp.plugins.jedi_symbols.enabled` | `boolean` | Enable or disable the plugin. | `true` | | `pylsp.plugins.jedi_symbols.all_scopes` | `boolean` | If True lists the names of all scopes instead of only the module namespace. | `true` | +| `pylsp.plugins.jedi_symbols.include_import_symbols` | `boolean` | If True includes symbols imported from other libraries. | `true` | | `pylsp.plugins.mccabe.enabled` | `boolean` | Enable or disable the plugin. | `true` | | `pylsp.plugins.mccabe.threshold` | `number` | The minimum threshold that triggers warnings about cyclomatic complexity. | `15` | | `pylsp.plugins.preload.enabled` | `boolean` | Enable or disable the plugin. | `true` | diff --git a/pylsp/config/schema.json b/pylsp/config/schema.json index bd4c2e70..d95db74a 100644 --- a/pylsp/config/schema.json +++ b/pylsp/config/schema.json @@ -157,6 +157,11 @@ "default": true, "description": "If True lists the names of all scopes instead of only the module namespace." }, + "pylsp.plugins.jedi_symbols.include_import_symbols": { + "type": "boolean", + "default": true, + "description": "If True includes symbols imported from other libraries." + }, "pylsp.plugins.mccabe.enabled": { "type": "boolean", "default": true,