Skip to content

Commit

Permalink
Use ruff as linter and formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
shayancanonical committed Jul 19, 2024
1 parent 3c48ce8 commit 2254834
Show file tree
Hide file tree
Showing 26 changed files with 213 additions and 436 deletions.
296 changes: 32 additions & 264 deletions poetry.lock

Large diffs are not rendered by default.

77 changes: 40 additions & 37 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,14 @@ opentelemetry-exporter-otlp-proto-http = "1.21.0"
optional = true

[tool.poetry.group.format.dependencies]
black = "^24.4.2"
isort = "^5.13.2"
ruff = "^0.4.5"

[tool.poetry.group.lint]
optional = true

[tool.poetry.group.lint.dependencies]
black = "^24.4.2"
isort = "^5.13.2"
flake8 = "^7.0.0"
flake8-docstrings = "^1.7.0"
flake8-copyright = "^0.2.4"
flake8-builtins = "^2.5.0"
pyproject-flake8 = "^7.0.0"
pep8-naming = "^0.14.1"
codespell = "^2.3.0"
ruff = "^0.4.5"

[tool.poetry.group.unit.dependencies]
pytest = "^8.2.2"
Expand Down Expand Up @@ -85,34 +77,45 @@ log_cli_level = "INFO"
markers = ["unstable"]

# Formatting tools configuration
[tool.black]
[tool.ruff]
# preview and explicit preview are enabled for CPY001
preview = true
target-version = "py38"
src = ["src", "."]
line-length = 99
target-version = ["py38"]

[tool.isort]
profile = "black"

# Linting tools configuration
[tool.flake8]
max-line-length = 99
max-doc-length = 99
max-complexity = 10
exclude = [".git", "__pycache__", ".tox", "build", "dist", "*.egg_info", "venv"]
select = ["E", "W", "F", "C", "N", "R", "D", "H"]
# Ignore W503, E501 because using black creates errors with this
[tool.ruff.lint]
explicit-preview-rules = true
select = ["A", "E", "W", "F", "C", "N", "D", "I", "CPY001"]
# Ignore E501 because using creates errors with this
# Ignore D107 Missing docstring in __init__
# Ignore D105 Missing docstring in magic method
# Ignore D415 Docstring first line punctuation (doesn't make sense for properties)
# Ignore D403 First word of the first line should be properly capitalized (false positive on "MySQL")
# Ignore N818 Exception should be named with an Error suffix
# Ignore D102 Missing docstring in public method (pydocstyle doesn't look for docstrings in super class
# Ignore W505 So that strings in comments aren't split across lines
# https://github.com/PyCQA/pydocstyle/issues/309) TODO: add pylint check? https://github.com/PyCQA/pydocstyle/issues/309#issuecomment-1284142716
ignore = ["W503", "E501", "D107", "D105", "D415", "D403", "N818", "D102", "W505"]
# D100, D101, D102, D103: Ignore missing docstrings in tests
per-file-ignores = ["tests/*:D100,D101,D102,D103,D104"]
docstring-convention = "google"
ignore = [
"D107",
"D203",
"D204",
"D213",
"D215",
"D400",
"D404",
"D406",
"D407",
"D408",
"D409",
"D413",
"D415",
"E501",
]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["D1"]

[tool.ruff.lint.flake8-copyright]
# Check for properly formatted copyright header in each file
copyright-check = "True"
copyright-author = "Canonical Ltd."
copyright-regexp = "Copyright\\s\\d{4}([-,]\\d{4})*\\s+%(author)s"
author = "Canonical Ltd."
notice-rgx = "Copyright\\s\\d{4}([-,]\\d{4})*\\s+"

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

[tool.ruff.lint.pydocstyle]
convention = "google"
2 changes: 1 addition & 1 deletion src/abstract_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def _upgrade(self) -> typing.Optional[upgrade.Upgrade]:
@property
@abc.abstractmethod
def _logrotate(self) -> logrotate.LogRotate:
"""logrotate"""
"""Logrotate"""

@property
@abc.abstractmethod
Expand Down
2 changes: 1 addition & 1 deletion src/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def _run_command(
command: typing.List[str],
*,
timeout: typing.Optional[int],
input: str = None,
stdin: str = None,
) -> str:
"""Run command in container.
Expand Down
1 change: 1 addition & 0 deletions src/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
https://juju.is/docs/sdk/a-charms-life
"""

import enum
import logging
import typing
Expand Down
4 changes: 3 additions & 1 deletion src/machine_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def _container(self) -> snap.Snap:
def _upgrade(self) -> typing.Optional[machine_upgrade.Upgrade]:
try:
return machine_upgrade.Upgrade(self)
except upgrade.PeerRelationNotReady:
except upgrade.PeerRelationNotReadyError:
pass

@property
Expand Down Expand Up @@ -102,6 +102,7 @@ def _exposed_read_only_endpoint(self) -> str:
return f"{self.host_address}:{self._READ_ONLY_PORT}"

def is_externally_accessible(self, *, event) -> typing.Optional[bool]:
"""Whether endpoints should be externally accessible."""
return self._database_provides.external_connectivity(event)

def _reconcile_node_port(self, *, event) -> None:
Expand All @@ -116,6 +117,7 @@ def _reconcile_ports(self, *, event) -> None:
self.unit.set_ports(*ports)

def wait_until_mysql_router_ready(self, *, event) -> None:
"""Wait until a connection to MySQL Router is possible."""
logger.debug("Waiting until MySQL Router is ready")
self.unit.status = ops.MaintenanceStatus("MySQL Router starting")
try:
Expand Down
2 changes: 2 additions & 0 deletions src/machine_logrotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __init__(self, *, container_: container.Container):
self._cron_file = self._container.path("/etc/cron.d/flush_mysqlrouter_logs")

def enable(self) -> None:
"""Enable logrotation."""
logger.debug("Creating logrotate config file")

template = jinja2.Template(self._container.path("templates/logrotate.j2").read_text())
Expand All @@ -50,6 +51,7 @@ def enable(self) -> None:
logger.debug("Added cron job for logrotate")

def disable(self) -> None:
"""Disable logrotation."""
logger.debug("Removing cron job for log rotation of mysqlrouter")
self._logrotate_config.unlink(missing_ok=True)
self._cron_file.unlink(missing_ok=True)
Expand Down
6 changes: 6 additions & 0 deletions src/machine_upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Derived from specification: DA058 - In-Place Upgrades - Kubernetes v2
(https://docs.google.com/document/d/1tLjknwHudjcHs42nzPVBNkHs98XxAOT2BXGGpP7NyEU/)
"""

import json
import logging
import time
Expand All @@ -30,6 +31,7 @@ class Upgrade(upgrade.Upgrade):

@property
def unit_state(self) -> typing.Optional[upgrade.UnitState]:
"""Get the unit's state."""
if (
self._unit_workload_container_version is not None
and self._unit_workload_container_version != self._app_workload_container_version
Expand All @@ -40,6 +42,7 @@ def unit_state(self) -> typing.Optional[upgrade.UnitState]:

@unit_state.setter
def unit_state(self, value: upgrade.UnitState) -> None:
"""Set the unit's state."""
if value is upgrade.UnitState.HEALTHY:
# Set snap revision on first install
self._unit_workload_container_version = snap.revision
Expand Down Expand Up @@ -71,6 +74,7 @@ def _get_unit_healthy_status(

@property
def app_status(self) -> typing.Optional[ops.StatusBase]:
"""Status to set for the app."""
if not self.in_progress:
return
if not self.is_compatible:
Expand Down Expand Up @@ -158,6 +162,7 @@ def upgrade_resumed(self, value: bool):

@property
def authorized(self) -> bool:
"""If the upgrade action is authorized."""
assert self._unit_workload_container_version != self._app_workload_container_version
for index, unit in enumerate(self._sorted_units):
if unit.name == self._unit.name:
Expand Down Expand Up @@ -187,6 +192,7 @@ def upgrade_unit(
tls: bool,
exporter_config: "relations.cos.ExporterConfig",
) -> None:
"""Upgrade the unit."""
logger.debug(f"Upgrading {self.authorized=}")
self.unit_state = upgrade.UnitState.UPGRADING
workload_.upgrade(event=event, unit=self._unit, tls=tls, exporter_config=exporter_config)
Expand Down
28 changes: 12 additions & 16 deletions src/machine_workload.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,19 @@ def _get_bootstrap_command(
) -> typing.List[str]:
command = super()._get_bootstrap_command(event=event, connection_info=connection_info)
if self._charm.is_externally_accessible(event=event):
command.extend(
[
"--conf-bind-address",
"0.0.0.0",
]
)
command.extend([
"--conf-bind-address",
"0.0.0.0",
])
else:
command.extend(
[
"--conf-use-sockets",
# For unix sockets, authentication fails on first connection if this option is not
# set. Workaround for https://bugs.mysql.com/bug.php?id=107291
"--conf-set-option",
"DEFAULT.server_ssl_mode=PREFERRED",
"--conf-skip-tcp",
]
)
command.extend([
"--conf-use-sockets",
# For unix sockets, authentication fails on first connection if this option is not
# set. Workaround for https://bugs.mysql.com/bug.php?id=107291
"--conf-set-option",
"DEFAULT.server_ssl_mode=PREFERRED",
"--conf-skip-tcp",
])
return command

def _update_configured_socket_file_locations(self) -> None:
Expand Down
38 changes: 18 additions & 20 deletions src/mysql_shell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Shell:

@property
def username(self):
"""The username to user."""
return self._connection_info.username

def _run_code(self, code: str) -> None:
Expand All @@ -82,14 +83,12 @@ def render(connection_info: "relations.database_requires.ConnectionInformation")
error_file = self._container.path("/tmp/mysqlsh_error.json")
temporary_script_file.write_text(script)
try:
self._container.run_mysql_shell(
[
"--no-wizard",
"--python",
"--file",
str(temporary_script_file.relative_to_container),
]
)
self._container.run_mysql_shell([
"--no-wizard",
"--python",
"--file",
str(temporary_script_file.relative_to_container),
])
except container.CalledProcessError as e:
logger.exception(
f"Failed to run MySQL Shell script:\n{logged_script}\n\nstderr:\n{e.stderr}\n"
Expand All @@ -105,8 +104,8 @@ def render(connection_info: "relations.database_requires.ConnectionInformation")
raise ShellDBError(**exception)
except ShellDBError as e:
if e.code == 2003:
logger.exception(server_exceptions.ConnectionError.MESSAGE)
raise server_exceptions.ConnectionError
logger.exception(server_exceptions.MySQLConnectionError.MESSAGE)
raise server_exceptions.MySQLConnectionError
else:
logger.exception(
f"Failed to run MySQL Shell script:\n{logged_script}\n\nMySQL client error {e.code}\nMySQL Shell traceback:\n{e.traceback_message}\n"
Expand Down Expand Up @@ -136,23 +135,22 @@ def create_application_database_and_user(self, *, username: str, database: str)
attributes = self._get_attributes()
logger.debug(f"Creating {database=} and {username=} with {attributes=}")
password = utils.generate_password()
self._run_sql(
[
f"CREATE DATABASE IF NOT EXISTS `{database}`",
f"CREATE USER `{username}` IDENTIFIED BY '{password}' ATTRIBUTE '{attributes}'",
f"GRANT ALL PRIVILEGES ON `{database}`.* TO `{username}`",
]
)
self._run_sql([
f"CREATE DATABASE IF NOT EXISTS `{database}`",
f"CREATE USER `{username}` IDENTIFIED BY '{password}' ATTRIBUTE '{attributes}'",
f"GRANT ALL PRIVILEGES ON `{database}`.* TO `{username}`",
])
logger.debug(f"Created {database=} and {username=} with {attributes=}")
return password

def add_attributes_to_mysql_router_user(
self, *, username: str, router_id: str, unit_name: str
) -> None:
"""Add attributes to user created during MySQL Router bootstrap."""
attributes = self._get_attributes(
{"router_id": router_id, "created_by_juju_unit": unit_name}
)
attributes = self._get_attributes({
"router_id": router_id,
"created_by_juju_unit": unit_name,
})
logger.debug(f"Adding {attributes=} to {username=}")
self._run_sql([f"ALTER USER `{username}` ATTRIBUTE '{attributes}'"])
logger.debug(f"Added {attributes=} to {username=}")
Expand Down
Loading

0 comments on commit 2254834

Please sign in to comment.