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

lsp-devtools: Use hatch for linting and testing #166

Merged
merged 1 commit into from
May 22, 2024
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
6 changes: 2 additions & 4 deletions .github/workflows/lsp-devtools-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,11 @@ jobs:
- run: |
python --version
python -m pip install --upgrade pip
python -m pip install --upgrade tox
python -m pip install --upgrade hatch
name: Setup Environment

- run: |
cd lib/lsp-devtools

version=$(echo ${{ matrix.python-version }} | tr -d .)
python -m tox run -f "py${version}"
hatch test -i py=${{ matrix.python-version }}
shell: bash
name: Run Tests
23 changes: 13 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ repos:
rev: 24.4.2
hooks:
- id: black
name: black (pytest-lsp)
files: 'lib/pytest-lsp/pytest_lsp/.*\.py'

- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
name: flake8 (lsp-devtools)
args: [--config=lib/lsp-devtools/setup.cfg]
files: 'lib/lsp-devtools/lsp_devtools/.*\.py'

- id: flake8
name: flake8 (pytest-lsp)
args: [--config=lib/pytest-lsp/setup.cfg]
Expand All @@ -30,16 +27,22 @@ repos:
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
name: isort (lsp-devtools)
args: [--settings-file, lib/lsp-devtools/pyproject.toml]
files: 'lib/lsp-devtools/lsp_devtools/.*\.py'

- id: isort
name: isort (pytest-lsp)
args: [--settings-file, lib/pytest-lsp/pyproject.toml]
files: 'lib/pytest-lsp/pytest_lsp/.*\.py'


- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
hooks:
- id: ruff
args: [--fix]
files: 'lib/lsp-devtools/.*\.py'

- id: ruff-format
files: 'lib/lsp-devtools/.*\.py'

- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v1.10.0'
hooks:
Expand Down
7 changes: 7 additions & 0 deletions lib/lsp-devtools/hatch.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ include = ["lsp_devtools", "tests", "CHANGES.md"]

[build.targets.wheel]
packages = ["lsp_devtools"]

[envs.hatch-test]
extra-dependencies = ["pytest-asyncio"]

[envs.hatch-static-analysis]
config-path = "ruff_defaults.toml"
dependencies = ["ruff==0.4.4"]
11 changes: 7 additions & 4 deletions lib/lsp-devtools/lsp_devtools/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
import typing
from datetime import datetime
from datetime import timezone
from functools import partial
from uuid import uuid4

Expand All @@ -26,6 +27,7 @@

MessageHandler = Callable[[bytes], Union[None, Coroutine[Any, Any, None]]]

UTC = timezone.utc
logger = logging.getLogger("lsp_devtools.agent")


Expand Down Expand Up @@ -157,8 +159,8 @@ async def start(self):
self.reader, self.writer = await get_streams(self.stdin, self.stdout)

# Keep mypy happy
assert self.server.stdin
assert self.server.stdout
if self.server.stdin is None or self.server.stdout is None:
raise RuntimeError("Unable to find server I/O streams")

# Connect stdin to the subprocess' stdin
client_to_server = asyncio.create_task(
Expand Down Expand Up @@ -196,10 +198,11 @@ async def forward_message(

# Include some additional metadata before passing it onto the devtool.
# TODO: How do we make sure we choose the same encoding as `message`?
now = datetime.now(tz=UTC).isoformat()
fields = [
f"Message-Source: {source}\r\n".encode(),
f"Message-Session: {self.session_id}\r\n".encode(),
f"Message-Timestamp: {datetime.now().isoformat()}\r\n".encode(),
f"Message-Timestamp: {now}\r\n".encode(),
message,
]

Expand All @@ -224,7 +227,7 @@ async def stop(self):
self.server.kill()

args = {}
if sys.version_info.minor > 8:
if sys.version_info >= (3, 9):
args["msg"] = "lsp-devtools agent is stopping."

# Cancel the tasks connecting client to server
Expand Down
6 changes: 4 additions & 2 deletions lib/lsp-devtools/lsp_devtools/client/lsp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import importlib.metadata
import json
from datetime import datetime
from datetime import timezone
from typing import Optional
from uuid import uuid4

Expand All @@ -10,6 +11,7 @@

from lsp_devtools.agent import logger

UTC = timezone.utc
VERSION = importlib.metadata.version("lsp-devtools")


Expand All @@ -27,7 +29,7 @@ def _procedure_handler(self, message):
extra={
"Message-Source": "server",
"Message-Session": self.session_id,
"Message-Timestamp": datetime.now().isoformat(),
"Message-Timestamp": datetime.now(tz=UTC).isoformat(),
},
)
return super()._procedure_handler(message)
Expand All @@ -39,7 +41,7 @@ def _send_data(self, data):
extra={
"Message-Source": "client",
"Message-Session": self.session_id,
"Message-Timestamp": datetime.now().isoformat(),
"Message-Timestamp": datetime.now(tz=UTC).isoformat(),
},
)
return super()._send_data(data)
Expand Down
14 changes: 7 additions & 7 deletions lib/lsp-devtools/lsp_devtools/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

from lsp_devtools.handlers import LspMessage

if sys.version_info.minor < 9:
if sys.version_info < (3, 9):
import importlib_resources as resources
else:
import importlib.resources as resources # type: ignore[no-redef]
from importlib import resources # type: ignore[no-redef]


class Database:
Expand Down Expand Up @@ -64,11 +64,11 @@ async def cursor(self):
async def add_message(self, session: str, timestamp: str, source: str, rpc: dict):
"""Add a new rpc message to the database."""

msg_id = rpc.get("id", None)
method = rpc.get("method", None)
params = rpc.get("params", None)
result = rpc.get("result", None)
error = rpc.get("error", None)
msg_id = rpc.get("id")
method = rpc.get("method")
params = rpc.get("params")
result = rpc.get("result")
error = rpc.get("error")

async with self.cursor() as cursor:
await cursor.execute(
Expand Down
4 changes: 2 additions & 2 deletions lib/lsp-devtools/lsp_devtools/handlers/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from lsp_devtools.handlers import LspHandler
from lsp_devtools.handlers import LspMessage

if sys.version_info.minor < 9:
if sys.version_info < (3, 9):
import importlib_resources as resources
else:
import importlib.resources as resources # type: ignore[no-redef]
from importlib import resources # type: ignore[no-redef]


class SqlHandler(LspHandler):
Expand Down
16 changes: 8 additions & 8 deletions lib/lsp-devtools/lsp_devtools/record/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,44 +207,44 @@ def start_recording(args, extra: List[str]):
def setup_filter_args(cmd: argparse.ArgumentParser):
"""Add arguments that can be used to filter messages."""

filter = cmd.add_argument_group(
filter_ = cmd.add_argument_group(
title="filter options",
description=(
"select which messages to record, mutliple options will be ANDed together. "
"Does not apply to raw message capture"
),
)
filter.add_argument(
filter_.add_argument(
"--message-source",
default="both",
choices=["client", "server", "both"],
help="only include messages from the given source",
)
filter.add_argument(
filter_.add_argument(
"--include-message-type",
action="append",
default=[],
dest="include_message_types",
choices=["request", "response", "result", "error", "notification"],
help="only include the given message type(s)",
)
filter.add_argument(
filter_.add_argument(
"--exclude-message-type",
action="append",
dest="exclude_message_types",
default=[],
choices=["request", "response", "result", "error", "notification"],
help="omit the given message type(s)",
)
filter.add_argument(
filter_.add_argument(
"--include-method",
action="append",
dest="include_methods",
default=[],
metavar="METHOD",
help="only include the given messages for the given method(s)",
)
filter.add_argument(
filter_.add_argument(
"--exclude-method",
action="append",
dest="exclude_methods",
Expand Down Expand Up @@ -291,14 +291,14 @@ def cli(commands: argparse._SubParsersAction):
)

setup_filter_args(cmd)
format = cmd.add_argument_group(
format_ = cmd.add_argument_group(
title="formatting options",
description=(
"control how the recorded messages are formatted "
"(does not apply to SQLite output or raw message capture)"
),
)
format.add_argument(
format_.add_argument(
"-f",
"--format-message",
default=None,
Expand Down
2 changes: 1 addition & 1 deletion lib/lsp-devtools/lsp_devtools/record/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def filter(self, record: logging.LogRecord) -> bool:
message_type = get_message_type(message)
message_method = self._get_message_method(message_type, message)

if self.message_source != "both" and source != self.message_source:
if self.message_source not in {"both", source}:
return False

if self.include_message_types and not message_matches_type(
Expand Down
3 changes: 2 additions & 1 deletion lib/lsp-devtools/lsp_devtools/record/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def format(self, message: dict, accessor: Optional[str] = None) -> str:
if isinstance(index, int):
obj = obj[index]
continue
elif isinstance(index, slice):

if isinstance(index, slice):
obj = obj[index]

return sep.join([self.format(o, remainder) for o in obj])
Expand Down
4 changes: 0 additions & 4 deletions lib/lsp-devtools/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ show_missing = true
skip_covered = true
sort = "Cover"

[tool.isort]
force_single_line = true
profile = "black"

[tool.pyright]
venv = ".env"
include = ["lsp_devtools"]
Expand Down
77 changes: 77 additions & 0 deletions lib/lsp-devtools/ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
extend = "ruff_defaults.toml"
line-length = 88
indent-width = 4

[format]
# Be like black where possible
quote-style = "double"
indent-style = "space"
line-ending = "auto"
skip-magic-trailing-comma = false

[lint]
ignore = [
"BLE001", # catch Exception:
"INP001", # Complains about namespace packages
"PT018", # Assertion should be broken down into multiple parts
"T201", # print found
"TRY003", # Exception message defined outside of class

# The following were added when migrating to ruff, we might want to consider
# enabling some of these again at some point.
"A002", # argument shadowing
"ARG001", # unused function argument
"ARG002", # unused method argument
"C405", # rewrite as set literal
"C408", # dict(x=y)
"C416", # Unecessary dict comprehension
"C419", # Unecessary list comprehension
"E402", # module import not at top of file
"EM101", # raise ValueError("Literal string, not variable")
"EM102", # raise ValueError(f"-string, not variable")
"FBT001", # boolean arguments
"FBT002", # boolean arguments
"FLY002", # f-string alternative available
"G003", # logging statement uses f-string
"G004", # logging statement uses +
"G201", # logging.error(.., exc_info=True)
"N801", # naming conventions
"N802", # naming conventions
"N806", # naming conventions
"PERF401", # use list comprehension
"PERF402", # use list or list.copy
"PLR2004", # magic values
"PLW2901", # overwriting for-loop variable
"PT006", # Complains about how `pytest.mark.parametrize` parameters are passed
"PT011", # pytest.raises(ValueError)
"RET503", # Missing return
"RET504", # Unecessary assignment before return
"RET505", # Unecessary elif after return
"RUF001", # ambiguous characters
"RUF012", # Mutable ClassVar annotation...
"RUF015", # Prefer next(iter(...))
"SIM102", # Use single if
"SIM105", # Use contextlib.suppress(...)
"SIM108", # Use ternary operator
"SIM115", # Use key in dict
"SIM118", # Use key in dict
"SLF001", # private member access
"TCH001", # move import to type checking block
"TCH002", # move import to type checking block
"TCH003", # move import to type checking block
"TID252", # Absolute vs relative imports
"TRY300", # Move statement to else block
]

[lint.per-file-ignores]
"**/tests/**/*" = [
"S",
"SLF001", # private member accessed
]

[lint.isort]
force-single-line = true

[lint.pyupgrade]
# At least for now...
keep-runtime-typing = true
Loading