Skip to content

setup isort & black, introduce pre-commit #331

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

Closed
wants to merge 2 commits into from
Closed
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
11 changes: 4 additions & 7 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,10 @@ jobs:
python-version: '3.7'
architecture: 'x64'
- run: python -m pip install --upgrade pip setuptools jsonschema
- run: pip install -e .[pylint,pycodestyle,pyflakes]
- name: Pylint checks
run: pylint pylsp test
- name: Code style checks
run: pycodestyle pylsp test
- name: Pyflakes checks
run: pyflakes pylsp test
- run: pip install -e .[lint]
- name: run linters via pre-commit
run: |
pre-commit run --all --show-diff-on-failure --color=always
- name: Validate JSON schema
run: echo {} | jsonschema pylsp/config/schema.json
- name: Ensure JSON schema and Markdown docs are in sync
Expand Down
33 changes: 33 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
repos:
- repo: local
hooks:
- id: isort
name: isort
entry: isort
language: system
types:
- python
- id: black
name: black
entry: black
language: system
types:
- python
- id: pyflakes
name: pyflakes
entry: pyflakes
language: system
types:
- python
- id: pylint
name: pylint
entry: pylint
language: system
types:
- python
- id: pycodestyle
name: pycodestyle
entry: pycodestyle
language: system
types:
- python
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@ To run the test suite:
pip install ".[test]" && pytest
```

To run the linters:
```sh
pip install ".[lint]" && pre-commit run --all
```
You can also locally run `pre-commit install` in your repo to run
linters on changed files when committing.

After adding configuration options to `schema.json`, refresh the `CONFIGURATION.md` file with

```
Expand Down
6 changes: 4 additions & 2 deletions pylsp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
# Copyright 2021- Python Language Server Contributors.

import os

import pluggy

from . import _version
from ._version import __version__

Expand All @@ -22,8 +24,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
62 changes: 19 additions & 43 deletions pylsp/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,39 @@
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 ._version import __version__
from .python_lsp import PythonLSPServer, start_io_lang_server, start_tcp_lang_server, start_ws_lang_server

LOG_FORMAT = "%(asctime)s {0} - %(levelname)s - %(name)s - %(message)s".format(
time.localtime().tm_zone)
LOG_FORMAT = "%(asctime)s {0} - %(levelname)s - %(name)s - %(message)s".format(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")
parser.add_argument("--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(
"--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"
)
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_group.add_argument("--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."
help="Redirect logs to the given file instead of writing to stderr." "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__
)
parser.add_argument("-V", "--version", action="version", version="%(prog)s v" + __version__)


def main():
Expand All @@ -74,15 +54,12 @@ 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 +76,13 @@ 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 +99,5 @@ def _configure_logger(verbose=0, log_config=None, log_file=None):
root_logger.setLevel(level)


if __name__ == '__main__':
if __name__ == "__main__":
main()
58 changes: 27 additions & 31 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 @@ -92,11 +95,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 +131,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 +147,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 +165,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,7 +186,7 @@ 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):
Expand All @@ -195,33 +200,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 +226,7 @@ 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 +238,14 @@ 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