Skip to content
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

Black format Repo #407

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
70076fa
README.md
tkrabel-db Jun 5, 2023
ba0f9ad
Add notebook support
tkrabel-db Jun 7, 2023
d5bf4f9
Add example messages for reference
tkrabel-db Jun 7, 2023
78233c7
lsp_types: add DocumentUri and use correctly
tkrabel-db Jun 7, 2023
09d6879
add diagnostics support
tkrabel-db Jun 11, 2023
500e120
support editing, adding, and removing cells
tkrabel-db Jun 12, 2023
b407a31
add unit tests for notebook document messages
tkrabel-db Jun 13, 2023
53e4ad6
add test_notebook_document
tkrabel-db Jun 13, 2023
c891cf0
fix unit tests and pylint
tkrabel-db Jun 13, 2023
3dcaf9f
fix pytest test_notebook_document.py
tkrabel-db Jun 14, 2023
ab3bcee
Fix pylint issues:
tkrabel-db Jun 14, 2023
2dd7165
support notebookDocument__did_close
tkrabel-db Jun 14, 2023
7722239
Add notebookDocumentSync to capabilities
tkrabel-db Jun 15, 2023
6c00bfc
Add notebookDocumentSync to capabilities
tkrabel-db Jun 15, 2023
60517c1
fix: publishDiagnostics line starts at line 0
tkrabel-db Jul 10, 2023
ecc27ee
fix: publishDiagnostics starts at 0 and newlines are counted correctly
tkrabel-db Jul 10, 2023
25cdfab
fix: publishDiagnostics starts at 0 and newlines are counted correctly
tkrabel-db Jul 10, 2023
2b35c95
fix: publishDiagnostics starts at 0 and newlines are counted correctly
tkrabel-db Jul 10, 2023
69a1621
fix: publishDiagnostics starts at 0 and newlines are counted correctly
tkrabel-db Jul 10, 2023
1e9a51f
fix: publishDiagnostics starts at 0 and newlines are counted correctly
tkrabel-db Jul 10, 2023
4f140da
fix: publishDiagnostics starts at 0 and newlines are counted correctly
tkrabel-db Jul 10, 2023
e79bbb2
feat: close cell if deleted
tkrabel-db Jul 20, 2023
ce9f458
skip tests on windows as it's flaky on py3.7
tkrabel-db Jul 22, 2023
5434d1f
fix test_notebook_document__did_change: need to wait for 2 calls to d…
tkrabel-db Jul 24, 2023
db13ca3
black autoformat the repo
tkrabel-db Jul 27, 2023
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,24 @@ pip install 'python-lsp-server[websockets]'

## Development

Dev install

```
# create conda env
cc python-lsp-server
ca python-lsp-server

pip install ".[all]"
pip install ".[websockets]"
```

Run server with ws

```
pylsp --ws -v # Info level logging
pylsp --ws -v -v # Debug level logging
```

To run the test suite:

```sh
Expand Down
4 changes: 2 additions & 2 deletions pylsp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ def convert_version_info(version: str) -> (int, ..., str):

_version.VERSION_INFO = convert_version_info(__version__)

PYLSP = 'pylsp'
IS_WIN = os.name == 'nt'
PYLSP = "pylsp"
IS_WIN = os.name == "nt"

hookspec = pluggy.HookspecMarker(PYLSP)
hookimpl = pluggy.HookimplMarker(PYLSP)
Expand Down
69 changes: 36 additions & 33 deletions pylsp/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,58 +12,58 @@
except Exception: # pylint: disable=broad-except
import json

from .python_lsp import (PythonLSPServer, start_io_lang_server,
start_tcp_lang_server, start_ws_lang_server)
from .python_lsp import (
PythonLSPServer,
start_io_lang_server,
start_tcp_lang_server,
start_ws_lang_server,
)
from ._version import __version__

LOG_FORMAT = "%(asctime)s {0} - %(levelname)s - %(name)s - %(message)s".format(
time.localtime().tm_zone)
time.localtime().tm_zone
)


def add_arguments(parser):
parser.description = "Python Language Server"

parser.add_argument(
"--tcp", action="store_true",
help="Use TCP server instead of stdio"
"--tcp", action="store_true", help="Use TCP server instead of stdio"
)
parser.add_argument(
"--ws", action="store_true",
help="Use Web Sockets server instead of stdio"
"--ws", action="store_true", help="Use Web Sockets server instead of stdio"
)
parser.add_argument("--host", default="127.0.0.1", help="Bind to this address")
parser.add_argument("--port", type=int, default=2087, help="Bind to this port")
parser.add_argument(
"--host", default="127.0.0.1",
help="Bind to this address"
)
parser.add_argument(
"--port", type=int, default=2087,
help="Bind to this port"
)
parser.add_argument(
'--check-parent-process', action="store_true",
"--check-parent-process",
action="store_true",
help="Check whether parent process is still alive using os.kill(ppid, 0) "
"and auto shut down language server process when parent process is not alive."
"Note that this may not work on a Windows machine."
"Note that this may not work on a Windows machine.",
)

log_group = parser.add_mutually_exclusive_group()
log_group.add_argument(
"--log-config",
help="Path to a JSON file containing Python logging config."
"--log-config", help="Path to a JSON file containing Python logging config."
)
log_group.add_argument(
"--log-file",
help="Redirect logs to the given file instead of writing to stderr."
"Has no effect if used with --log-config."
"Has no effect if used with --log-config.",
)

parser.add_argument(
'-v', '--verbose', action='count', default=0,
help="Increase verbosity of log output, overrides log config file"
"-v",
"--verbose",
action="count",
default=0,
help="Increase verbosity of log output, overrides log config file",
)

parser.add_argument(
'-V', '--version', action='version', version='%(prog)s v' + __version__
"-V", "--version", action="version", version="%(prog)s v" + __version__
)


Expand All @@ -74,15 +74,14 @@ def main():
_configure_logger(args.verbose, args.log_config, args.log_file)

if args.tcp:
start_tcp_lang_server(args.host, args.port, args.check_parent_process,
PythonLSPServer)
start_tcp_lang_server(
args.host, args.port, args.check_parent_process, PythonLSPServer
)
elif args.ws:
start_ws_lang_server(args.port, args.check_parent_process,
PythonLSPServer)
start_ws_lang_server(args.port, args.check_parent_process, PythonLSPServer)
else:
stdin, stdout = _binary_stdio()
start_io_lang_server(stdin, stdout, args.check_parent_process,
PythonLSPServer)
start_io_lang_server(stdin, stdout, args.check_parent_process, PythonLSPServer)


def _binary_stdio():
Expand All @@ -99,14 +98,18 @@ def _configure_logger(verbose=0, log_config=None, log_file=None):
root_logger = logging.root

if log_config:
with open(log_config, 'r', encoding='utf-8') as f:
with open(log_config, "r", encoding="utf-8") as f:
logging.config.dictConfig(json.load(f))
else:
formatter = logging.Formatter(LOG_FORMAT)
if log_file:
log_handler = logging.handlers.RotatingFileHandler(
log_file, mode='a', maxBytes=50*1024*1024,
backupCount=10, encoding=None, delay=0
log_file,
mode="a",
maxBytes=50 * 1024 * 1024,
backupCount=10,
encoding=None,
delay=0,
)
else:
log_handler = logging.StreamHandler()
Expand All @@ -123,5 +126,5 @@ def _configure_logger(verbose=0, log_config=None, log_file=None):
root_logger.setLevel(level)


if __name__ == '__main__':
if __name__ == "__main__":
main()
70 changes: 37 additions & 33 deletions pylsp/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@

# Eol chars accepted by the LSP protocol
# the ordering affects performance
EOL_CHARS = ['\r\n', '\r', '\n']
EOL_CHARS = ["\r\n", "\r", "\n"]
EOL_REGEX = re.compile(f'({"|".join(EOL_CHARS)})')

log = logging.getLogger(__name__)


def debounce(interval_s, keyed_by=None):
"""Debounce calls to this function until interval_s seconds have passed."""

def wrapper(func):
timers = {}
lock = threading.Lock()
Expand All @@ -48,7 +49,9 @@ def run():
timer = threading.Timer(interval_s, run)
timers[key] = timer
timer.start()

return debounced

return wrapper


Expand Down Expand Up @@ -78,7 +81,9 @@ def find_parents(root, path, names):
# Search each of /a/b/c, /a/b, /a
while dirs:
search_dir = os.path.join(*dirs)
existing = list(filter(os.path.exists, [os.path.join(search_dir, n) for n in names]))
existing = list(
filter(os.path.exists, [os.path.join(search_dir, n) for n in names])
)
if existing:
return existing
dirs.pop()
Expand All @@ -92,11 +97,11 @@ def path_to_dot_name(path):
directory = os.path.dirname(path)
module_name, _ = os.path.splitext(os.path.basename(path))
full_name = [module_name]
while os.path.exists(os.path.join(directory, '__init__.py')):
while os.path.exists(os.path.join(directory, "__init__.py")):
this_directory = os.path.basename(directory)
directory = os.path.dirname(directory)
full_name = [this_directory] + full_name
return '.'.join(full_name)
return ".".join(full_name)


def match_uri_to_workspace(uri, workspaces):
Expand Down Expand Up @@ -128,6 +133,7 @@ def merge_dicts(dict_a, dict_b):

If override_nones is True, then
"""

def _merge_dicts_(a, b):
for key in set(a.keys()).union(b.keys()):
if key in a and key in b:
Expand All @@ -143,15 +149,16 @@ def _merge_dicts_(a, b):
yield (key, a[key])
elif b[key] is not None:
yield (key, b[key])

return dict(_merge_dicts_(dict_a, dict_b))


def escape_plain_text(contents: str) -> str:
"""
Format plain text to display nicely in environments which do not respect whitespaces.
"""
contents = contents.replace('\t', '\u00A0' * 4)
contents = contents.replace(' ', '\u00A0' * 2)
contents = contents.replace("\t", "\u00A0" * 4)
contents = contents.replace(" ", "\u00A0" * 2)
return contents


Expand All @@ -160,17 +167,17 @@ def escape_markdown(contents: str) -> str:
Format plain text to display nicely in Markdown environment.
"""
# escape markdown syntax
contents = re.sub(r'([\\*_#[\]])', r'\\\1', contents)
contents = re.sub(r"([\\*_#[\]])", r"\\\1", contents)
# preserve white space characters
contents = escape_plain_text(contents)
return contents


def wrap_signature(signature):
return '```python\n' + signature + '\n```\n'
return "```python\n" + signature + "\n```\n"


SERVER_SUPPORTED_MARKUP_KINDS = {'markdown', 'plaintext'}
SERVER_SUPPORTED_MARKUP_KINDS = {"markdown", "plaintext"}


def choose_markup_kind(client_supported_markup_kinds: List[str]):
Expand All @@ -181,10 +188,12 @@ def choose_markup_kind(client_supported_markup_kinds: List[str]):
for kind in client_supported_markup_kinds:
if kind in SERVER_SUPPORTED_MARKUP_KINDS:
return kind
return 'markdown'
return "markdown"


def format_docstring(contents: str, markup_kind: str, signatures: Optional[List[str]] = None):
def format_docstring(
contents: str, markup_kind: str, signatures: Optional[List[str]] = None
):
"""Transform the provided docstring into a MarkupContent object.

If `markup_kind` is 'markdown' the docstring will get converted to
Expand All @@ -195,33 +204,24 @@ def format_docstring(contents: str, markup_kind: str, signatures: Optional[List[
to the provided contents of the docstring if given.
"""
if not isinstance(contents, str):
contents = ''
contents = ""

if markup_kind == 'markdown':
if markup_kind == "markdown":
try:
value = docstring_to_markdown.convert(contents)
return {
'kind': 'markdown',
'value': value
}
return {"kind": "markdown", "value": value}
except docstring_to_markdown.UnknownFormatError:
# try to escape the Markdown syntax instead:
value = escape_markdown(contents)

if signatures:
value = wrap_signature('\n'.join(signatures)) + '\n\n' + value
value = wrap_signature("\n".join(signatures)) + "\n\n" + value

return {
'kind': 'markdown',
'value': value
}
return {"kind": "markdown", "value": value}
value = contents
if signatures:
value = '\n'.join(signatures) + '\n\n' + value
return {
'kind': 'plaintext',
'value': escape_plain_text(value)
}
value = "\n".join(signatures) + "\n\n" + value
return {"kind": "plaintext", "value": escape_plain_text(value)}


def clip_column(column, lines, line_number):
Expand All @@ -230,7 +230,9 @@ def clip_column(column, lines, line_number):

https://microsoft.github.io/language-server-protocol/specification#position
"""
max_column = len(lines[line_number].rstrip('\r\n')) if len(lines) > line_number else 0
max_column = (
len(lines[line_number].rstrip("\r\n")) if len(lines) > line_number else 0
)
return min(column, max_column)


Expand All @@ -242,14 +244,16 @@ def position_to_jedi_linecolumn(document, position):
"""
code_position = {}
if position:
code_position = {'line': position['line'] + 1,
'column': clip_column(position['character'],
document.lines,
position['line'])}
code_position = {
"line": position["line"] + 1,
"column": clip_column(
position["character"], document.lines, position["line"]
),
}
return code_position


if os.name == 'nt':
if os.name == "nt":
import ctypes

kernel32 = ctypes.windll.kernel32
Expand Down
Loading