Skip to content

Fix skipping imported symbols #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down
5 changes: 5 additions & 0 deletions pylsp/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
42 changes: 30 additions & 12 deletions pylsp/plugins/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,46 @@ 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:
except (TypeError, FileNotFoundError):
# Python 2 on Windows has no .samefile, but then these are
# strings for sure
docismodule = document.path == d.module_path
Expand Down
3 changes: 2 additions & 1 deletion pylsp/python_lsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions pylsp/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions test/test_language_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
from threading import Thread

from flaky import flaky
from pylsp_jsonrpc.exceptions import JsonRpcMethodNotFound
import pytest

Expand Down Expand Up @@ -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', {
Expand Down