Skip to content

Commit

Permalink
Add linting and type checking
Browse files Browse the repository at this point in the history
Added a GitHub workflow to run pre-commit, pylint, and pyright.

pylint and pyright are not included as pre-commit checks, because they
need to analyze the project as a whole, not just the changed files, and
they do not work correctly in an isolated environment without having
access to dependencies.

Adjusted the pylint configuration to ignore diagnostics that are either
not useful or duplicate those reported by pyright.

Fixed all type errors reported by pyright.

Added VS Code extension recommendations for all the formatting and
linting tools, and enabled type checking in VS Code.
  • Loading branch information
joelspadin committed Aug 25, 2024
1 parent 0cb912f commit 4ddfabe
Show file tree
Hide file tree
Showing 30 changed files with 261 additions and 110 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI

on:
pull_request:
push:

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ">=3.10"
cache: "pip"
- name: Install dependencies
run: |
pip install .
pip install pylint
- name: pre-commit
uses: pre-commit/[email protected]

- name: pyright
uses: jakebailey/pyright-action@v2

- name: pylint
run: pylint zmk
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
fail_fast: false
repos:
- repo: https://github.com/psf/black
rev: "23.9.1"
rev: "24.8.0"
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: "5.13.2"
hooks:
- id: isort
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.2
rev: v1.5.5
hooks:
- id: remove-tabs
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: check-yaml
Expand Down
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.python"
]
}
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@
"**/templates/common/**": "mako"
},
"isort.check": true,
"isort.args": ["--profile", "black"]
"isort.args": ["--settings-path", "${workspaceFolder}"],
"python.analysis.importFormat": "relative",
"python.analysis.typeCheckingMode": "standard"
}
14 changes: 14 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ build-backend = "setuptools.build_meta"
[tool.isort]
profile = "black"

[tool.pylint.MASTER]
ignore = "_version.py"

[tool.pylint."MESSAGES CONTROL"]
disable = [
"arguments-differ", # Covered by pyright
"fixme",
"too-few-public-methods",
"too-many-arguments",
"too-many-branches",
"too-many-instance-attributes",
"too-many-locals",
]

[tool.setuptools]
packages = ["zmk"]

Expand Down
2 changes: 2 additions & 0 deletions zmk/backports.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Backports from Python > 3.10.
"""

# pyright: reportMissingImports = false

try:
# pylint: disable=unused-import
from enum import StrEnum
Expand Down
38 changes: 29 additions & 9 deletions zmk/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
Build matrix processing.
"""

import collections.abc
from collections.abc import Mapping, Sequence
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any, Iterable, Optional
from typing import Any, Iterable, Optional, TypeVar, cast, overload

import dacite

from .repo import Repo
from .yaml import YAML

T = TypeVar("T")


@dataclass
class BuildItem:
Expand Down Expand Up @@ -52,7 +54,7 @@ class BuildMatrix:

_path: Path
_yaml: YAML
_data: Any
_data: dict[str, Any] | None

@classmethod
def from_repo(cls, repo: Repo):
Expand All @@ -64,7 +66,7 @@ def __init__(self, path: Path) -> None:
self._yaml = YAML(typ="rt")
self._yaml.indent(mapping=2, sequence=4, offset=2)
try:
self._data = self._yaml.load(self._path)
self._data = cast(dict[str, Any], self._yaml.load(self._path))
except FileNotFoundError:
self._data = None

Expand Down Expand Up @@ -106,7 +108,7 @@ def append(self, items: BuildItem | Iterable[BuildItem]) -> list[BuildItem]:
return []

if not self._data:
self._data = self._yaml.map()
self._data = cast(dict[str, Any], self._yaml.map())

if "include" not in self._data:
self._data["include"] = self._yaml.seq()
Expand All @@ -121,7 +123,7 @@ def remove(self, items: BuildItem | Iterable[BuildItem]) -> list[BuildItem]:
:return: the items that were removed.
"""
if not self._data or "include" not in self._data:
return False
return []

removed = []
items = [items] if isinstance(items, BuildItem) else items
Expand All @@ -138,7 +140,25 @@ def remove(self, items: BuildItem | Iterable[BuildItem]) -> list[BuildItem]:
return removed


def _keys_to_python(data: Any):
@overload
def _keys_to_python(data: str) -> str: ...


@overload
def _keys_to_python(
data: Sequence[T],
) -> Sequence[T]: ...


@overload
def _keys_to_python(data: Mapping[str, T]) -> Mapping[str, T]: ...


@overload
def _keys_to_python(data: T) -> T: ...


def _keys_to_python(data: Any) -> Any:
"""
Fix any keys with hyphens to underscores so that dacite.from_dict() will
work correctly.
Expand All @@ -151,10 +171,10 @@ def fix_key(key: str):
case str():
return data

case collections.abc.Sequence():
case Sequence():
return [_keys_to_python(i) for i in data]

case collections.abc.Mapping():
case Mapping():
return {fix_key(k): _keys_to_python(v) for k, v in data.items()}

case _:
Expand Down
4 changes: 2 additions & 2 deletions zmk/commands/cd.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import shellingham
import typer

from ..config import Config
from ..config import get_config
from ..exceptions import FatalError, FatalHomeMissing, FatalHomeNotSet


Expand All @@ -22,7 +22,7 @@ def cd(ctx: typer.Context):
'Use "cd $(zmk config user.home)" instead.'
)

cfg = ctx.find_object(Config)
cfg = get_config(ctx)
home = cfg.home_path

if home is None:
Expand Down
20 changes: 14 additions & 6 deletions zmk/commands/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import typer
from rich.markdown import Markdown

from ..config import Config, Settings
from ..config import Config, Settings, get_config
from ..exceptions import FatalError
from ..menu import show_menu
from ..repo import Repo
Expand All @@ -42,7 +42,7 @@ def code(
):
"""Open the repo or a .keymap or .conf file in a text editor."""

cfg = ctx.find_object(Config)
cfg = get_config(ctx)
repo = cfg.get_repo()

if open_build_matrix:
Expand All @@ -56,7 +56,7 @@ def code(
subprocess.call(cmd, shell=True)


def _get_file(repo: Repo, keyboard: str, open_conf: bool):
def _get_file(repo: Repo, keyboard: Optional[str], open_conf: bool):
if not keyboard:
return repo.path

Expand Down Expand Up @@ -98,7 +98,7 @@ class Editor:
"Executable name or command line to execute this tool"
support: Support = Support.FILE
"Types of files this tool supports editing"
test: Callable[[], bool] = None
test: Callable[[], bool] | None = None
"""
Function that returns true if the tool is installed.
Defaults to `which {self.cmd}`.
Expand Down Expand Up @@ -169,7 +169,11 @@ def _select_editor(cfg: Config):
)

editor = show_menu("Select a text editor:", file_editors, filter_func=_filter)
cfg.set(Settings.CORE_EDITOR, editor.get_command())
editor_command = editor.get_command()
if not editor_command:
raise TypeError(f"Invalid editor {editor.name}")

cfg.set(Settings.CORE_EDITOR, editor_command)

explorer = None
if editor.support & Support.DIR:
Expand All @@ -181,7 +185,11 @@ def _select_editor(cfg: Config):
dir_editors,
filter_func=_filter,
)
cfg.set(Settings.CORE_EXPLORER, explorer.get_command())
explorer_command = explorer.get_command()
if not explorer_command:
raise TypeError(f"Invalid explorer {editor.name}")

cfg.set(Settings.CORE_EXPLORER, explorer_command)

cfg.write()

Expand Down
6 changes: 3 additions & 3 deletions zmk/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from rich.console import Console

from .. import styles
from ..config import Config
from ..config import Config, get_config

console = Console(
highlighter=styles.KeyValueHighlighter(), theme=styles.KEY_VALUE_THEME
Expand All @@ -17,7 +17,7 @@

def _path_callback(ctx: typer.Context, value: bool):
if value:
cfg = ctx.find_object(Config)
cfg = get_config(ctx)
print(cfg.path)
raise typer.Exit()

Expand Down Expand Up @@ -51,7 +51,7 @@ def config(
):
"""Get and set ZMK CLI settings."""

cfg = ctx.find_object(Config)
cfg = get_config(ctx)

if name is None:
_list_settings(cfg)
Expand Down
4 changes: 2 additions & 2 deletions zmk/commands/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

import typer

from ..config import Config
from ..config import get_config
from ..repo import Repo


def download(ctx: typer.Context):
"""Open the web page to download firmware from GitHub."""

cfg = ctx.find_object(Config)
cfg = get_config(ctx)
repo = cfg.get_repo()

actions_url = _get_actions_url(repo)
Expand Down
4 changes: 2 additions & 2 deletions zmk/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from rich.prompt import Confirm, Prompt
from rich.table import Table

from ..config import Config
from ..config import Config, get_config
from ..exceptions import FatalError
from ..prompt import UrlPrompt
from ..repo import Repo, find_containing_repo, is_repo
Expand All @@ -29,7 +29,7 @@ def init(ctx: typer.Context):
"""Create a new ZMK config repo or clone an existing one."""

console = rich.get_console()
cfg = ctx.find_object(Config)
cfg = get_config(ctx)

_check_dependencies()
_check_for_existing_repo(cfg)
Expand Down
Loading

0 comments on commit 4ddfabe

Please sign in to comment.