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

Added workaround and early returning if file name matches excluded arg #142

Merged
42 changes: 42 additions & 0 deletions bundled/tool/lsp_server.py
Expand Up @@ -3,8 +3,10 @@
"""Implementation of tool support over LSP."""
from __future__ import annotations

import argparse
import ast
import copy
import fnmatch
import json
import os
import pathlib
Expand Down Expand Up @@ -394,6 +396,11 @@ def _run_tool_on_document(
argv += TOOL_ARGS + settings["args"] + extra_args

if use_stdin:

if _is_file_in_excluded_pattern(document.path, argv):
log_to_output(f"Excluded file: {document.path} because it matches pattern in args")
return None

argv += ["-"]

if use_path:
Expand Down Expand Up @@ -545,6 +552,41 @@ def _to_run_result_with_logging(rpc_result: jsonrpc.RpcRunResult) -> utils.RunRe
return utils.RunResult(rpc_result.stdout, error)


def _is_file_in_excluded_pattern(file_path: str, argv: list(str)) -> bool:
arg = _parse_autopep_exclude_arg(argv)

if arg.exclude is not None:
exclude_patterns = _split_comma_separated(arg.exclude)

for pattern in exclude_patterns:
if fnmatch.fnmatch(file_path, pattern):
return True

return False


def _parse_autopep_exclude_arg(
argv: list(str)
):
parser = argparse.ArgumentParser(
description="Exclude Argument Parser"
)

parser.add_argument(
"--exclude",
metavar='globs',
required=False
)

exclude_argument, _ = parser.parse_known_args(argv)

return exclude_argument

def _split_comma_separated(string: str):
"""Return a set of strings."""
return {text.strip() for text in string.split(',') if text.strip()}


# *****************************************************
# Logging and notification.
# *****************************************************
Expand Down
2 changes: 2 additions & 0 deletions src/test/python_tests/test_data/sample3/sample.unformatted
@@ -0,0 +1,2 @@
import sys;print(sys.executable)
# There is intentionally no last empty line.
@@ -0,0 +1 @@

@@ -0,0 +1,2 @@
import sys;print(sys.executable)
# There is intentionally no last empty line.
3 changes: 3 additions & 0 deletions src/test/python_tests/test_data/sample4/sample_formatted.py
@@ -0,0 +1,3 @@
import sys
print(sys.executable)
# There is intentionally no last empty line.
141 changes: 140 additions & 1 deletion src/test/python_tests/test_formatting.py
Expand Up @@ -3,11 +3,12 @@
"""
Test for formatting over LSP.
"""
import copy
import pathlib

from hamcrest import assert_that, is_

from .lsp_test_client import constants, session, utils
from .lsp_test_client import constants, defaults, session, utils

FORMATTER = utils.get_server_info_defaults()
TIMEOUT = 10000 # 10 seconds
Expand Down Expand Up @@ -138,3 +139,141 @@ def test_skipping_site_packages_files():

expected = None
assert_that(actual, is_(expected))


def test_skipping_excluded_files():
"""Test skipping formatting when the file is in excluded pattern"""

UNFORMATTED_EXCLUDED_FILE_PATH = constants.TEST_DATA / "sample3" / "sample.unformatted"

contents = UNFORMATTED_EXCLUDED_FILE_PATH.read_text()

with utils.python_file(contents, UNFORMATTED_EXCLUDED_FILE_PATH.parent) as pf:
with session.LspSession() as ls_session:
# Use any stdlib path here
uri = utils.as_uri(str(pf))

init_args = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE)
init_options = init_args["initializationOptions"]
init_options["settings"][0]["args"] = ["--exclude=**/*.py"]
ls_session.initialize(init_args)
# ls_session.initialize()
kirankumarmanku marked this conversation as resolved.
Show resolved Hide resolved

ls_session.notify_did_open(
{
"textDocument": {
"uri": uri,
"languageId": "python",
"version": 1,
"text": UNFORMATTED_EXCLUDED_FILE_PATH.read_text(encoding="utf-8"),
}
}
)

actual = ls_session.text_document_formatting(
{
"textDocument": {"uri": uri},
# `options` is not used by black
"options": {"tabSize": 4, "insertSpaces": True},
}
)

expected = None
assert_that(actual, is_(expected))

def test_formatting_file_not_in_excluded_files():
"""Test formatting when the file is not in excluded pattern"""
FORMATTED_TEST_FILE_PATH = constants.TEST_DATA / "sample4" / "sample.py"
UNFORMATTED_INCLUDED_FILE_PATH = constants.TEST_DATA / "sample4" / "sample.included.unformatted"

contents = UNFORMATTED_INCLUDED_FILE_PATH.read_text()
lines = contents.splitlines(keepends=False)

with utils.python_file(contents, UNFORMATTED_INCLUDED_FILE_PATH.parent) as pf:
with session.LspSession() as ls_session:
# Use any stdlib path here
uri = utils.as_uri(str(pf))

init_args = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE)
init_options = init_args["initializationOptions"]
init_options["settings"][0]["args"] = ["--exclude=**/*exclude"]
ls_session.initialize(init_args)

ls_session.notify_did_open(
{
"textDocument": {
"uri": uri,
"languageId": "python",
"version": 1,
"text": UNFORMATTED_INCLUDED_FILE_PATH.read_text(encoding="utf-8"),
}
}
)

actual = ls_session.text_document_formatting(
{
"textDocument": {"uri": uri},
# `options` is not used by black
"options": {"tabSize": 4, "insertSpaces": True},
}
)

expected = [
{
"range": {
"start": {"line": 0, "character": 0},
"end": {"line": len(lines), "character": 0},
},
"newText": FORMATTED_TEST_FILE_PATH.read_text(),
}
]
assert_that(actual, is_(expected))

def test_formatting_file_with_excluded_and_other_args():
"""Test formatting when we have more arguments specified"""
FORMATTED_TEST_FILE_PATH = constants.TEST_DATA / "sample4" / "sample.py"
UNFORMATTED_INCLUDED_FILE_PATH = constants.TEST_DATA / "sample4" / "sample.included.unformatted"

contents = UNFORMATTED_INCLUDED_FILE_PATH.read_text()
lines = contents.splitlines(keepends=False)

with utils.python_file(contents, UNFORMATTED_INCLUDED_FILE_PATH.parent) as pf:

with session.LspSession() as ls_session:
# Use any stdlib path here
uri = utils.as_uri(str(pf))

init_args = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE)
init_options = init_args["initializationOptions"]
init_options["settings"][0]["args"] = ["--exclude=**/*exclude", "--aggressive"]
ls_session.initialize(init_args)

ls_session.notify_did_open(
{
"textDocument": {
"uri": uri,
"languageId": "python",
"version": 1,
"text": UNFORMATTED_INCLUDED_FILE_PATH.read_text(encoding="utf-8"),
}
}
)

actual = ls_session.text_document_formatting(
{
"textDocument": {"uri": uri},
# `options` is not used by black
"options": {"tabSize": 4, "insertSpaces": True},
}
)

expected = [
{
"range": {
"start": {"line": 0, "character": 0},
"end": {"line": len(lines), "character": 0},
},
"newText": FORMATTED_TEST_FILE_PATH.read_text(),
}
]
assert_that(actual, is_(expected))