Skip to content

Commit 037d1bb

Browse files
authored
[DPE-5324] Increase linting rules (#649)
* Increase linting rules * Missed import * Fix list * Use generator * Bump linter version * Await the generator
1 parent 3c7c783 commit 037d1bb

38 files changed

+593
-574
lines changed

pyproject.toml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,13 @@ target-version = ["py38"]
100100
[tool.ruff]
101101
# preview and explicit preview are enabled for CPY001
102102
preview = true
103-
target-version = "py38"
103+
target-version = "py310"
104104
src = ["src", "."]
105105
line-length = 99
106106

107107
[tool.ruff.lint]
108108
explicit-preview-rules = true
109-
select = ["A", "E", "W", "F", "C", "N", "D", "I001", "CPY001"]
109+
select = ["A", "E", "W", "F", "C", "N", "D", "I001", "B", "CPY", "RUF", "S", "SIM", "UP", "TCH"]
110110
extend-ignore = [
111111
"D203",
112112
"D204",
@@ -125,12 +125,19 @@ extend-ignore = [
125125
ignore = ["E501", "D107"]
126126

127127
[tool.ruff.lint.per-file-ignores]
128-
"tests/*" = ["D100", "D101", "D102", "D103", "D104"]
128+
"tests/*" = [
129+
"D100", "D101", "D102", "D103", "D104",
130+
# Asserts
131+
"B011",
132+
# Disable security checks for tests
133+
"S",
134+
]
129135

130136
[tool.ruff.lint.flake8-copyright]
131137
# Check for properly formatted copyright header in each file
132138
author = "Canonical Ltd."
133139
notice-rgx = "Copyright\\s\\d{4}([-,]\\d{4})*\\s+"
140+
min-file-size = 1
134141

135142
[tool.ruff.lint.mccabe]
136143
max-complexity = 10

src/backups.py

Lines changed: 39 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
import tempfile
1313
from datetime import datetime, timezone
1414
from pathlib import Path
15-
from subprocess import PIPE, TimeoutExpired, run
16-
from typing import Dict, List, Optional, Tuple
15+
from subprocess import TimeoutExpired, run
1716

1817
import boto3 as boto3
1918
import botocore
@@ -92,7 +91,7 @@ def _tls_ca_chain_filename(self) -> str:
9291
return f"{self.charm._storage_path}/pgbackrest-tls-ca-chain.crt"
9392
return ""
9493

95-
def _are_backup_settings_ok(self) -> Tuple[bool, Optional[str]]:
94+
def _are_backup_settings_ok(self) -> tuple[bool, str | None]:
9695
"""Validates whether backup settings are OK."""
9796
if self.model.get_relation(self.relation_name) is None:
9897
return (
@@ -112,17 +111,18 @@ def _can_initialise_stanza(self) -> bool:
112111
# Don't allow stanza initialisation if this unit hasn't started the database
113112
# yet and either hasn't joined the peer relation yet or hasn't configured TLS
114113
# yet while other unit already has TLS enabled.
115-
if not self.charm._patroni.member_started and (
116-
(len(self.charm._peers.data.keys()) == 2)
117-
or (
118-
"tls" not in self.charm.unit_peer_data
119-
and any("tls" in unit_data for _, unit_data in self.charm._peers.data.items())
114+
return not (
115+
not self.charm._patroni.member_started
116+
and (
117+
(len(self.charm._peers.data.keys()) == 2)
118+
or (
119+
"tls" not in self.charm.unit_peer_data
120+
and any("tls" in unit_data for _, unit_data in self.charm._peers.data.items())
121+
)
120122
)
121-
):
122-
return False
123-
return True
123+
)
124124

125-
def _can_unit_perform_backup(self) -> Tuple[bool, Optional[str]]:
125+
def _can_unit_perform_backup(self) -> tuple[bool, str | None]:
126126
"""Validates whether this unit can perform a backup."""
127127
if self.charm.is_blocked:
128128
return False, "Unit is in a blocking state"
@@ -154,7 +154,7 @@ def _can_unit_perform_backup(self) -> Tuple[bool, Optional[str]]:
154154

155155
return self._are_backup_settings_ok()
156156

157-
def can_use_s3_repository(self) -> Tuple[bool, Optional[str]]:
157+
def can_use_s3_repository(self) -> tuple[bool, str | None]:
158158
"""Returns whether the charm was configured to use another cluster repository."""
159159
# Prevent creating backups and storing in another cluster repository.
160160
try:
@@ -166,10 +166,8 @@ def can_use_s3_repository(self) -> Tuple[bool, Optional[str]]:
166166
# Raise an error if the connection timeouts, so the user has the possibility to
167167
# fix network issues and call juju resolve to re-trigger the hook that calls
168168
# this method.
169-
logger.error(
170-
f"error: {str(e)} - please fix the error and call juju resolve on this unit"
171-
)
172-
raise TimeoutError
169+
logger.error(f"error: {e!s} - please fix the error and call juju resolve on this unit")
170+
raise TimeoutError from e
173171

174172
else:
175173
if return_code != 0:
@@ -184,11 +182,11 @@ def can_use_s3_repository(self) -> Tuple[bool, Optional[str]]:
184182
])
185183
if return_code != 0:
186184
raise Exception(error)
187-
system_identifier_from_instance = [
185+
system_identifier_from_instance = next(
188186
line
189187
for line in system_identifier_from_instance.splitlines()
190188
if "Database system identifier" in line
191-
][0].split(" ")[-1]
189+
).split(" ")[-1]
192190
system_identifier_from_stanza = str(stanza.get("db")[0]["system-id"])
193191
if system_identifier_from_instance != system_identifier_from_stanza or stanza.get(
194192
"name"
@@ -207,7 +205,7 @@ def _change_connectivity_to_database(self, connectivity: bool) -> None:
207205
self.charm.unit_peer_data.update({"connectivity": "on" if connectivity else "off"})
208206
self.charm.update_config()
209207

210-
def _construct_endpoint(self, s3_parameters: Dict) -> str:
208+
def _construct_endpoint(self, s3_parameters: dict) -> str:
211209
"""Construct the S3 service endpoint using the region.
212210
213211
This is needed when the provided endpoint is from AWS, and it doesn't contain the region.
@@ -260,9 +258,7 @@ def _create_bucket_if_not_exists(self) -> None:
260258
# Re-raise the error if the connection timeouts, so the user has the possibility to
261259
# fix network issues and call juju resolve to re-trigger the hook that calls
262260
# this method.
263-
logger.error(
264-
f"error: {str(e)} - please fix the error and call juju resolve on this unit"
265-
)
261+
logger.error(f"error: {e!s} - please fix the error and call juju resolve on this unit")
266262
raise e
267263
except ClientError:
268264
logger.warning("Bucket %s doesn't exist or you don't have access to it.", bucket_name)
@@ -286,17 +282,17 @@ def _empty_data_files(self) -> bool:
286282
if path.exists() and path.is_dir():
287283
shutil.rmtree(path)
288284
except OSError as e:
289-
logger.warning(f"Failed to remove contents of the data directory with error: {str(e)}")
285+
logger.warning(f"Failed to remove contents of the data directory with error: {e!s}")
290286
return False
291287

292288
return True
293289

294290
def _execute_command(
295291
self,
296-
command: List[str],
297-
command_input: bytes = None,
298-
timeout: int = None,
299-
) -> Tuple[int, str, str]:
292+
command: list[str],
293+
command_input: bytes | None = None,
294+
timeout: int | None = None,
295+
) -> tuple[int, str, str]:
300296
"""Execute a command in the workload container."""
301297

302298
def demote():
@@ -308,11 +304,11 @@ def result():
308304

309305
return result
310306

311-
process = run(
307+
# Input is generated by the charm
308+
process = run( # noqa: S603
312309
command,
313310
input=command_input,
314-
stdout=PIPE,
315-
stderr=PIPE,
311+
capture_output=True,
316312
preexec_fn=demote(),
317313
timeout=timeout,
318314
)
@@ -349,17 +345,7 @@ def _format_backup_list(self, backup_list) -> str:
349345
path,
350346
) in backup_list:
351347
backups.append(
352-
"{:<20s} | {:<19s} | {:<8s} | {:<20s} | {:<23s} | {:<20s} | {:<20s} | {:<8s} | {:s}".format(
353-
backup_id,
354-
backup_action,
355-
backup_status,
356-
reference,
357-
lsn_start_stop,
358-
start,
359-
stop,
360-
backup_timeline,
361-
path,
362-
)
348+
f"{backup_id:<20s} | {backup_action:<19s} | {backup_status:<8s} | {reference:<20s} | {lsn_start_stop:<23s} | {start:<20s} | {stop:<20s} | {backup_timeline:<8s} | {path:s}"
363349
)
364350
return "\n".join(backups)
365351

@@ -414,7 +400,7 @@ def _generate_backup_list_output(self) -> str:
414400
backup_path,
415401
))
416402

417-
for timeline, (timeline_stanza, timeline_id) in self._list_timelines().items():
403+
for timeline, (_, timeline_id) in self._list_timelines().items():
418404
backup_list.append((
419405
timeline,
420406
"restore",
@@ -542,7 +528,7 @@ def _parse_psql_timestamp(self, timestamp: str) -> datetime:
542528
dt = dt.astimezone(tz=timezone.utc)
543529
return dt.replace(tzinfo=None)
544530

545-
def _parse_backup_id(self, label) -> Tuple[str, str]:
531+
def _parse_backup_id(self, label) -> tuple[str, str]:
546532
"""Parse backup ID as a timestamp and its type."""
547533
if label[-1] == "F":
548534
timestamp = label
@@ -751,7 +737,7 @@ def _on_s3_credential_gone(self, _) -> None:
751737
if self.charm.is_blocked and self.charm.unit.status.message in S3_BLOCK_MESSAGES:
752738
self.charm.unit.status = ActiveStatus()
753739

754-
def _on_create_backup_action(self, event) -> None: # noqa: C901
740+
def _on_create_backup_action(self, event) -> None:
755741
"""Request that pgBackRest creates a backup."""
756742
backup_type = event.params.get("type", "full")
757743
if backup_type not in BACKUP_TYPE_OVERRIDES:
@@ -788,7 +774,7 @@ def _on_create_backup_action(self, event) -> None: # noqa: C901
788774
Model Name: {self.model.name}
789775
Application Name: {self.model.app.name}
790776
Unit Name: {self.charm.unit.name}
791-
Juju Version: {str(juju_version)}
777+
Juju Version: {juju_version!s}
792778
"""
793779
if not self._upload_content_to_s3(
794780
metadata,
@@ -826,7 +812,7 @@ def _on_create_backup_action(self, event) -> None: # noqa: C901
826812
def _run_backup(
827813
self,
828814
event: ActionEvent,
829-
s3_parameters: Dict,
815+
s3_parameters: dict,
830816
datetime_backup_requested: str,
831817
backup_type: str,
832818
) -> None:
@@ -920,7 +906,7 @@ def _on_list_backups_action(self, event) -> None:
920906
event.set_results({"backups": formatted_list})
921907
except ListBackupsError as e:
922908
logger.exception(e)
923-
event.fail(f"Failed to list PostgreSQL backups with error: {str(e)}")
909+
event.fail(f"Failed to list PostgreSQL backups with error: {e!s}")
924910

925911
def _on_restore_action(self, event): # noqa: C901
926912
"""Request that pgBackRest restores a backup."""
@@ -940,10 +926,8 @@ def _on_restore_action(self, event): # noqa: C901
940926
try:
941927
backups = self._list_backups(show_failed=False)
942928
timelines = self._list_timelines()
943-
is_backup_id_real = backup_id and backup_id in backups.keys()
944-
is_backup_id_timeline = (
945-
backup_id and not is_backup_id_real and backup_id in timelines.keys()
946-
)
929+
is_backup_id_real = backup_id and backup_id in backups
930+
is_backup_id_timeline = backup_id and not is_backup_id_real and backup_id in timelines
947931
if backup_id and not is_backup_id_real and not is_backup_id_timeline:
948932
error_message = f"Invalid backup-id: {backup_id}"
949933
logger.error(f"Restore failed: {error_message}")
@@ -1145,7 +1129,7 @@ def _render_pgbackrest_conf_file(self) -> bool:
11451129
self._tls_ca_chain_filename, "\n".join(s3_parameters["tls-ca-chain"]), 0o644
11461130
)
11471131

1148-
with open("templates/pgbackrest.conf.j2", "r") as file:
1132+
with open("templates/pgbackrest.conf.j2") as file:
11491133
template = Template(file.read())
11501134
# Render the template file with the correct values.
11511135
rendered = template.render(
@@ -1177,7 +1161,7 @@ def _restart_database(self) -> None:
11771161
self.charm.update_config()
11781162
self.charm._patroni.start_patroni()
11791163

1180-
def _retrieve_s3_parameters(self) -> Tuple[Dict, List[str]]:
1164+
def _retrieve_s3_parameters(self) -> tuple[dict, list[str]]:
11811165
"""Retrieve S3 parameters from the S3 integrator relation."""
11821166
s3_parameters = self.s3_client.get_s3_connection_info()
11831167
required_parameters = [
@@ -1254,7 +1238,7 @@ def _upload_content_to_s3(
12541238
self: str,
12551239
content: str,
12561240
s3_path: str,
1257-
s3_parameters: Dict,
1241+
s3_parameters: dict,
12581242
) -> bool:
12591243
"""Uploads the provided contents to the provided S3 bucket.
12601244

0 commit comments

Comments
 (0)