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

pytest-lsp: Use hatch for linting and testing #167

Merged
merged 4 commits 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/pytest-lsp-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/pytest-lsp

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
28 changes: 2 additions & 26 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,15 @@ repos:
exclude: 'lib/pytest-lsp/pytest_lsp/clients/.*\.json'
- id: trailing-whitespace

- repo: https://github.com/psf/black
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 (pytest-lsp)
args: [--config=lib/pytest-lsp/setup.cfg]
files: 'lib/pytest-lsp/pytest_lsp/.*\.py'

- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- 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'
files: 'lib/.*\.py'

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

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

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

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

[[envs.hatch-test.matrix]]
python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
pytest = ["7", "8"]

[envs.hatch-test.overrides]
matrix.pytest.dependencies = [
{ value = "pytest>=7,<8", if = ["7"] },
{ value = "pytest>=8,<9", if = ["8"] },
]

[envs.hatch-static-analysis]
config-path = "ruff_defaults.toml"
dependencies = ["ruff==0.4.4"]
4 changes: 0 additions & 4 deletions lib/pytest-lsp/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ show_missing = true
skip_covered = true
sort = "Cover"

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

[tool.pytest.ini_options]
asyncio_mode = "auto"

Expand Down
9 changes: 5 additions & 4 deletions lib/pytest-lsp/pytest_lsp/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

"""

# ruff: noqa: S101
import logging
import warnings
from typing import Any
Expand Down Expand Up @@ -38,7 +39,7 @@ def check_result_for(*, method: str) -> Callable[[ResultChecker], ResultChecker]
"""Define a result check."""

def defcheck(fn: ResultChecker):
if (existing := RESULT_CHECKS.get(method, None)) is not None:
if (existing := RESULT_CHECKS.get(method)) is not None:
raise ValueError(f"{fn!r} conflicts with existing check {existing!r}")

RESULT_CHECKS[method] = fn
Expand All @@ -51,7 +52,7 @@ def check_params_of(*, method: str) -> Callable[[ParamsChecker], ParamsChecker]:
"""Define a params check."""

def defcheck(fn: ParamsChecker):
if (existing := PARAMS_CHECKS.get(method, None)) is not None:
if (existing := PARAMS_CHECKS.get(method)) is not None:
raise ValueError(f"{fn!r} conflicts with existing check {existing!r}")

PARAMS_CHECKS[method] = fn
Expand Down Expand Up @@ -86,7 +87,7 @@ def check_result_against_client_capabilities(
if capabilities == types.ClientCapabilities():
return

result_checker = RESULT_CHECKS.get(method, None)
result_checker = RESULT_CHECKS.get(method)
if result_checker is None:
return

Expand Down Expand Up @@ -121,7 +122,7 @@ def check_params_against_client_capabilities(
if capabilities == types.ClientCapabilities():
return

params_checker = PARAMS_CHECKS.get(method, None)
params_checker = PARAMS_CHECKS.get(method)
if params_checker is None:
return

Expand Down
39 changes: 23 additions & 16 deletions lib/pytest-lsp/pytest_lsp/client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import asyncio
import json
import logging
Expand All @@ -7,12 +9,6 @@
import traceback
import typing
import warnings
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Type
from typing import Union

from lsprotocol import types
from lsprotocol.converters import get_converter
Expand All @@ -25,10 +21,18 @@
from .checks import LspSpecificationWarning
from .protocol import LanguageClientProtocol

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]

if typing.TYPE_CHECKING:
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Type
from typing import Union


__version__ = "0.4.1"
Expand Down Expand Up @@ -61,9 +65,9 @@ def __init__(self, *args, configuration: Optional[Dict[str, Any]] = None, **kwar
self.diagnostics: Dict[str, List[types.Diagnostic]] = {}
"""Holds any recieved diagnostics."""

self.progress_reports: Dict[types.ProgressToken, List[types.ProgressParams]] = (
{}
)
self.progress_reports: Dict[
types.ProgressToken, List[types.ProgressParams]
] = {}
"""Holds any received progress updates."""

self.error: Optional[Exception] = None
Expand Down Expand Up @@ -283,7 +287,7 @@ def cancel_all_tasks(message: str):
"""Called to cancel all awaited tasks."""

for task in asyncio.all_tasks():
if sys.version_info.minor < 9:
if sys.version_info < (3, 9):
task.cancel()
else:
task.cancel(message)
Expand Down Expand Up @@ -318,17 +322,20 @@ def create_work_done_progress(
# TODO: Send an error reponse to the client - might require changes
# to pygls...
warnings.warn(
f"Duplicate progress token: {params.token!r}", LspSpecificationWarning
f"Duplicate progress token: {params.token!r}",
LspSpecificationWarning,
stacklevel=2,
)

client.progress_reports.setdefault(params.token, [])
return None

@client.feature(types.PROGRESS)
def progress(client: LanguageClient, params: types.ProgressParams):
if params.token not in client.progress_reports:
warnings.warn(
f"Unknown progress token: {params.token!r}", LspSpecificationWarning
f"Unknown progress token: {params.token!r}",
LspSpecificationWarning,
stacklevel=2,
)

if not params.value:
Expand Down Expand Up @@ -412,7 +419,7 @@ def client_capabilities(client_spec: str) -> types.ClientCapabilities:
filename = typing.cast(pathlib.Path, resource)

# Skip the README or any other files that we don't care about.
if not filename.suffix == ".json":
if filename.suffix != ".json":
continue

name, version = filename.stem.split("_v")
Expand Down
19 changes: 12 additions & 7 deletions lib/pytest-lsp/pytest_lsp/plugin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
from __future__ import annotations

import inspect
import logging
import sys
import textwrap
import typing
from typing import Any
from typing import Callable
from typing import Dict
from typing import List
from typing import Optional

import attrs
import pytest
Expand All @@ -17,6 +14,14 @@
from pytest_lsp.client import LanguageClient
from pytest_lsp.client import make_test_lsp_client

if typing.TYPE_CHECKING:
from typing import Any
from typing import Callable
from typing import Dict
from typing import List
from typing import Optional


logger = logging.getLogger("client")


Expand Down Expand Up @@ -128,9 +133,9 @@ def pytest_runtest_makereport(item: pytest.Item, call: pytest.CallInfo):


# anext() was added in 3.10
if sys.version_info.minor < 10:
if sys.version_info < (3, 10):

async def anext(it):
async def anext(it): # noqa: A001
return await it.__anext__()


Expand Down
4 changes: 3 additions & 1 deletion lib/pytest-lsp/pytest_lsp/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ async def send_request_async(self, method, params=None):
)
result = await super().send_request_async(method, params)
check_result_against_client_capabilities(
self._server.capabilities, method, result # type: ignore
self._server.capabilities,
method,
result, # type: ignore
)

return result
Expand Down
77 changes: 77 additions & 0 deletions lib/pytest-lsp/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