Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-python-lsp-server for 
openSUSE:Factory checked in at 2021-08-06 22:44:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-lsp-server (Old)
 and      /work/SRC/openSUSE:Factory/.python-python-lsp-server.new.1899 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-python-lsp-server"

Fri Aug  6 22:44:46 2021 rev:2 rq:910382 version:1.2.1

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-python-lsp-server/python-python-lsp-server.changes
        2021-07-16 22:13:24.310549099 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-python-lsp-server.new.1899/python-python-lsp-server.changes
      2021-08-06 22:45:25.146002662 +0200
@@ -1,0 +2,9 @@
+Thu Aug  5 20:18:13 UTC 2021 - Ben Greiner <c...@bnavigator.de>
+
+- Update to version 1.2.1
+  * Implement completion item resolve requests for faster
+    completions.
+  * Initialize workspaces from the initialize request.
+  * Catch errors when getting docstrings on _resolve_completion
+
+-------------------------------------------------------------------

Old:
----
  python-lsp-server-1.1.0.tar.gz

New:
----
  python-lsp-server-1.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-python-lsp-server.spec ++++++
--- /var/tmp/diff_new_pack.UaBkDM/_old  2021-08-06 22:45:25.714001654 +0200
+++ /var/tmp/diff_new_pack.UaBkDM/_new  2021-08-06 22:45:25.714001654 +0200
@@ -19,30 +19,30 @@
 %{?!python_module:%define python_module() python3-%{**}}
 %define skip_python2 1
 Name:           python-python-lsp-server
-Version:        1.1.0
+Version:        1.2.1
 Release:        0
 Summary:        Python Language Server for the Language Server Protocol
 License:        MIT
 URL:            https://github.com/python-lsp/python-lsp-server
 Source:         
https://files.pythonhosted.org/packages/source/p/python-lsp-server/python-lsp-server-%{version}.tar.gz
-BuildRequires:  python-rpm-macros
 BuildRequires:  %{python_module setuptools >= 39.0.0}
+BuildRequires:  python-rpm-macros
 # SECTION test requirements
 BuildRequires:  %{python_module jedi >= 0.17.2}
-BuildRequires:  %{python_module pluggy}
-BuildRequires:  %{python_module python-lsp-jsonrpc >= 1.0.0}
-BuildRequires:  %{python_module ujson >= 3.0.0}
-BuildRequires:  %{python_module flaky}
 BuildRequires:  %{python_module PyQt5}
 BuildRequires:  %{python_module autopep8}
 BuildRequires:  %{python_module flake8 >= 3.8.0}
+BuildRequires:  %{python_module flaky}
 BuildRequires:  %{python_module mccabe >= 0.6.0}
+BuildRequires:  %{python_module pluggy}
 BuildRequires:  %{python_module pycodestyle >= 2.7.0}
 BuildRequires:  %{python_module pydocstyle >= 2.0.0}
 BuildRequires:  %{python_module pyflakes >= 2.3.0}
 BuildRequires:  %{python_module pylint >= 2.5.0}
 BuildRequires:  %{python_module pytest}
+BuildRequires:  %{python_module python-lsp-jsonrpc >= 1.0.0}
 BuildRequires:  %{python_module rope >= 0.10.5}
+BuildRequires:  %{python_module ujson >= 3.0.0}
 BuildRequires:  %{python_module yapf}
 BuildRequires:  %{python_module matplotlib  if (%python-base without 
python36-base)}
 BuildRequires:  %{python_module numpy if (%python-base without python36-base)}
@@ -120,5 +120,4 @@
 %{python_sitelib}/pylsp
 %{python_sitelib}/python_lsp_server-%{version}*-info
 
-
 %changelog

++++++ python-lsp-server-1.1.0.tar.gz -> python-lsp-server-1.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/PKG-INFO 
new/python-lsp-server-1.2.1/PKG-INFO
--- old/python-lsp-server-1.1.0/PKG-INFO        2021-06-25 23:45:00.085278300 
+0200
+++ new/python-lsp-server-1.2.1/PKG-INFO        2021-08-04 19:11:45.349771700 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: python-lsp-server
-Version: 1.1.0
+Version: 1.2.1
 Summary: Python Language Server for the Language Server Protocol
 Home-page: https://github.com/python-lsp/python-lsp-server
 Author: Python Language Server Contributors
@@ -83,11 +83,13 @@
 
 The default configuration source is pycodestyle. Change the 
`pylsp.configurationSources` setting to `['flake8']` in order to respect flake8 
configuration instead.
 
-Overall configuration is computed first from user configuration (in home 
directory), overridden by configuration passed in by the language client, and 
then overriden by configuration discovered in the workspace.
+Overall configuration is computed first from user configuration (in home 
directory), overridden by configuration passed in by the language client, and 
then overridden by configuration discovered in the workspace.
 
 To enable pydocstyle for linting docstrings add the following setting in your 
LSP configuration:
 `"pylsp.plugins.pydocstyle.enabled": true`
 
+All configuration options are described in 
[`CONFIGURATION.md`](https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md).
+
 ## LSP Server Features
 
 * Auto Completion
@@ -105,8 +107,14 @@
 
 To run the test suite:
 
+```sh
+pip install '.[test]' && pytest
+```
+
+After adding configuration options to `schema.json`, refresh the 
`CONFIGURATION.md` file with
+
 ```
-pip install .[test] && pytest
+python scripts/jsonschema2md.py pylsp/config/schema.json CONFIGURATION.md
 ```
 
 ## License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/README.md 
new/python-lsp-server-1.2.1/README.md
--- old/python-lsp-server-1.1.0/README.md       2021-06-25 23:26:52.000000000 
+0200
+++ new/python-lsp-server-1.2.1/README.md       2021-07-28 07:34:07.000000000 
+0200
@@ -60,11 +60,13 @@
 
 The default configuration source is pycodestyle. Change the 
`pylsp.configurationSources` setting to `['flake8']` in order to respect flake8 
configuration instead.
 
-Overall configuration is computed first from user configuration (in home 
directory), overridden by configuration passed in by the language client, and 
then overriden by configuration discovered in the workspace.
+Overall configuration is computed first from user configuration (in home 
directory), overridden by configuration passed in by the language client, and 
then overridden by configuration discovered in the workspace.
 
 To enable pydocstyle for linting docstrings add the following setting in your 
LSP configuration:
 `"pylsp.plugins.pydocstyle.enabled": true`
 
+All configuration options are described in 
[`CONFIGURATION.md`](https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md).
+
 ## LSP Server Features
 
 * Auto Completion
@@ -82,8 +84,14 @@
 
 To run the test suite:
 
+```sh
+pip install '.[test]' && pytest
+```
+
+After adding configuration options to `schema.json`, refresh the 
`CONFIGURATION.md` file with
+
 ```
-pip install .[test] && pytest
+python scripts/jsonschema2md.py pylsp/config/schema.json CONFIGURATION.md
 ```
 
 ## License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/pylsp/_version.py 
new/python-lsp-server-1.2.1/pylsp/_version.py
--- old/python-lsp-server-1.1.0/pylsp/_version.py       2021-06-25 
23:44:13.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/_version.py       2021-08-04 
19:10:52.000000000 +0200
@@ -4,5 +4,5 @@
 """PyLSP versioning information."""
 
 
-VERSION_INFO = (1, 1, 0)
+VERSION_INFO = (1, 2, 1)
 __version__ = '.'.join(map(str, VERSION_INFO))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/pylsp/hookspecs.py 
new/python-lsp-server-1.2.1/pylsp/hookspecs.py
--- old/python-lsp-server-1.1.0/pylsp/hookspecs.py      2021-04-14 
23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/hookspecs.py      2021-07-28 
07:34:07.000000000 +0200
@@ -29,6 +29,11 @@
     pass
 
 
+@hookspec(firstresult=True)
+def pylsp_completion_item_resolve(config, workspace, document, 
completion_item):
+    pass
+
+
 @hookspec
 def pylsp_definitions(config, workspace, document, position):
     pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/pylsp/plugins/definition.py 
new/python-lsp-server-1.2.1/pylsp/plugins/definition.py
--- old/python-lsp-server-1.1.0/pylsp/plugins/definition.py     2021-04-14 
23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/plugins/definition.py     2021-07-31 
20:34:03.000000000 +0200
@@ -11,7 +11,7 @@
 def pylsp_definitions(config, document, position):
     settings = config.plugin_settings('jedi_definition')
     code_position = _utils.position_to_jedi_linecolumn(document, position)
-    definitions = document.jedi_script().goto(
+    definitions = document.jedi_script(use_document_path=True).goto(
         follow_imports=settings.get('follow_imports', True),
         follow_builtin_imports=settings.get('follow_builtin_imports', True),
         **code_position)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/pylsp/plugins/hover.py 
new/python-lsp-server-1.2.1/pylsp/plugins/hover.py
--- old/python-lsp-server-1.1.0/pylsp/plugins/hover.py  2021-04-14 
23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/plugins/hover.py  2021-07-31 
20:34:03.000000000 +0200
@@ -11,7 +11,7 @@
 @hookimpl
 def pylsp_hover(document, position):
     code_position = _utils.position_to_jedi_linecolumn(document, position)
-    definitions = document.jedi_script().infer(**code_position)
+    definitions = 
document.jedi_script(use_document_path=True).infer(**code_position)
     word = document.word_at_position(position)
 
     # Find first exact matching definition
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-lsp-server-1.1.0/pylsp/plugins/jedi_completion.py 
new/python-lsp-server-1.2.1/pylsp/plugins/jedi_completion.py
--- old/python-lsp-server-1.1.0/pylsp/plugins/jedi_completion.py        
2021-06-25 23:26:52.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/plugins/jedi_completion.py        
2021-08-04 19:08:14.000000000 +0200
@@ -3,7 +3,10 @@
 
 import logging
 import os.path as osp
+from collections import defaultdict
+from time import time
 
+from jedi.api.classes import Completion
 import parso
 
 from pylsp import _utils, hookimpl, lsp
@@ -37,10 +40,12 @@
 @hookimpl
 def pylsp_completions(config, document, position):
     """Get formatted completions for current code position"""
+    # pylint: disable=too-many-locals
     settings = config.plugin_settings('jedi_completion', 
document_path=document.path)
+    resolve_eagerly = settings.get('eager', False)
     code_position = _utils.position_to_jedi_linecolumn(document, position)
 
-    code_position["fuzzy"] = settings.get("fuzzy", False)
+    code_position['fuzzy'] = settings.get('fuzzy', False)
     completions = 
document.jedi_script(use_document_path=True).complete(**code_position)
 
     if not completions:
@@ -52,25 +57,63 @@
     should_include_params = settings.get('include_params')
     should_include_class_objects = settings.get('include_class_objects', True)
 
+    max_labels_resolve = settings.get('resolve_at_most_labels', 25)
+    modules_to_cache_labels_for = settings.get('cache_labels_for', None)
+    if modules_to_cache_labels_for is not None:
+        LABEL_RESOLVER.cached_modules = modules_to_cache_labels_for
+
     include_params = snippet_support and should_include_params and 
use_snippets(document, position)
     include_class_objects = snippet_support and should_include_class_objects 
and use_snippets(document, position)
 
     ready_completions = [
-        _format_completion(c, include_params)
-        for c in completions
+        _format_completion(
+            c,
+            include_params,
+            resolve=resolve_eagerly,
+            resolve_label=(i < max_labels_resolve)
+        )
+        for i, c in enumerate(completions)
     ]
 
+    # TODO split up once other improvements are merged
     if include_class_objects:
-        for c in completions:
+        for i, c in enumerate(completions):
             if c.type == 'class':
-                completion_dict = _format_completion(c, False)
+                completion_dict = _format_completion(
+                    c,
+                    False,
+                    resolve=resolve_eagerly,
+                    resolve_label=(i < max_labels_resolve)
+                )
                 completion_dict['kind'] = lsp.CompletionItemKind.TypeParameter
                 completion_dict['label'] += ' object'
                 ready_completions.append(completion_dict)
 
+    for completion_dict in ready_completions:
+        completion_dict['data'] = {
+            'doc_uri': document.uri
+        }
+
+    # most recently retrieved completion items, used for resolution
+    document.shared_data['LAST_JEDI_COMPLETIONS'] = {
+        # label is the only required property; here it is assumed to be unique
+        completion['label']: (completion, data)
+        for completion, data in zip(ready_completions, completions)
+    }
+
     return ready_completions or None
 
 
+@hookimpl
+def pylsp_completion_item_resolve(completion_item, document):
+    """Resolve formatted completion for given non-resolved completion"""
+    shared_data = 
document.shared_data['LAST_JEDI_COMPLETIONS'].get(completion_item['label'])
+    if shared_data:
+        completion, data = shared_data
+        return _resolve_completion(completion, data)
+    return completion_item
+
+
 def is_exception_class(name):
     """
     Determine if a class name is an instance of an Exception.
@@ -114,31 +157,46 @@
             break
     if '(' in act_lines[-1].strip():
         last_character = ')'
-    code = '\n'.join(act_lines).split(';')[-1].strip() + last_character
+    code = '\n'.join(act_lines).rsplit(';', maxsplit=1)[-1].strip() + 
last_character
     tokens = parso.parse(code)
     expr_type = tokens.children[0].type
     return (expr_type not in _IMPORTS and
             not (expr_type in _ERRORS and 'import' in code))
 
 
-def _format_completion(d, include_params=True):
+def _resolve_completion(completion, d):
+    # pylint: disable=broad-except
+    completion['detail'] = _detail(d)
+    try:
+        docs = _utils.format_docstring(d.docstring())
+    except Exception:
+        docs = ''
+    completion['documentation'] = docs
+    return completion
+
+
+def _format_completion(d, include_params=True, resolve=False, 
resolve_label=False):
     completion = {
-        'label': _label(d),
+        'label': _label(d, resolve_label),
         'kind': _TYPE_MAP.get(d.type),
-        'detail': _detail(d),
-        'documentation': _utils.format_docstring(d.docstring()),
         'sortText': _sort_text(d),
         'insertText': d.name
     }
 
+    if resolve:
+        completion = _resolve_completion(completion, d)
+
     if d.type == 'path':
         path = osp.normpath(d.name)
         path = path.replace('\\', '\\\\')
         path = path.replace('/', '\\/')
         completion['insertText'] = path
 
-    sig = d.get_signatures()
-    if (include_params and sig and not is_exception_class(d.name)):
+    if include_params and not is_exception_class(d.name):
+        sig = d.get_signatures()
+        if not sig:
+            return completion
+
         positional_args = [param for param in sig[0].params
                            if '=' not in param.description and
                            param.name not in {'/', '*'}]
@@ -162,12 +220,12 @@
     return completion
 
 
-def _label(definition):
-    sig = definition.get_signatures()
-    if definition.type in ('function', 'method') and sig:
-        params = ', '.join(param.name for param in sig[0].params)
-        return '{}({})'.format(definition.name, params)
-
+def _label(definition, resolve=False):
+    if not resolve:
+        return definition.name
+    sig = LABEL_RESOLVER.get_or_create(definition)
+    if sig:
+        return sig
     return definition.name
 
 
@@ -186,3 +244,86 @@
     # If its 'hidden', put it next last
     prefix = 'z{}' if definition.name.startswith('_') else 'a{}'
     return prefix.format(definition.name)
+
+
+class LabelResolver:
+
+    def __init__(self, format_label_callback, time_to_live=60 * 30):
+        self.format_label = format_label_callback
+        self._cache = {}
+        self._time_to_live = time_to_live
+        self._cache_ttl = defaultdict(set)
+        self._clear_every = 2
+        # see 
https://github.com/davidhalter/jedi/blob/master/jedi/inference/helpers.py#L194-L202
+        self._cached_modules = {'pandas', 'numpy', 'tensorflow', 'matplotlib'}
+
+    @property
+    def cached_modules(self):
+        return self._cached_modules
+
+    @cached_modules.setter
+    def cached_modules(self, new_value):
+        self._cached_modules = set(new_value)
+
+    def clear_outdated(self):
+        now = self.time_key()
+        to_clear = [
+            timestamp
+            for timestamp in self._cache_ttl
+            if timestamp < now
+        ]
+        for time_key in to_clear:
+            for key in self._cache_ttl[time_key]:
+                del self._cache[key]
+            del self._cache_ttl[time_key]
+
+    def time_key(self):
+        return int(time() / self._time_to_live)
+
+    def get_or_create(self, completion: Completion):
+        if not completion.full_name:
+            use_cache = False
+        else:
+            module_parts = completion.full_name.split('.')
+            use_cache = module_parts and module_parts[0] in 
self._cached_modules
+
+        if use_cache:
+            key = self._create_completion_id(completion)
+            if key not in self._cache:
+                if self.time_key() % self._clear_every == 0:
+                    self.clear_outdated()
+
+                self._cache[key] = self.resolve_label(completion)
+                self._cache_ttl[self.time_key()].add(key)
+            return self._cache[key]
+
+        return self.resolve_label(completion)
+
+    def _create_completion_id(self, completion: Completion):
+        return (
+            completion.full_name, completion.module_path,
+            completion.line, completion.column,
+            self.time_key()
+        )
+
+    def resolve_label(self, completion):
+        try:
+            sig = completion.get_signatures()
+            return self.format_label(completion, sig)
+        except Exception as e:  # pylint: disable=broad-except
+            log.warning(
+                'Something went wrong when resolving label for {completion}: 
{e}',
+                completion=completion, e=e
+            )
+            return ''
+
+
+def format_label(completion, sig):
+    if sig and completion.type in ('function', 'method'):
+        params = ', '.join(param.name for param in sig[0].params)
+        label = '{}({})'.format(completion.name, params)
+        return label
+    return completion.name
+
+
+LABEL_RESOLVER = LabelResolver(format_label)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-lsp-server-1.1.0/pylsp/plugins/rope_completion.py 
new/python-lsp-server-1.2.1/pylsp/plugins/rope_completion.py
--- old/python-lsp-server-1.1.0/pylsp/plugins/rope_completion.py        
2021-04-14 23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/plugins/rope_completion.py        
2021-07-28 07:34:07.000000000 +0200
@@ -13,11 +13,26 @@
 @hookimpl
 def pylsp_settings():
     # Default rope_completion to disabled
-    return {'plugins': {'rope_completion': {'enabled': False}}}
+    return {'plugins': {'rope_completion': {'enabled': False, 'eager': False}}}
+
+
+def _resolve_completion(completion, data):
+    try:
+        doc = data.get_doc()
+    except AttributeError:
+        doc = ""
+    completion['detail'] = '{0} {1}'.format(data.scope or "", data.name)
+    completion['documentation'] = doc
+    return completion
 
 
 @hookimpl
 def pylsp_completions(config, workspace, document, position):
+    # pylint: disable=too-many-locals
+
+    settings = config.plugin_settings('rope_completion', 
document_path=document.path)
+    resolve_eagerly = settings.get('eager', False)
+
     # Rope is a bit rubbish at completing module imports, so we'll return None
     word = document.word_at_position({
         # The -1 should really be trying to look at the previous word, but 
that might be quite expensive
@@ -41,22 +56,37 @@
     definitions = sorted_proposals(definitions)
     new_definitions = []
     for d in definitions:
-        try:
-            doc = d.get_doc()
-        except AttributeError:
-            doc = None
-        new_definitions.append({
+        item = {
             'label': d.name,
             'kind': _kind(d),
-            'detail': '{0} {1}'.format(d.scope or "", d.name),
-            'documentation': doc or "",
-            'sortText': _sort_text(d)
-        })
+            'sortText': _sort_text(d),
+            'data': {
+                'doc_uri': document.uri
+            }
+        }
+        if resolve_eagerly:
+            item = _resolve_completion(item, d)
+        new_definitions.append(item)
+
+    # most recently retrieved completion items, used for resolution
+    document.shared_data['LAST_ROPE_COMPLETIONS'] = {
+        # label is the only required property; here it is assumed to be unique
+        completion['label']: (completion, data)
+        for completion, data in zip(new_definitions, definitions)
+    }
+
     definitions = new_definitions
 
     return definitions or None
 
 
+@hookimpl
+def pylsp_completion_item_resolve(completion_item, document):
+    """Resolve formatted completion for given non-resolved completion"""
+    completion, data = 
document.shared_data['LAST_ROPE_COMPLETIONS'].get(completion_item['label'])
+    return _resolve_completion(completion, data)
+
+
 def _sort_text(definition):
     """ Ensure builtins appear at the bottom.
     Description is of format <type>: <module>.<item>
@@ -72,7 +102,7 @@
 
 
 def _kind(d):
-    """ Return the VSCode type """
+    """ Return the LSP type """
     MAP = {
         'none': lsp.CompletionItemKind.Value,
         'type': lsp.CompletionItemKind.Class,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/pylsp/plugins/symbols.py 
new/python-lsp-server-1.2.1/pylsp/plugins/symbols.py
--- old/python-lsp-server-1.1.0/pylsp/plugins/symbols.py        2021-06-25 
23:26:52.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/plugins/symbols.py        2021-08-01 
21:49:22.000000000 +0200
@@ -19,28 +19,46 @@
     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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/pylsp/python_lsp.py 
new/python-lsp-server-1.2.1/pylsp/python_lsp.py
--- old/python-lsp-server-1.1.0/pylsp/python_lsp.py     2021-06-25 
23:38:40.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/python_lsp.py     2021-08-01 
21:49:22.000000000 +0200
@@ -162,8 +162,8 @@
                 'resolveProvider': False,  # We may need to make this 
configurable
             },
             'completionProvider': {
-                'resolveProvider': False,  # We know everything ahead of time
-                'triggerCharacters': ['.']
+                'resolveProvider': True,  # We could know everything ahead of 
time, but this takes time to transfer
+                'triggerCharacters': ['.'],
             },
             'documentFormattingProvider': True,
             'documentHighlightProvider': True,
@@ -199,7 +199,8 @@
         log.info('Server capabilities: %s', server_capabilities)
         return server_capabilities
 
-    def m_initialize(self, processId=None, rootUri=None, rootPath=None, 
initializationOptions=None, **_kwargs):
+    def m_initialize(self, processId=None, rootUri=None, rootPath=None,
+                     initializationOptions=None, workspaceFolders=None, 
**_kwargs):
         log.debug('Language server initialized with %s %s %s %s', processId, 
rootUri, rootPath, initializationOptions)
         if rootUri is None:
             rootUri = uris.from_fs_path(rootPath) if rootPath is not None else 
''
@@ -210,6 +211,19 @@
                                     processId, _kwargs.get('capabilities', {}))
         self.workspace = Workspace(rootUri, self._endpoint, self.config)
         self.workspaces[rootUri] = self.workspace
+        if workspaceFolders:
+            for folder in workspaceFolders:
+                uri = folder['uri']
+                if uri == rootUri:
+                    # Already created
+                    continue
+                workspace_config = config.Config(
+                    uri, self.config._init_opts,
+                    self.config._process_id, self.config._capabilities)
+                workspace_config.update(self.config._settings)
+                self.workspaces[uri] = Workspace(
+                    uri, self._endpoint, workspace_config)
+
         self._dispatchers = self._hook('pylsp_dispatchers')
         self._hook('pylsp_initialize')
 
@@ -250,6 +264,10 @@
             'items': flatten(completions)
         }
 
+    def completion_item_resolve(self, completion_item):
+        doc_uri = completion_item.get('data', {}).get('doc_uri', None)
+        return self._hook('pylsp_completion_item_resolve', doc_uri, 
completion_item=completion_item)
+
     def definitions(self, doc_uri, position):
         return flatten(self._hook('pylsp_definitions', doc_uri, 
position=position))
 
@@ -296,6 +314,9 @@
     def folding(self, doc_uri):
         return flatten(self._hook('pylsp_folding_range', doc_uri))
 
+    def m_completion_item__resolve(self, **completionItem):
+        return self.completion_item_resolve(completionItem)
+
     def m_text_document__did_close(self, textDocument=None, **_kwargs):
         workspace = self._match_uri_to_workspace(textDocument['uri'])
         workspace.rm_document(textDocument['uri'])
@@ -362,9 +383,9 @@
         return self.signature_help(textDocument['uri'], position)
 
     def m_workspace__did_change_configuration(self, settings=None):
-        self.config.update((settings or {}).get('pylsp', {}))
-        for workspace_uri in self.workspaces:
-            workspace = self.workspaces[workspace_uri]
+        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:
                 self.lint(doc_uri, is_saved=False)
@@ -433,8 +454,7 @@
             # Only externally changed python files and lint configs may result 
in changed diagnostics.
             return
 
-        for workspace_uri in self.workspaces:
-            workspace = self.workspaces[workspace_uri]
+        for workspace in self.workspaces.values():
             for doc_uri in workspace.documents:
                 # Changes in doc_uri are already handled by 
m_text_document__did_save
                 if doc_uri not in changed_py_files:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/pylsp/workspace.py 
new/python-lsp-server-1.2.1/pylsp/workspace.py
--- old/python-lsp-server-1.1.0/pylsp/workspace.py      2021-04-14 
23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/pylsp/workspace.py      2021-08-01 
21:49:22.000000000 +0200
@@ -138,6 +138,7 @@
         self.path = uris.to_fs_path(uri)
         self.dot_path = _utils.path_to_dot_name(self.path)
         self.filename = os.path.basename(self.path)
+        self.shared_data = {}
 
         self._config = workspace._config
         self._workspace = workspace
@@ -238,8 +239,8 @@
         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)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-lsp-server-1.1.0/python_lsp_server.egg-info/PKG-INFO 
new/python-lsp-server-1.2.1/python_lsp_server.egg-info/PKG-INFO
--- old/python-lsp-server-1.1.0/python_lsp_server.egg-info/PKG-INFO     
2021-06-25 23:44:57.000000000 +0200
+++ new/python-lsp-server-1.2.1/python_lsp_server.egg-info/PKG-INFO     
2021-08-04 19:11:44.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: python-lsp-server
-Version: 1.1.0
+Version: 1.2.1
 Summary: Python Language Server for the Language Server Protocol
 Home-page: https://github.com/python-lsp/python-lsp-server
 Author: Python Language Server Contributors
@@ -83,11 +83,13 @@
 
 The default configuration source is pycodestyle. Change the 
`pylsp.configurationSources` setting to `['flake8']` in order to respect flake8 
configuration instead.
 
-Overall configuration is computed first from user configuration (in home 
directory), overridden by configuration passed in by the language client, and 
then overriden by configuration discovered in the workspace.
+Overall configuration is computed first from user configuration (in home 
directory), overridden by configuration passed in by the language client, and 
then overridden by configuration discovered in the workspace.
 
 To enable pydocstyle for linting docstrings add the following setting in your 
LSP configuration:
 `"pylsp.plugins.pydocstyle.enabled": true`
 
+All configuration options are described in 
[`CONFIGURATION.md`](https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md).
+
 ## LSP Server Features
 
 * Auto Completion
@@ -105,8 +107,14 @@
 
 To run the test suite:
 
+```sh
+pip install '.[test]' && pytest
+```
+
+After adding configuration options to `schema.json`, refresh the 
`CONFIGURATION.md` file with
+
 ```
-pip install .[test] && pytest
+python scripts/jsonschema2md.py pylsp/config/schema.json CONFIGURATION.md
 ```
 
 ## License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/test/fixtures.py 
new/python-lsp-server-1.2.1/test/fixtures.py
--- old/python-lsp-server-1.1.0/test/fixtures.py        2021-04-14 
23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/test/fixtures.py        2021-07-28 
07:34:07.000000000 +0200
@@ -35,6 +35,34 @@
 
 
 @pytest.fixture
+def pylsp_w_workspace_folders(tmpdir):
+    """ Return an initialized python LS """
+    ls = PythonLSPServer(StringIO, StringIO)
+
+    folder1 = tmpdir.mkdir('folder1')
+    folder2 = tmpdir.mkdir('folder2')
+
+    ls.m_initialize(
+        processId=1,
+        rootUri=uris.from_fs_path(str(folder1)),
+        initializationOptions={},
+        workspaceFolders=[
+            {
+                'uri': uris.from_fs_path(str(folder1)),
+                'name': 'folder1'
+            },
+            {
+                'uri': uris.from_fs_path(str(folder2)),
+                'name': 'folder2'
+            }
+        ]
+    )
+
+    workspace_folders = [folder1, folder2]
+    return (ls, workspace_folders)
+
+
+@pytest.fixture
 def workspace(tmpdir):
     """Return a workspace."""
     ws = Workspace(uris.from_fs_path(str(tmpdir)), Mock())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-lsp-server-1.1.0/test/plugins/test_completion.py 
new/python-lsp-server-1.2.1/test/plugins/test_completion.py
--- old/python-lsp-server-1.1.0/test/plugins/test_completion.py 2021-06-25 
23:26:52.000000000 +0200
+++ new/python-lsp-server-1.2.1/test/plugins/test_completion.py 2021-07-28 
18:44:09.000000000 +0200
@@ -1,6 +1,7 @@
 # Copyright 2017-2020 Palantir Technologies, Inc.
 # Copyright 2021- Python Language Server Contributors.
 
+import math
 import os
 import sys
 
@@ -12,6 +13,7 @@
 from pylsp import uris, lsp
 from pylsp.workspace import Document
 from pylsp.plugins.jedi_completion import pylsp_completions as 
pylsp_jedi_completions
+from pylsp.plugins.jedi_completion import pylsp_completion_item_resolve as 
pylsp_jedi_completion_item_resolve
 from pylsp.plugins.rope_completion import pylsp_completions as 
pylsp_rope_completions
 from pylsp._utils import JEDI_VERSION
 
@@ -44,6 +46,10 @@
 print Hello().world
 
 print Hello().every
+
+def documented_hello():
+    \"\"\"Sends a polite greeting\"\"\"
+    pass
 """
 
 
@@ -139,6 +145,27 @@
     pylsp_jedi_completions(config, doc, {'line': 1, 'character': 1000})
 
 
+def test_jedi_completion_item_resolve(config, workspace):
+    # Over the blank line
+    com_position = {'line': 8, 'character': 0}
+    doc = Document(DOC_URI, workspace, DOC)
+    config.update({'plugins': {'jedi_completion': {'resolve_at_most_labels': 
math.inf}}})
+    completions = pylsp_jedi_completions(config, doc, com_position)
+
+    items = {c['label']: c for c in completions}
+
+    documented_hello_item = items['documented_hello()']
+
+    assert 'documentation' not in documented_hello_item
+    assert 'detail' not in documented_hello_item
+
+    resolved_documented_hello = pylsp_jedi_completion_item_resolve(
+        completion_item=documented_hello_item,
+        document=doc
+    )
+    assert 'Sends a polite greeting' in 
resolved_documented_hello['documentation']
+
+
 def test_jedi_completion_with_fuzzy_enabled(config, workspace):
     # Over 'i' in os.path.isabs(...)
     config.update({'plugins': {'jedi_completion': {'fuzzy': True}}})
@@ -154,6 +181,24 @@
     pylsp_jedi_completions(config, doc, {'line': 1, 'character': 1000})
 
 
+def test_jedi_completion_resolve_at_most(config, workspace):
+    # Over 'i' in os.path.isabs(...)
+    com_position = {'line': 1, 'character': 15}
+    doc = Document(DOC_URI, workspace, DOC)
+
+    # Do not resolve any labels
+    config.update({'plugins': {'jedi_completion': {'resolve_at_most_labels': 
0}}})
+    items = pylsp_jedi_completions(config, doc, com_position)
+    labels = {i['label'] for i in items}
+    assert 'isabs' in labels
+
+    # Resolve all items
+    config.update({'plugins': {'jedi_completion': {'resolve_at_most_labels': 
math.inf}}})
+    items = pylsp_jedi_completions(config, doc, com_position)
+    labels = {i['label'] for i in items}
+    assert 'isabs(path)' in labels
+
+
 def test_rope_completion(config, workspace):
     # Over 'i' in os.path.isabs(...)
     com_position = {'line': 1, 'character': 15}
@@ -169,6 +214,7 @@
     # Over the blank line
     com_position = {'line': 8, 'character': 0}
     doc = Document(DOC_URI, workspace, DOC)
+    config.update({'plugins': {'jedi_completion': {'resolve_at_most_labels': 
math.inf}}})
     completions = pylsp_jedi_completions(config, doc, com_position)
 
     items = {c['label']: c['sortText'] for c in completions}
@@ -410,7 +456,9 @@
     # After 'import logh' with new environment
     completions = pylsp_jedi_completions(doc._config, doc, com_position)
     assert completions[0]['label'] == 'loghub'
-    assert 'changelog generator' in completions[0]['documentation'].lower()
+
+    resolved = pylsp_jedi_completion_item_resolve(completions[0], doc)
+    assert 'changelog generator' in resolved['documentation'].lower()
 
 
 def test_document_path_completions(tmpdir, workspace_other_root_path):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-lsp-server-1.1.0/test/plugins/test_definitions.py 
new/python-lsp-server-1.2.1/test/plugins/test_definitions.py
--- old/python-lsp-server-1.1.0/test/plugins/test_definitions.py        
2021-04-14 23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/test/plugins/test_definitions.py        
2021-07-31 20:34:03.000000000 +0200
@@ -1,6 +1,8 @@
 # Copyright 2017-2020 Palantir Technologies, Inc.
 # Copyright 2021- Python Language Server Contributors.
 
+import os
+
 from pylsp import uris
 from pylsp.plugins.definition import pylsp_definitions
 from pylsp.workspace import Document
@@ -57,3 +59,36 @@
 
     doc = Document(DOC_URI, workspace, DOC)
     assert [{'uri': DOC_URI, 'range': def_range}] == pylsp_definitions(config, 
doc, cursor_pos)
+
+
+def test_document_path_definitions(config, workspace_other_root_path, tmpdir):
+    # Create a dummy module out of the workspace's root_path and try to get
+    # a definition on it in another file placed next to it.
+    module_content = '''
+def foo():
+    pass
+'''
+
+    p = tmpdir.join("mymodule.py")
+    p.write(module_content)
+
+    # Content of doc to test definition
+    doc_content = """from mymodule import foo"""
+    doc_path = str(tmpdir) + os.path.sep + 'myfile.py'
+    doc_uri = uris.from_fs_path(doc_path)
+    doc = Document(doc_uri, workspace_other_root_path, doc_content)
+
+    # The range where is defined in mymodule.py
+    def_range = {
+        'start': {'line': 1, 'character': 4},
+        'end': {'line': 1, 'character': 7}
+    }
+
+    # The position where foo is called in myfile.py
+    cursor_pos = {'line': 0, 'character': 24}
+
+    # The uri for mymodule.py
+    module_path = str(p)
+    module_uri = uris.from_fs_path(module_path)
+
+    assert [{'uri': module_uri, 'range': def_range}] == 
pylsp_definitions(config, doc, cursor_pos)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/test/plugins/test_hover.py 
new/python-lsp-server-1.2.1/test/plugins/test_hover.py
--- old/python-lsp-server-1.1.0/test/plugins/test_hover.py      2021-04-14 
23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/test/plugins/test_hover.py      2021-07-31 
20:34:03.000000000 +0200
@@ -1,6 +1,8 @@
 # Copyright 2017-2020 Palantir Technologies, Inc.
 # Copyright 2021- Python Language Server Contributors.
 
+import os
+
 from pylsp import uris
 from pylsp.plugins.hover import pylsp_hover
 from pylsp.workspace import Document
@@ -72,3 +74,28 @@
     } == pylsp_hover(doc, hov_position)
 
     assert {'contents': ''} == pylsp_hover(doc, no_hov_position)
+
+
+def test_document_path_hover(workspace_other_root_path, tmpdir):
+    # Create a dummy module out of the workspace's root_path and try to get
+    # a definition on it in another file placed next to it.
+    module_content = '''
+def foo():
+    """A docstring for foo."""
+    pass
+'''
+
+    p = tmpdir.join("mymodule.py")
+    p.write(module_content)
+
+    # Content of doc to test definition
+    doc_content = """from mymodule import foo
+foo"""
+    doc_path = str(tmpdir) + os.path.sep + 'myfile.py'
+    doc_uri = uris.from_fs_path(doc_path)
+    doc = Document(doc_uri, workspace_other_root_path, doc_content)
+
+    cursor_pos = {'line': 1, 'character': 3}
+    contents = pylsp_hover(doc, cursor_pos)['contents']
+
+    assert contents[1] == 'A docstring for foo.'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/test/test_language_server.py 
new/python-lsp-server-1.2.1/test/test_language_server.py
--- old/python-lsp-server-1.1.0/test/test_language_server.py    2021-04-22 
23:56:35.000000000 +0200
+++ new/python-lsp-server-1.2.1/test/test_language_server.py    2021-08-04 
19:08:14.000000000 +0200
@@ -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,8 @@
     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', {
         'rootPath': os.path.dirname(__file__),
@@ -83,6 +86,7 @@
     assert 'capabilities' in response
 
 
+@flaky(max_runs=10, min_passes=1)
 @pytest.mark.skipif(os.name == 'nt' or (sys.platform.startswith('linux') and 
PY3),
                     reason='Skipped on win and fails on linux >=3.6')
 def test_exit_with_parent_process_died(client_exited_server):  # pylint: 
disable=redefined-outer-name
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/test/test_utils.py 
new/python-lsp-server-1.2.1/test/test_utils.py
--- old/python-lsp-server-1.1.0/test/test_utils.py      2021-06-25 
23:26:52.000000000 +0200
+++ new/python-lsp-server-1.2.1/test/test_utils.py      2021-07-11 
07:05:46.000000000 +0200
@@ -2,8 +2,8 @@
 # Copyright 2021- Python Language Server Contributors.
 
 import time
+from unittest import mock
 
-import unittest.mock as mock
 from flaky import flaky
 
 from pylsp import _utils
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-lsp-server-1.1.0/test/test_workspace.py 
new/python-lsp-server-1.2.1/test/test_workspace.py
--- old/python-lsp-server-1.1.0/test/test_workspace.py  2021-04-14 
23:02:42.000000000 +0200
+++ new/python-lsp-server-1.2.1/test/test_workspace.py  2021-07-28 
07:34:07.000000000 +0200
@@ -69,6 +69,45 @@
     assert workspace_root in test_doc.sys_path()
 
 
+def test_multiple_workspaces_from_initialize(pylsp_w_workspace_folders):
+    pylsp, workspace_folders = pylsp_w_workspace_folders
+
+    assert len(pylsp.workspaces) == 2
+
+    folders_uris = [uris.from_fs_path(str(folder)) for folder in 
workspace_folders]
+
+    for folder_uri in folders_uris:
+        assert folder_uri in pylsp.workspaces
+
+    assert folders_uris[0] == pylsp.root_uri
+
+    # Create file in the first workspace folder.
+    file1 = workspace_folders[0].join('file1.py')
+    file1.write('import os')
+    msg1 = {
+        'uri': path_as_uri(str(file1)),
+        'version': 1,
+        'text': 'import os'
+    }
+
+    pylsp.m_text_document__did_open(textDocument=msg1)
+    assert msg1['uri'] in pylsp.workspace._docs
+    assert msg1['uri'] in pylsp.workspaces[folders_uris[0]]._docs
+
+    # Create file in the second workspace folder.
+    file2 = workspace_folders[1].join('file2.py')
+    file2.write('import sys')
+    msg2 = {
+        'uri': path_as_uri(str(file2)),
+        'version': 1,
+        'text': 'import sys'
+    }
+
+    pylsp.m_text_document__did_open(textDocument=msg2)
+    assert msg2['uri'] not in pylsp.workspace._docs
+    assert msg2['uri'] in pylsp.workspaces[folders_uris[1]]._docs
+
+
 def test_multiple_workspaces(tmpdir, pylsp):
     workspace1_dir = tmpdir.mkdir('workspace1')
     workspace2_dir = tmpdir.mkdir('workspace2')

Reply via email to