Skip to content
This repository was archived by the owner on Nov 24, 2023. It is now read-only.

Commit 33de710

Browse files
committed
Clean up structure of the script, add dev configuration
1 parent 774e602 commit 33de710

File tree

9 files changed

+221
-96
lines changed

9 files changed

+221
-96
lines changed

.vscode/settings.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"[jsonc]": {
3+
"editor.formatOnSave": false,
4+
},
5+
"[python]": {
6+
"editor.codeActionsOnSave": {
7+
"source.organizeImports": true
8+
},
9+
"editor.defaultFormatter": "ms-python.black-formatter",
10+
"editor.formatOnSave": true,
11+
},
12+
"files.eol": "\n",
13+
"files.trimTrailingWhitespace": true,
14+
"files.insertFinalNewline": true,
15+
"files.trimFinalNewlines": true,
16+
17+
"python.analysis.typeCheckingMode": "basic",
18+
"python.languageServer": "Pylance",
19+
20+
"python.linting.mypyEnabled": true,
21+
"python.linting.pylintEnabled": true,
22+
23+
"black-formatter.args": [ "--config", "pyproject.toml" ],
24+
"isort.args": [ "--settings-file", "pyproject.toml" ],
25+
"pylint.args": [ "--rcfile", "pyproject.toml" ],
26+
"python.linting.mypyArgs": [ "--config-file", "pyproject.toml" ],
27+
28+
"python.testing.unittestEnabled": false,
29+
"python.testing.pytestEnabled": false,
30+
}

.vscode/tasks.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "Clean build artifacts",
6+
"type": "shell",
7+
"command": [
8+
"rm -r dist/*;",
9+
"rm -r *.egg-info;"
10+
],
11+
"hide": true,
12+
"presentation": {
13+
"echo": false,
14+
"panel": "dedicated",
15+
"focus": false,
16+
"clear": true,
17+
"reveal": "never"
18+
}
19+
},
20+
{
21+
"label": "Build package",
22+
"type": "shell",
23+
"command": [
24+
"python -m build;"
25+
],
26+
"group": "build",
27+
"dependsOn": [
28+
"Clean build artifacts"
29+
],
30+
"presentation": {
31+
"echo": false,
32+
"panel": "dedicated",
33+
"showReuseMessage": false,
34+
"clear": true,
35+
"reveal": "silent"
36+
}
37+
},
38+
{
39+
"label": "Publish to TestPyPI",
40+
"type": "shell",
41+
"command": [
42+
"python -m twine upload --repository testpypi dist/*;"
43+
],
44+
"presentation": {
45+
"echo": false,
46+
"panel": "dedicated",
47+
"showReuseMessage": false,
48+
"clear": true,
49+
"reveal": "silent"
50+
}
51+
}
52+
]
53+
}
Lines changed: 3 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,5 @@
1-
# Inspired by https://github.com/soul-catcher/mypy-gitlab-code-quality
1+
from .main import main
22

3-
from __future__ import annotations
43

5-
import json
6-
import re
7-
from base64 import b64encode
8-
from collections.abc import Hashable, Sequence
9-
from sys import byteorder, hash_info, stdin
10-
from typing import TextIO
11-
12-
13-
def get_hash(tpl: Sequence[Hashable]) -> str:
14-
return b64encode(
15-
hash(tpl).to_bytes(hash_info.width // 8, byteorder, signed=True)
16-
).decode()
17-
18-
19-
def append_line_to_issues(
20-
issues: list[dict],
21-
fingerprint: str,
22-
severity: str,
23-
lineNumber: int,
24-
description: str,
25-
path: str,
26-
check_name: str,
27-
) -> None:
28-
issues.append(
29-
{
30-
"type": "issue",
31-
"check_name": check_name,
32-
"description": description,
33-
"categories": [
34-
"Style",
35-
],
36-
"severity": severity,
37-
"location": {
38-
"path": path,
39-
"lines": {
40-
"begin": lineNumber,
41-
},
42-
},
43-
"fingerprint": fingerprint,
44-
}
45-
)
46-
47-
48-
def parse_lines(lines: TextIO) -> list[dict]:
49-
issues: list[dict] = []
50-
while True:
51-
try:
52-
brief_line: str = next(lines)
53-
details_line: str = next(lines)
54-
except StopIteration:
55-
break
56-
brief_line = brief_line.rstrip("\n")
57-
details_line = details_line.rstrip("\n")
58-
59-
match_brief = re.fullmatch(
60-
r"^(?P<path>.+?)" r":(?P<line>\d+)" r"\s(?P<brief>.*)$",
61-
brief_line,
62-
)
63-
if match_brief is None:
64-
continue
65-
66-
match_details = re.fullmatch(
67-
r"^\s*(?P<check_name>\w{4,5})" r":\s(?P<details>.*)$",
68-
details_line,
69-
)
70-
if match_details is None:
71-
continue
72-
73-
severity: str = "info"
74-
path: str = match_brief["path"]
75-
lineNumber: int = int(match_brief["line"])
76-
brief: str = match_brief["brief"]
77-
check_name: str = match_details["check_name"]
78-
details: str = match_details["details"]
79-
description: str = brief + " " + details
80-
fingerprint: str = get_hash(match_brief.groups() + match_details.groups())
81-
82-
append_line_to_issues(
83-
issues, fingerprint, severity, lineNumber, details, path, check_name
84-
)
85-
return issues
86-
87-
88-
def main() -> None:
89-
print(
90-
json.dumps(
91-
parse_lines(stdin),
92-
indent="\t",
93-
)
94-
)
4+
if __name__ == "__main__":
5+
main()
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import json
2+
import re
3+
from sys import stdin
4+
from typing import Generator, TextIO
5+
6+
from .src.cq_types import Issue, LinesStructure, LocationStructure
7+
from .src.encoder import DataclassJSONEncoder
8+
from .src.hash import get_hash
9+
10+
11+
def get_pydocstyle_output(output: TextIO) -> Generator[dict, None, None]:
12+
path_regex: str = r"^(?P<path>.+?)"
13+
line_regex: str = r":(?P<line>\d+)"
14+
brief_regex: str = r"\s(?P<brief>.*)$"
15+
error_code_regex: str = r"^\s*(?P<error_code>\w{4,5})"
16+
details_regex: str = r":\s(?P<details>.*)$"
17+
18+
while True:
19+
try:
20+
brief_line: str = next(output)
21+
details_line: str = next(output)
22+
except StopIteration:
23+
return
24+
25+
brief_line = brief_line.rstrip("\n")
26+
details_line = details_line.rstrip("\n")
27+
28+
match_brief = re.fullmatch(path_regex + line_regex + brief_regex, brief_line)
29+
if match_brief is None:
30+
continue
31+
32+
match_details = re.fullmatch(error_code_regex + details_regex, details_line)
33+
if match_details is None:
34+
continue
35+
36+
errors = match_brief.groupdict()
37+
errors.update(match_details.groupdict())
38+
yield errors
39+
40+
41+
def get_code_quality_issues() -> Generator:
42+
output = get_pydocstyle_output(stdin)
43+
44+
for entry in output:
45+
yield Issue(
46+
type="issue",
47+
check_name=entry["error_code"],
48+
description=entry["details"],
49+
categories=["Style"],
50+
severity="info",
51+
location=LocationStructure(
52+
path=entry["path"],
53+
lines=LinesStructure(begin=int(entry["line"])),
54+
),
55+
fingerprint=get_hash(tuple(entry.values())),
56+
)
57+
58+
59+
def main() -> None:
60+
issues: list = list(get_code_quality_issues())
61+
json_output: str = json.dumps(issues, indent="\t", cls=DataclassJSONEncoder)
62+
print(json_output)

pydocstyle_gitlab_code_quality/src/__init__.py

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass
5+
class LinesStructure:
6+
begin: int
7+
8+
9+
@dataclass
10+
class LocationStructure:
11+
path: str
12+
lines: LinesStructure
13+
14+
15+
@dataclass
16+
class Issue:
17+
type: str
18+
check_name: str
19+
description: str
20+
categories: list
21+
severity: str
22+
location: LocationStructure
23+
fingerprint: str
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from dataclasses import asdict, is_dataclass
2+
from json import JSONEncoder
3+
from typing import Any
4+
5+
6+
class DataclassJSONEncoder(JSONEncoder):
7+
def default(self, o: Any) -> Any:
8+
if is_dataclass(o):
9+
return asdict(o)
10+
return super().default(o)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from __future__ import annotations
2+
3+
from base64 import b64encode
4+
from collections.abc import Hashable, Sequence
5+
from sys import byteorder, hash_info
6+
7+
8+
def get_hash(tpl: Sequence[Hashable]) -> str:
9+
return b64encode(hash(tpl).to_bytes(hash_info.width // 8, byteorder, signed=True)).decode()

pyproject.toml

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ build-backend = "setuptools.build_meta"
66
name = "pydocstyle-gitlab-code-quality"
77
version = "0.0.1"
88
authors = [
9-
{ name = "Aleksander Kluczka", email = "alagaesia.vrael@gmail.com" },
9+
{ name = "Aleksander Kluczka", email = "aleksander.kluczka@gmail.com" },
1010
]
1111
description = "Simple script to generate gitlab code quality report from output of pydocstyle."
1212
readme = "README.md"
1313
requires-python = ">=3.7"
14-
license = { file = "LICENSE.md" }
14+
license = { text = "MIT" }
1515
classifiers = [
1616
"Development Status :: 3 - Alpha",
17+
"Environment :: Console",
1718
"License :: OSI Approved :: MIT License",
1819
"Operating System :: OS Independent",
1920
"Programming Language :: Python :: 3",
@@ -28,5 +29,31 @@ classifiers = [
2829
"Homepage" = "https://github.com/vis4rd/pydocstyle-gitlab-code-quality"
2930
"Bug Tracker" = "https://github.com/vis4rd/pydocstyle-gitlab-code-quality/issues"
3031

31-
[project.entry-points."pydocstyle_gitlab_code_quality"]
32-
cli = "pydocstyle_gitlab_code_quality:main"
32+
[project.scripts]
33+
pydocstyle-gitlab-code-quality = "pydocstyle_gitlab_code_quality:main"
34+
35+
[tool.black]
36+
line-length = 100
37+
38+
[tool.isort]
39+
profile = "black"
40+
skip = "__init__.py"
41+
42+
[tool.mypy]
43+
ignore_missing_imports = true
44+
show_column_numbers = true
45+
pretty = true
46+
disallow_untyped_defs = true
47+
48+
[tool.pylint]
49+
max-line-length = 100
50+
disable = [
51+
"C0103", # not snake_case naming style
52+
"C0114", # missing module docstring
53+
"C0115", # missing class docstring
54+
"C0116", # missing method docstring
55+
"R0902", # too many instance attributes
56+
"R0903", # too few public methods
57+
"W0102" # dangerous default value of an argument
58+
]
59+
ignore = ["__init__.py"]

0 commit comments

Comments
 (0)