Skip to content

Commit

Permalink
Add import linter task (#227)
Browse files Browse the repository at this point in the history
---------
Co-authored-by: Nicola Coretti <[email protected]>
  • Loading branch information
Jannis-Mittenzwei authored Nov 15, 2024
1 parent 3d10c86 commit 70456d8
Show file tree
Hide file tree
Showing 14 changed files with 264 additions and 58 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
poetry.lock linguist-generated=true
2 changes: 2 additions & 0 deletions .import_linter_config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[importlinter]
root_package = exasol.toolbox
1 change: 1 addition & 0 deletions doc/changes/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Fixed the issue with publishing new documentation after releasing a new version
## ✨ Added

* #149: Added nox task to lint imports
* Added support to manually trigger documentation build
* #248: Added security results to workflow summary
* #233: Added nox task to verify dependency declarations
1 change: 1 addition & 0 deletions doc/developer_guide/modules/modules.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ Modules

sphinx/sphinx
nox
nox_tasks
pre_commit_hooks

1 change: 1 addition & 0 deletions doc/developer_guide/modules/nox.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
nox
===

The nox package contains nox related functionalities like pre defined nox tasks.

.. figure:: ../../_static/nothing-to-see-here.png
Expand Down
15 changes: 15 additions & 0 deletions doc/developer_guide/modules/nox_tasks.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
nox_tasks
=========

lint:import (experimental)
__________________________

`Import Linter <https://import-linter.readthedocs.io/en/stable/readme.html>`_
allows you to define and enforce rules for the imports within and between Python packages.

.. important::

First configure the linter in file :code:`.import_linter_config`, see
`import-linter top-level-configuration <https://import-linter.readthedocs.io/en/stable/usage.html#top-level-configuration>`_
and `import-linter contract types <https://import-linter.readthedocs.io/en/stable/contract_types.html>`_

1 change: 1 addition & 0 deletions doc/developer_guide/modules/pre_commit_hooks.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pre_commit_hooks
=================

In the pre_commit_hook package contains git commit hooks and similar functionalities.

.. figure:: ../../_static/nothing-to-see-here.png
Expand Down
68 changes: 50 additions & 18 deletions exasol/toolbox/nox/_lint.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
from __future__ import annotations

import argparse
import sys
from pathlib import Path
from typing import (
Dict,
Iterable,
List,
Dict
)

import nox
import rich.console
import tomlkit
from nox import Session

from exasol.toolbox.nox._shared import python_files
from noxconfig import PROJECT_CONFIG

from pathlib import Path
import rich.console
import tomlkit
import sys


def _pylint(session: Session, files: Iterable[str]) -> None:
session.run(
Expand Down Expand Up @@ -74,27 +74,28 @@ def _security_lint(session: Session, files: Iterable[str]) -> None:
)


def _import_lint(session: Session, path: Path) -> None:
session.run("poetry", "run", "lint-imports", "--config", path)


class Dependencies:
def __init__(self, illegal: Dict[str, List[str]] | None):
def __init__(self, illegal: dict[str, list[str]] | None):
self._illegal = illegal or {}

@staticmethod
def parse(pyproject_toml: str) -> "Dependencies":
def parse(pyproject_toml: str) -> Dependencies:
def _source_filter(version) -> bool:
ILLEGAL_SPECIFIERS = ['url', 'git', 'path']
return any(
specifier in version
for specifier in ILLEGAL_SPECIFIERS
)
ILLEGAL_SPECIFIERS = ["url", "git", "path"]
return any(specifier in version for specifier in ILLEGAL_SPECIFIERS)

def find_illegal(part) -> List[str]:
def find_illegal(part) -> list[str]:
return [
f"{name} = {version}"
for name, version in part.items()
if _source_filter(version)
]

illegal: Dict[str, List[str]] = {}
illegal: dict[str, list[str]] = {}
toml = tomlkit.loads(pyproject_toml)
poetry = toml.get("tool", {}).get("poetry", {})

Expand All @@ -114,11 +115,11 @@ def find_illegal(part) -> List[str]:
return Dependencies(illegal)

@property
def illegal(self) -> Dict[str, List[str]]:
def illegal(self) -> dict[str, list[str]]:
return self._illegal


def report_illegal(illegal: Dict[str, List[str]], console: rich.console.Console):
def report_illegal(illegal: dict[str, list[str]], console: rich.console.Console):
count = sum(len(deps) for deps in illegal.values())
suffix = "y" if count == 1 else "ies"
console.print(f"{count} illegal dependenc{suffix}\n", style="red")
Expand Down Expand Up @@ -158,4 +159,35 @@ def dependency_check(session: Session) -> None:
console = rich.console.Console()
if illegal := dependencies.illegal:
report_illegal(illegal, console)
sys.exit(1)
sys.exit(1)


@nox.session(name="lint:import", python=False)
def import_lint(session: Session) -> None:
"""(experimental) Runs import linter on the project"""
parser = argparse.ArgumentParser(
usage="nox -s import-lint -- [options]",
description="Runs the import linter on the project",
)
parser.add_argument(
"-c",
"--config",
type=str,
help="path to the configuration file for the importlinter",
metavar="TEXT",
)

args: argparse.Namespace = parser.parse_args(args=session.posargs)
file: str = args.config
path: Path | None = None
if file is None:
path = getattr(
PROJECT_CONFIG, "import_linter_config", Path(".import_linter_config")
)
else:
path = Path(file)
if not path.exists():
session.error(
"Please make sure you have a configuration file for the importlinter"
)
_import_lint(session=session, path=path)
23 changes: 13 additions & 10 deletions exasol/toolbox/tools/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
from enum import Enum
from functools import partial
from inspect import cleandoc
from pathlib import Path
from typing import (
Generator,
Iterable,
Tuple,
)

import typer
from pathlib import Path

stdout = print
stderr = partial(print, file=sys.stderr)
Expand Down Expand Up @@ -128,14 +129,16 @@ def from_json(report_str: str, prefix: Path) -> Iterable[SecurityIssue]:
cwe=str(issue["issue_cwe"].get("id", "")),
test_id=issue["test_id"],
description=issue["issue_text"],
references=tuple(references)
references=tuple(references),
)


def issues_to_markdown(issues: Iterable[SecurityIssue]) -> str:
template = cleandoc("""
template = cleandoc(
"""
{header}{rows}
""")
"""
)

def _header():
header = "# Security\n\n"
Expand All @@ -153,10 +156,7 @@ def _row(issue):
row = row[:-5] + "|"
return row

return template.format(
header=_header(),
rows="\n".join(_row(i) for i in issues)
)
return template.format(header=_header(), rows="\n".join(_row(i) for i in issues))


def security_issue_title(issue: Issue) -> str:
Expand Down Expand Up @@ -217,6 +217,7 @@ def create_security_issue(issue: Issue, project="") -> Tuple[str, str]:
CVE_CLI = typer.Typer()
CLI.add_typer(CVE_CLI, name="cve", help="Work with CVE's")


class Format(str, Enum):
Maven = "maven"

Expand Down Expand Up @@ -320,8 +321,10 @@ class PPrintFormats(str, Enum):

@CLI.command(name="pretty-print")
def json_issue_to_markdown(
json_file: typer.FileText = typer.Argument(mode="r", help="json file with issues to convert"),
path: Path = typer.Argument(default=Path("."), help="path to project root")
json_file: typer.FileText = typer.Argument(
mode="r", help="json file with issues to convert"
),
path: Path = typer.Argument(default=Path("."), help="path to project root"),
) -> None:
content = json_file.read()
issues = from_json(content, path.absolute())
Expand Down
1 change: 1 addition & 0 deletions noxconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Config:

root: Path = Path(__file__).parent
doc: Path = Path(__file__).parent / "doc"
importlinter: Path = Path(__file__).parent / ".import_linter_config"
version_file: Path = Path(__file__).parent / "exasol" / "toolbox" / "version.py"
path_filters: Iterable[str] = (
"dist",
Expand Down
Loading

0 comments on commit 70456d8

Please sign in to comment.