Skip to content

Commit 45593ee

Browse files
authored
MPT-17825: resolve WPS5XX violations (#153)
πŸ€– **Codex-generated PR** β€” Please review carefully. ## Summary - resolve WPS5XX issues in source code for control-flow and style patterns - update logic in console, errors, handlers, stats, apps, and audit plugin for wemake compliance - remove `WPS504,WPS518,WPS519,WPS529` from flake8 per-file ignores in `pyproject.toml` ## Validation - `make check-all` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> Closes [MPT-17825](https://softwareone.atlassian.net/browse/MPT-17825) ## Release Notes - **cli/core/console.py**: Refactored per-character styling to build Text pieces via indexed iteration and Text.assemble instead of in-place char.stylize. - **cli/core/errors.py**: Consolidated HTTP error handling in `wrap_http_error`; added `_parse_bad_request_message(response)` to extract BAD_REQUEST JSON errors with graceful fallbacks. - **cli/core/handlers/errors.py**: Simplified `ExcelFileHandlerError.__init__` to default message/details cleanly and copy details into a new list when provided. - **cli/core/handlers/excel_file_handler.py**: Inverted `_clean_worksheets` logic to clear the entire cache when no sheet is specified and remove a specific sheet when provided. - **cli/core/nested_dicts.py**: Simplified nested path setting control flow; base-case returns after dict.update and intermediate non-dict values are treated as missing/overwritten. - **cli/core/products/app.py**: Minor refactor of `out_path` assignment to use an explicit None check (behavior unchanged). - **cli/core/products/models/template.py**: Reordered None-check in `to_xlsx` default handling (behavior unchanged). - **cli/core/stats.py**: Made `ErrorMessagesCollector.__str__` robust to missing empty-key sections by using `section.get("", [])` and conditional appending. - **cli/plugins/audit_plugin/app.py**: Inverted conditional used to display Source/Target Value columns so missing values show red "<missing>" consistently. - **pyproject.toml**: Removed per-file flake8 ignores for WPS518, WPS519, and WPS529 to align with wemake-python-styleguide WPS5XX rules. - **Tests**: Added/updated tests covering error wrapping, Excel file handler cache behavior, nested dict overwriting, and ExcelFileHandlerError detail copying. <!-- end of auto-generated comment: release notes by coderabbit.ai --> [MPT-17825]: https://softwareone.atlassian.net/browse/MPT-17825?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2 parents c7d92be + 656e671 commit 45593ee

File tree

15 files changed

+148
-35
lines changed

15 files changed

+148
-35
lines changed

β€Žcli/core/console.pyβ€Ž

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,8 @@ def show_banner() -> None:
4343

4444
for line in banner_lines:
4545
colored_line = Text()
46-
for index in range(len(line)):
47-
char = line[index : index + 1]
48-
char.stylize(colors[index])
49-
colored_line = Text.assemble(colored_line, char)
46+
for index, char in enumerate(line.plain):
47+
colored_line = Text.assemble(colored_line, Text(char, style=colors[index]))
5048
console.print(colored_line)
5149

5250

β€Žcli/core/errors.pyβ€Ž

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import ParamSpec, TypeVar
55

66
from mpt_api_client.exceptions import MPTHttpError as APIException
7-
from requests import RequestException
7+
from requests import RequestException, Response
88

99
CallableParams = ParamSpec("CallableParams")
1010
RetType = TypeVar("RetType")
@@ -25,7 +25,27 @@ def __str__(self) -> str:
2525
return f"{self._request_msg} with response body {self._response_body}"
2626

2727

28-
def wrap_http_error[**CallableParams, RetType]( # noqa: C901
28+
def _parse_bad_request_message(response: Response) -> str:
29+
try:
30+
response_body = response.json()
31+
except ValueError:
32+
return str(response.content)
33+
34+
response_errors = response_body.get("errors", {}) if isinstance(response_body, dict) else {}
35+
if not isinstance(response_errors, dict) or not response_errors:
36+
return str(response.content)
37+
38+
return "\n".join(
39+
(
40+
f"{field}: {error_details[0]}"
41+
if isinstance(error_details, (list, tuple)) and error_details
42+
else f"{field}: {error_details}"
43+
)
44+
for field, error_details in response_errors.items()
45+
)
46+
47+
48+
def wrap_http_error[**CallableParams, RetType](
2949
func: Callable[CallableParams, RetType],
3050
) -> Callable[CallableParams, RetType]:
3151
"""Decorator to wrap HTTP request functions and handle RequestException.
@@ -45,18 +65,13 @@ def _wrapper(*args: CallableParams.args, **kwargs: CallableParams.kwargs) -> Ret
4565
except RequestException as error:
4666
if error.response is None:
4767
msg = "No response"
48-
elif error.response.status_code == HTTPStatus.BAD_REQUEST:
49-
response_body = error.response.json()
50-
51-
msg = ""
52-
if "errors" in response_body:
53-
for field, error_details in response_body["errors"].items():
54-
msg += f"{field}: {error_details[0]}\n"
55-
else:
56-
msg = str(error.response.content)
57-
else:
68+
raise MPTAPIError(str(error), msg) from error
69+
70+
if error.response.status_code != HTTPStatus.BAD_REQUEST:
5871
msg = str(error.response.content)
72+
raise MPTAPIError(str(error), msg) from error
5973

74+
msg = _parse_bad_request_message(error.response)
6075
raise MPTAPIError(str(error), msg) from error
6176

6277
return _wrapper

β€Žcli/core/handlers/errors.pyβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ class ExcelFileHandlerError(Exception):
44
_default_message = "Excel file handler error"
55

66
def __init__(self, message: str | None = None, details: list | None = None):
7-
self.message = message if message is not None else self._default_message
8-
self.details = details if details is not None else []
7+
self.message = self._default_message if message is None else message
8+
self.details = [] if details is None else list(details)
99

1010

1111
class RequiredSheetsError(ExcelFileHandlerError):

β€Žcli/core/handlers/excel_file_handler.pyβ€Ž

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,11 @@ def write_cell(
335335
sheet[coordinate] = cell_value
336336

337337
def _clean_worksheets(self, sheet_name: str | None = None) -> None:
338-
if sheet_name is not None:
339-
self._worksheets_cache.pop(sheet_name, None)
340-
else:
338+
if sheet_name is None:
341339
self._worksheets_cache = {}
340+
return
341+
342+
self._worksheets_cache.pop(sheet_name, None)
342343

343344
def _get_fields_from_horizontal_worksheet(self, worksheet_name: str, max_row: int) -> list[str]:
344345
return list(

β€Žcli/core/nested_dicts.pyβ€Ž

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ def set_dict_value(original_dict: dict[str, Any], path: str, new_value: Any) ->
1515
"""
1616
path_list = path.split(".", 1)
1717
if len(path_list) == 1:
18-
original_dict[path_list[0]] = new_value
18+
original_dict.update({path_list[0]: new_value})
19+
return original_dict
20+
21+
current, next_path = path_list[0], path_list[-1]
22+
if current in original_dict:
23+
current_value = original_dict.get(current)
24+
next_dict = current_value if isinstance(current_value, dict) else {}
1925
else:
20-
current, next_path = path_list[0], path_list[-1]
21-
if current in original_dict:
22-
original_dict[current] = set_dict_value(original_dict[current], next_path, new_value)
23-
else:
24-
original_dict[current] = set_dict_value({}, next_path, new_value)
26+
next_dict = {}
2527

28+
original_dict.update({current: set_dict_value(next_dict, next_path, new_value)})
2629
return original_dict

β€Žcli/core/price_lists/app.pyβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def export( # noqa: C901
141141
)
142142
raise typer.Exit(code=4)
143143

144-
out_path = out_path if out_path is not None else str(Path.cwd())
144+
out_path = str(Path.cwd()) if out_path is None else out_path
145145
mpt_client = create_api_mpt_client_from_account(active_account)
146146
stats = PriceListStatsCollector()
147147
has_error = False

β€Žcli/core/products/app.pyβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def export( # noqa: C901
5151
)
5252
raise typer.Exit(code=4)
5353

54-
out_path = out_path if out_path is not None else str(Path.cwd())
54+
out_path = str(Path.cwd()) if out_path is None else out_path
5555
has_error = False
5656
for product_id in product_ids:
5757
file_path = Path(out_path) / f"{product_id}.xlsx"

β€Žcli/core/products/models/template.pyβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def to_xlsx(self) -> dict[str, Any]:
6969
constants.TEMPLATES_NAME: self.name,
7070
constants.TEMPLATES_ACTION: self.action,
7171
constants.TEMPLATES_TYPE: self.type,
72-
constants.TEMPLATES_DEFAULT: str(self.default) if self.default is not None else None,
72+
constants.TEMPLATES_DEFAULT: None if self.default is None else str(self.default),
7373
constants.TEMPLATES_CONTENT: self.template_content,
7474
constants.TEMPLATES_CREATED: self.created_date,
7575
constants.TEMPLATES_MODIFIED: self.updated_date,

β€Žcli/core/stats.pyβ€Ž

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ def __str__(self) -> str:
7070
msg = ""
7171
for section_name, section in self._sections.items():
7272
msg = f"{msg}{section_name}:"
73-
if "" in section:
74-
msg = f"{msg} {', '.join(section[''])}\n"
73+
section_messages = section.get("", [])
74+
if section_messages:
75+
msg = f"{msg} {', '.join(section_messages)}\n"
7576
else:
7677
msg += "\n"
7778

β€Žcli/plugins/audit_plugin/app.pyβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ def compare_audit_trails(source_trail: dict[str, Any], target_trail: dict[str, A
5858
formatted_path = format_json_path(key, source_trail, target_trail)
5959
table.add_row(
6060
formatted_path,
61-
str(source_value) if source_value is not None else "[red]<missing>[/red]",
62-
str(target_value) if target_value is not None else "[red]<missing>[/red]",
61+
"[red]<missing>[/red]" if source_value is None else str(source_value),
62+
"[red]<missing>[/red]" if target_value is None else str(target_value),
6363
)
6464

6565
if differences_found:

0 commit comments

Comments
Β (0)