Skip to content

Commit 0d742a8

Browse files
authored
Add linting and formatting tools configuration (#194)
* Add linting and formatting tools configuration Added `ruff` and `mypy` configurations in `pyproject.toml` to enable strict linting and static type checking. This includes specifications for excluded directories, line length, complexity limits, inline quotes style, and various mypy checks to ensure code quality and consistency. Additionally, moved `jsonschema` to correct dependency section and enhanced pytest setup with additional options. * Reformat * Update linting tool in build workflow Replace flake8 with ruff in the GitHub Actions build workflow. The new tool improves linting checks and applies fixes automatically where possible. * Exclude proto directory from relevant tools Added 'hyperstyle/src/python/review/inspectors/common/inspector/proto' to the exclusion lists in two different configuration sections to ensure the directory is ignored by relevant tools. This change will help maintain cleaner project management and avoid unnecessary processing of proto files. --------- Co-authored-by: meanmail <[email protected]>
1 parent fdd8825 commit 0d742a8

File tree

107 files changed

+4451
-3766
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+4451
-3766
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,11 @@ jobs:
7171
echo $PMD_DIRECTORY && echo $PMD_VERSION
7272
echo $GOLANG_LINT_DIRECTORY && echo $GOLANG_LINT_VERSION
7373
74-
- name: Lint with flake8
74+
- name: Lint
7575
run: |
7676
cd /review
7777
# stop the build if there are Python syntax errors or undefined names
78-
/hyperstyle/bin/flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,venv,test/resources,.eggs,review.egg-info,.pytest_cache,node_modules,hyperstyle/src/python/review/inspectors/common/inspector/proto
79-
80-
# TODO: change max-complexity into 10 after refactoring
81-
# TODO: remove R504, A003, E800, E402, WPS1, WPS2, WPS3, WPS4, WPS5, WPS6, H601
82-
/hyperstyle/bin/flake8 . --count --max-complexity=11 --max-line-length=120 --max-doc-length=120 --ignore=R504,A003,E800,E402,W503,WPS,H601,N400,I100,I201,I202, --statistics --exclude=.git,__pycache__,docs/source/conf.py,old,build,dist,venv,test/resources,.eggs,review.egg-info,.pytest_cache,node_modules,hyperstyle/src/python/review/inspectors/common/inspector/proto
78+
/hyperstyle/bin/ruff check --unsafe-fixes --preview .
8379
8480
- name: Check installed module can run python linters
8581
run: |

hyperstyle/src/python/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from pathlib import Path
24

35
MAIN_FOLDER = Path(__file__)
Lines changed: 75 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from __future__ import annotations
2+
13
from dataclasses import dataclass
24
from enum import Enum, unique
3-
from typing import List, Optional
5+
from typing import ClassVar
46

57
from hyperstyle.src.python.review.common.language import Language
68
from hyperstyle.src.python.review.common.language_version import LanguageVersion
@@ -9,91 +11,110 @@
911

1012
@unique
1113
class VerbosityLevel(Enum):
12-
"""
13-
Same meaning as the logging level. Should be used in command-line args.
14-
"""
14+
"""Same meaning as the logging level. Should be used in command-line args."""
15+
1516
DEBUG = 3
1617
INFO = 2
1718
ERROR = 1
1819
DISABLE = 0
1920

2021
@classmethod
21-
def values(cls) -> List[int]:
22+
def values(cls) -> list[int]:
2223
return [member.value for member in VerbosityLevel]
2324

2425

2526
@dataclass(frozen=True)
2627
class ArgumentsInfo:
27-
short_name: Optional[str]
28+
short_name: str | None
2829
long_name: str
2930
description: str
3031

3132

3233
@unique
3334
class RunToolArgument(Enum):
34-
VERBOSITY = ArgumentsInfo('-v', '--verbosity',
35-
'Choose logging level: '
36-
f'{VerbosityLevel.ERROR.value} - ERROR; '
37-
f'{VerbosityLevel.INFO.value} - INFO; '
38-
f'{VerbosityLevel.DEBUG.value} - DEBUG; '
39-
f'{VerbosityLevel.DISABLE.value} - disable logging; '
40-
'default is 0')
35+
VERBOSITY = ArgumentsInfo(
36+
"-v",
37+
"--verbosity",
38+
"Choose logging level: "
39+
f"{VerbosityLevel.ERROR.value} - ERROR; "
40+
f"{VerbosityLevel.INFO.value} - INFO; "
41+
f"{VerbosityLevel.DEBUG.value} - DEBUG; "
42+
f"{VerbosityLevel.DISABLE.value} - disable logging; "
43+
"default is 0",
44+
)
4145

42-
inspectors = [inspector.lower() for inspector in InspectorType.available_values()]
43-
disabled_inspectors_example = f'-d {inspectors[0].lower()},{inspectors[1].lower()}'
46+
inspectors: ClassVar = [inspector.lower() for inspector in InspectorType.available_values()]
47+
disabled_inspectors_example = f"-d {inspectors[0].lower()},{inspectors[1].lower()}"
4448

45-
DISABLE = ArgumentsInfo('-d', '--disable',
46-
'Disable inspectors. '
47-
f'Available values: {", ".join(inspectors)}. '
48-
f'Example: {disabled_inspectors_example}')
49+
DISABLE = ArgumentsInfo(
50+
"-d",
51+
"--disable",
52+
'Disable inspectors. '
53+
f'Available values: {", ".join(inspectors)}. '
54+
f'Example: {disabled_inspectors_example}',
55+
)
4956

50-
DUPLICATES = ArgumentsInfo(None, '--allow-duplicates',
51-
'Allow duplicate issues found by different linters. '
52-
'By default, duplicates are skipped.')
57+
DUPLICATES = ArgumentsInfo(
58+
None,
59+
"--allow-duplicates",
60+
"Allow duplicate issues found by different linters. " "By default, duplicates are skipped.",
61+
)
5362

54-
LANG = ArgumentsInfo(None, '--language',
55-
'Specify the language to inspect. The tool will check all languages by default. '
56-
'Available values are: '
57-
f'{", ".join([l.lower() for l in Language.values()])}.')
63+
LANG = ArgumentsInfo(
64+
None,
65+
"--language",
66+
'Specify the language to inspect. The tool will check all languages by default. '
67+
'Available values are: '
68+
f'{", ".join([l.lower() for l in Language.values()])}.',
69+
)
5870

59-
LANG_VERSION = ArgumentsInfo(None, '--language-version',
60-
'Specify the language version for JAVA inspectors. '
61-
'Available values are: '
62-
f'{LanguageVersion.PYTHON_3.value}, {LanguageVersion.JAVA_8.value}, '
63-
f'{LanguageVersion.JAVA_11.value}, {LanguageVersion.KOTLIN.value}.')
71+
LANG_VERSION = ArgumentsInfo(
72+
None,
73+
"--language-version",
74+
"Specify the language version for JAVA inspectors. "
75+
"Available values are: "
76+
f"{LanguageVersion.PYTHON_3.value}, {LanguageVersion.JAVA_8.value}, "
77+
f"{LanguageVersion.JAVA_11.value}, {LanguageVersion.KOTLIN.value}.",
78+
)
6479

65-
CPU = ArgumentsInfo(None, '--n-cpu',
66-
'Specify number of cpu that can be used to run inspectors')
80+
CPU = ArgumentsInfo(None, "--n-cpu", "Specify number of cpu that can be used to run inspectors")
6781

68-
PATH = ArgumentsInfo(None, 'path', 'Path to file or directory to inspect.')
82+
PATH = ArgumentsInfo(None, "path", "Path to file or directory to inspect.")
6983

70-
FORMAT = ArgumentsInfo('-f', '--format',
71-
'The output format. Default is JSON.')
84+
FORMAT = ArgumentsInfo("-f", "--format", "The output format. Default is JSON.")
7285

73-
START_LINE = ArgumentsInfo('-s', '--start-line',
74-
'The first line to be analyzed. It starts from 1.')
86+
START_LINE = ArgumentsInfo("-s", "--start-line", "The first line to be analyzed. It starts from 1.")
7587

76-
END_LINE = ArgumentsInfo('-e', '--end-line', 'The end line to be analyzed or None.')
88+
END_LINE = ArgumentsInfo("-e", "--end-line", "The end line to be analyzed or None.")
7789

78-
NEW_FORMAT = ArgumentsInfo(None, '--new-format',
79-
'The argument determines whether the tool '
80-
'should use the new format')
90+
NEW_FORMAT = ArgumentsInfo(
91+
None, "--new-format", "The argument determines whether the tool " "should use the new format"
92+
)
8193

82-
HISTORY = ArgumentsInfo(None, '--history',
83-
'JSON string, which contains lists of issues in the previous submissions '
84-
'for other tasks for one user.')
94+
HISTORY = ArgumentsInfo(
95+
None,
96+
"--history",
97+
"JSON string, which contains lists of issues in the previous submissions "
98+
"for other tasks for one user.",
99+
)
85100

86-
WITH_ALL_CATEGORIES = ArgumentsInfo(None, '--with-all-categories',
87-
'Without this flag, all issues will be categorized into 5 main categories: '
88-
'CODE_STYLE, BEST_PRACTICES, ERROR_PRONE, COMPLEXITY, INFO.')
101+
WITH_ALL_CATEGORIES = ArgumentsInfo(
102+
None,
103+
"--with-all-categories",
104+
"Without this flag, all issues will be categorized into 5 main categories: "
105+
"CODE_STYLE, BEST_PRACTICES, ERROR_PRONE, COMPLEXITY, INFO.",
106+
)
89107

90-
GROUP_BY_DIFFICULTY = ArgumentsInfo(None, '--group-by-difficulty',
91-
'With this flag, the final grade will be grouped by the issue difficulty.')
108+
GROUP_BY_DIFFICULTY = ArgumentsInfo(
109+
None,
110+
"--group-by-difficulty",
111+
"With this flag, the final grade will be grouped by the issue difficulty.",
112+
)
92113

93114
IJ_CONFIG = ArgumentsInfo(
94115
None,
95-
'--ij-config',
96-
'JSON string containing information for setting up a connection to the IJ server '
97-
'for each language to be analyzed with the IJ inspector. '
98-
'It should be a dictionary of dictionaries where for each language host and port are specified.',
116+
"--ij-config",
117+
"JSON string containing information for setting up a connection to the IJ server "
118+
"for each language to be analyzed with the IJ inspector. "
119+
"It should be a dictionary of dictionaries where for each language host and port are specified.",
99120
)

hyperstyle/src/python/review/application_config.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
1+
from __future__ import annotations
2+
13
from dataclasses import dataclass
2-
from typing import Optional, Set
4+
from typing import TYPE_CHECKING
35

4-
from hyperstyle.src.python.review.common.language import Language
5-
from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
6+
if TYPE_CHECKING:
7+
from hyperstyle.src.python.review.common.language import Language
8+
from hyperstyle.src.python.review.inspectors.common.inspector.inspector_type import InspectorType
69

710

811
@dataclass
912
class ApplicationConfig:
10-
disabled_inspectors: Set[InspectorType]
13+
disabled_inspectors: set[InspectorType]
1114
allow_duplicates: bool
1215
n_cpu: int
1316
inspectors_config: dict
1417
with_all_categories: bool
1518
start_line: int = 1
16-
language: Optional[Language] = None
17-
end_line: Optional[int] = None
19+
language: Language | None = None
20+
end_line: int | None = None
1821
new_format: bool = False
19-
history: Optional[str] = None
22+
history: str | None = None
2023
group_by_difficulty: bool = False
21-
ij_config: Optional[str] = None
24+
ij_config: str | None = None
2225

2326
@staticmethod
24-
def get_default_config() -> 'ApplicationConfig':
27+
def get_default_config() -> ApplicationConfig:
2528
return ApplicationConfig(
2629
disabled_inspectors=set(),
2730
allow_duplicates=False,
2831
n_cpu=1,
29-
inspectors_config={'n_cpu': 1},
32+
inspectors_config={"n_cpu": 1},
3033
with_all_categories=True,
3134
start_line=1,
3235
language=None,

hyperstyle/src/python/review/common/file_system.py

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
from __future__ import annotations
2+
13
import linecache
24
import logging
35
import os
46
import tempfile
7+
from collections.abc import Callable
58
from contextlib import contextmanager
69
from enum import Enum, unique
710
from pathlib import Path
8-
from typing import Callable, List, Optional, Tuple, Union
911

1012
logger = logging.getLogger(__name__)
1113

@@ -19,32 +21,32 @@ class FileSystemItem(Enum):
1921

2022
@unique
2123
class Encoding(Enum):
22-
ISO_ENCODING = 'ISO-8859-1'
23-
UTF_ENCODING = 'utf-8'
24+
ISO_ENCODING = "ISO-8859-1"
25+
UTF_ENCODING = "utf-8"
2426

2527

2628
# Make sure all extensions (except an empty one) have a dot
2729
@unique
2830
class Extension(Enum):
29-
EMPTY = ''
30-
PY = '.py'
31-
JAVA = '.java'
32-
KT = '.kt'
33-
JS = '.js'
34-
KTS = '.kts'
35-
GO = '.go'
31+
EMPTY = ""
32+
PY = ".py"
33+
JAVA = ".java"
34+
KT = ".kt"
35+
JS = ".js"
36+
KTS = ".kts"
37+
GO = ".go"
3638

3739
# Not empty extensions are returned with a dot, for example, '.txt'
3840
# If file has no extensions, an empty one ('') is returned
3941
@classmethod
40-
def from_file(cls, file: Union[Path, str]) -> Optional['Extension']:
42+
def from_file(cls, file: Path | str) -> Extension | None:
4143
try:
4244
return Extension(get_extension_from_file(file))
4345
except ValueError:
4446
return None
4547

4648

47-
def get_extension_from_file(file: Union[Path, str]) -> str:
49+
def get_extension_from_file(file: Path | str) -> str:
4850
return os.path.splitext(file)[1]
4951

5052

@@ -58,19 +60,20 @@ def all_items_condition(name: str) -> bool:
5860
# To get all files or subdirs (depends on the last parameter) from root that match item_condition
5961
# Note that all subdirs or files already contain the full path for them
6062
def get_all_file_system_items(
61-
root: Path,
62-
item_condition: ItemCondition = all_items_condition,
63-
item_type: FileSystemItem = FileSystemItem.FILE,
64-
without_subdirs: bool = False,
65-
) -> List[Path]:
63+
root: Path,
64+
item_condition: ItemCondition = all_items_condition,
65+
item_type: FileSystemItem = FileSystemItem.FILE,
66+
without_subdirs: bool = False,
67+
) -> list[Path]:
6668
if not root.is_dir():
67-
raise ValueError(f'The {root} is not a directory')
69+
msg = f"The {root} is not a directory"
70+
raise ValueError(msg)
6871

6972
items = []
7073
for fs_tuple in os.walk(root):
7174
for item in fs_tuple[item_type.value]:
7275
if item_condition(item):
73-
items.append(Path(os.path.join(fs_tuple[FileSystemItem.PATH.value], item)))
76+
items.append(Path(fs_tuple[FileSystemItem.PATH.value]) / item)
7477

7578
if without_subdirs:
7679
break
@@ -85,7 +88,7 @@ def new_temp_dir() -> Path:
8588
yield Path(temp_dir)
8689

8790

88-
def new_temp_file(suffix: Extension = Extension.EMPTY) -> Tuple[str, str]:
91+
def new_temp_file(suffix: Extension = Extension.EMPTY) -> tuple[str, str]:
8992
yield tempfile.mkstemp(suffix=suffix.value)
9093

9194

@@ -96,11 +99,11 @@ def get_file_line(path: Path, line_number: int):
9699
).strip()
97100

98101

99-
def get_content_from_file(file_path: Path, encoding: str = Encoding.ISO_ENCODING.value,
100-
to_strip_nl: bool = True) -> str:
101-
with open(file_path, 'r', encoding=encoding) as f:
102-
content = f.read()
103-
return content if not to_strip_nl else content.rstrip('\n')
102+
def get_content_from_file(
103+
file_path: Path, encoding: str = Encoding.ISO_ENCODING.value, to_strip_nl: bool = True
104+
) -> str:
105+
content = Path(file_path).read_text(encoding=encoding)
106+
return content if not to_strip_nl else content.rstrip("\n")
104107

105108

106109
# Before using it, check that there are no line breaks in the string
@@ -109,7 +112,7 @@ def __is_line_empty(line: str) -> bool:
109112

110113

111114
def __is_comment(line: str) -> bool:
112-
return line.strip().startswith(('#', '//'))
115+
return line.strip().startswith(("#", "//"))
113116

114117

115118
def get_total_code_lines_from_file(path: Path) -> int:
@@ -124,6 +127,6 @@ def get_total_code_lines_from_code(code: str) -> int:
124127

125128
def check_set_up_env_variable(variable_name: str) -> bool:
126129
if variable_name not in os.environ:
127-
logger.warning(f'{variable_name} was not set up!')
130+
logger.warning(f"{variable_name} was not set up!")
128131
return False
129132
return True

0 commit comments

Comments
 (0)