Skip to content

Commit 70456d8

Browse files
Add import linter task (#227)
--------- Co-authored-by: Nicola Coretti <nicola.coretti@exasol.com>
1 parent 3d10c86 commit 70456d8

File tree

14 files changed

+264
-58
lines changed

14 files changed

+264
-58
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
poetry.lock linguist-generated=true

.import_linter_config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[importlinter]
2+
root_package = exasol.toolbox

doc/changes/unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Fixed the issue with publishing new documentation after releasing a new version
66
## ✨ Added
77

8+
* #149: Added nox task to lint imports
89
* Added support to manually trigger documentation build
910
* #248: Added security results to workflow summary
1011
* #233: Added nox task to verify dependency declarations

doc/developer_guide/modules/modules.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ Modules
66

77
sphinx/sphinx
88
nox
9+
nox_tasks
910
pre_commit_hooks
1011

doc/developer_guide/modules/nox.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
nox
22
===
3+
34
The nox package contains nox related functionalities like pre defined nox tasks.
45

56
.. figure:: ../../_static/nothing-to-see-here.png
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
nox_tasks
2+
=========
3+
4+
lint:import (experimental)
5+
__________________________
6+
7+
`Import Linter <https://import-linter.readthedocs.io/en/stable/readme.html>`_
8+
allows you to define and enforce rules for the imports within and between Python packages.
9+
10+
.. important::
11+
12+
First configure the linter in file :code:`.import_linter_config`, see
13+
`import-linter top-level-configuration <https://import-linter.readthedocs.io/en/stable/usage.html#top-level-configuration>`_
14+
and `import-linter contract types <https://import-linter.readthedocs.io/en/stable/contract_types.html>`_
15+

doc/developer_guide/modules/pre_commit_hooks.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pre_commit_hooks
22
=================
3+
34
In the pre_commit_hook package contains git commit hooks and similar functionalities.
45

56
.. figure:: ../../_static/nothing-to-see-here.png

exasol/toolbox/nox/_lint.py

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
from __future__ import annotations
22

3+
import argparse
4+
import sys
5+
from pathlib import Path
36
from typing import (
7+
Dict,
48
Iterable,
59
List,
6-
Dict
710
)
811

912
import nox
13+
import rich.console
14+
import tomlkit
1015
from nox import Session
1116

1217
from exasol.toolbox.nox._shared import python_files
1318
from noxconfig import PROJECT_CONFIG
1419

15-
from pathlib import Path
16-
import rich.console
17-
import tomlkit
18-
import sys
19-
2020

2121
def _pylint(session: Session, files: Iterable[str]) -> None:
2222
session.run(
@@ -74,27 +74,28 @@ def _security_lint(session: Session, files: Iterable[str]) -> None:
7474
)
7575

7676

77+
def _import_lint(session: Session, path: Path) -> None:
78+
session.run("poetry", "run", "lint-imports", "--config", path)
79+
80+
7781
class Dependencies:
78-
def __init__(self, illegal: Dict[str, List[str]] | None):
82+
def __init__(self, illegal: dict[str, list[str]] | None):
7983
self._illegal = illegal or {}
8084

8185
@staticmethod
82-
def parse(pyproject_toml: str) -> "Dependencies":
86+
def parse(pyproject_toml: str) -> Dependencies:
8387
def _source_filter(version) -> bool:
84-
ILLEGAL_SPECIFIERS = ['url', 'git', 'path']
85-
return any(
86-
specifier in version
87-
for specifier in ILLEGAL_SPECIFIERS
88-
)
88+
ILLEGAL_SPECIFIERS = ["url", "git", "path"]
89+
return any(specifier in version for specifier in ILLEGAL_SPECIFIERS)
8990

90-
def find_illegal(part) -> List[str]:
91+
def find_illegal(part) -> list[str]:
9192
return [
9293
f"{name} = {version}"
9394
for name, version in part.items()
9495
if _source_filter(version)
9596
]
9697

97-
illegal: Dict[str, List[str]] = {}
98+
illegal: dict[str, list[str]] = {}
9899
toml = tomlkit.loads(pyproject_toml)
99100
poetry = toml.get("tool", {}).get("poetry", {})
100101

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

116117
@property
117-
def illegal(self) -> Dict[str, List[str]]:
118+
def illegal(self) -> dict[str, list[str]]:
118119
return self._illegal
119120

120121

121-
def report_illegal(illegal: Dict[str, List[str]], console: rich.console.Console):
122+
def report_illegal(illegal: dict[str, list[str]], console: rich.console.Console):
122123
count = sum(len(deps) for deps in illegal.values())
123124
suffix = "y" if count == 1 else "ies"
124125
console.print(f"{count} illegal dependenc{suffix}\n", style="red")
@@ -158,4 +159,35 @@ def dependency_check(session: Session) -> None:
158159
console = rich.console.Console()
159160
if illegal := dependencies.illegal:
160161
report_illegal(illegal, console)
161-
sys.exit(1)
162+
sys.exit(1)
163+
164+
165+
@nox.session(name="lint:import", python=False)
166+
def import_lint(session: Session) -> None:
167+
"""(experimental) Runs import linter on the project"""
168+
parser = argparse.ArgumentParser(
169+
usage="nox -s import-lint -- [options]",
170+
description="Runs the import linter on the project",
171+
)
172+
parser.add_argument(
173+
"-c",
174+
"--config",
175+
type=str,
176+
help="path to the configuration file for the importlinter",
177+
metavar="TEXT",
178+
)
179+
180+
args: argparse.Namespace = parser.parse_args(args=session.posargs)
181+
file: str = args.config
182+
path: Path | None = None
183+
if file is None:
184+
path = getattr(
185+
PROJECT_CONFIG, "import_linter_config", Path(".import_linter_config")
186+
)
187+
else:
188+
path = Path(file)
189+
if not path.exists():
190+
session.error(
191+
"Please make sure you have a configuration file for the importlinter"
192+
)
193+
_import_lint(session=session, path=path)

exasol/toolbox/tools/security.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
from enum import Enum
1212
from functools import partial
1313
from inspect import cleandoc
14+
from pathlib import Path
1415
from typing import (
1516
Generator,
1617
Iterable,
1718
Tuple,
1819
)
20+
1921
import typer
20-
from pathlib import Path
2122

2223
stdout = print
2324
stderr = partial(print, file=sys.stderr)
@@ -128,14 +129,16 @@ def from_json(report_str: str, prefix: Path) -> Iterable[SecurityIssue]:
128129
cwe=str(issue["issue_cwe"].get("id", "")),
129130
test_id=issue["test_id"],
130131
description=issue["issue_text"],
131-
references=tuple(references)
132+
references=tuple(references),
132133
)
133134

134135

135136
def issues_to_markdown(issues: Iterable[SecurityIssue]) -> str:
136-
template = cleandoc("""
137+
template = cleandoc(
138+
"""
137139
{header}{rows}
138-
""")
140+
"""
141+
)
139142

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

156-
return template.format(
157-
header=_header(),
158-
rows="\n".join(_row(i) for i in issues)
159-
)
159+
return template.format(header=_header(), rows="\n".join(_row(i) for i in issues))
160160

161161

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

220+
220221
class Format(str, Enum):
221222
Maven = "maven"
222223

@@ -320,8 +321,10 @@ class PPrintFormats(str, Enum):
320321

321322
@CLI.command(name="pretty-print")
322323
def json_issue_to_markdown(
323-
json_file: typer.FileText = typer.Argument(mode="r", help="json file with issues to convert"),
324-
path: Path = typer.Argument(default=Path("."), help="path to project root")
324+
json_file: typer.FileText = typer.Argument(
325+
mode="r", help="json file with issues to convert"
326+
),
327+
path: Path = typer.Argument(default=Path("."), help="path to project root"),
325328
) -> None:
326329
content = json_file.read()
327330
issues = from_json(content, path.absolute())

noxconfig.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Config:
3535

3636
root: Path = Path(__file__).parent
3737
doc: Path = Path(__file__).parent / "doc"
38+
importlinter: Path = Path(__file__).parent / ".import_linter_config"
3839
version_file: Path = Path(__file__).parent / "exasol" / "toolbox" / "version.py"
3940
path_filters: Iterable[str] = (
4041
"dist",

0 commit comments

Comments
 (0)