Skip to content

Commit

Permalink
Updated linters and reformatters
Browse files Browse the repository at this point in the history
  • Loading branch information
liminspace committed Aug 26, 2024
1 parent 70c004a commit 6483ecc
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 57 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: pre-commit

on:
push:
pull_request:
types: [ opened, synchronize ]

jobs:
pre-commit:
runs-on: ubuntu-latest
emv:
PYTHON_VER: 3.12
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python ${{ env.PYTHON_VER }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VER }}
- name: Run pre-commit tasks
uses: pre-commit/action@v3.0.1
17 changes: 9 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ repos:
rev: 'v0.6.2'
hooks:
- id: ruff
args: [ --fix ]
args:
- --fix
- id: ruff-format

- repo: https://github.com/PyCQA/bandit
rev: 1.7.9
hooks:
- id: bandit
args: [ "-c", "pyproject.toml" ]
additional_dependencies: ["bandit[toml]"]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
additional_dependencies:
- types-requests

- repo: https://github.com/PyCQA/bandit
rev: 1.7.9
hooks:
- id: bandit
args: [ "-c", "pyproject.toml" ]
additional_dependencies: [ "bandit[toml]" ]
9 changes: 4 additions & 5 deletions mjml/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ def _get_renderer_params(cls) -> Dict[str, Any]:
else mjml_settings.MJML_CHECK_CMD_ON_STARTUP
),
}
elif mjml_settings.MJML_BACKEND_MODE == "tcpserver":
if mjml_settings.MJML_BACKEND_MODE == "tcpserver":
return {
"BACKEND": "mjml.backends.TCPServerBackend",
"SERVERS": mjml_settings.MJML_TCPSERVERS or [("127.0.0.1", 28101)],
}
elif mjml_settings.MJML_BACKEND_MODE == "httpserver":
if mjml_settings.MJML_BACKEND_MODE == "httpserver":
return {
"BACKEND": "mjml.backends.RequestsHTTPServerBackend",
"SERVERS": [
Expand All @@ -68,12 +68,11 @@ def _get_renderer_params(cls) -> Dict[str, Any]:
{
"URL": "https://api.mjml.io/v1/render",
"HTTP_AUTH": None, # None (default) or ('login', 'password')
}
},
]
],
}
else:
RuntimeError("Invalid MJML settings")
RuntimeError("Invalid MJML settings")

# default settings
return {
Expand Down
17 changes: 10 additions & 7 deletions mjml/backends/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,39 +58,42 @@ def render_mjml_to_html(self, mjml_source: str) -> str:
stderr = p.communicate(force_bytes(mjml_source))[1]
except OSError as e:
cmd_str = " ".join(self._cmd_args)
raise RuntimeError(
err_msg = (
f'Problem to run command "{cmd_str}"\n'
f"{e}\n"
"Check that mjml is installed and allow permissions to execute.\n"
"See https://github.com/mjmlio/mjml#installation"
) from e
)
raise RuntimeError(err_msg) from e
stdout_tmp_f.seek(0)
stdout = stdout_tmp_f.read()

if stderr:
raise RuntimeError(f"MJML stderr is not empty: {force_str(stderr)}.")
err_msg = f"MJML stderr is not empty: {force_str(stderr)}."
raise RuntimeError(err_msg)

return force_str(stdout)

def check(self) -> None:
if not self._check_on_startup:
return None
return

try:
html = self.render_mjml_to_html(
"<mjml><mj-body><mj-container><mj-text>" "MJMLv3" "</mj-text></mj-container></mj-body></mjml>"
"<mjml><mj-body><mj-container><mj-text>MJMLv3</mj-text></mj-container></mj-body></mjml>",
)
except RuntimeError:
try:
html = self.render_mjml_to_html(
"<mjml><mj-body><mj-section><mj-column><mj-text>"
"MJMLv4"
"</mj-text></mj-column></mj-section></mj-body></mjml>"
"</mj-text></mj-column></mj-section></mj-body></mjml>",
)
except RuntimeError as e:
raise RendererBackendCheckFailedError(e) from e
if "<html " not in html:
raise RendererBackendCheckFailedError(
err_msg = (
"mjml command returns wrong result.\n"
"Check MJML is installed correctly. See https://github.com/mjmlio/mjml#installation"
)
raise RendererBackendCheckFailedError(err_msg)
24 changes: 14 additions & 10 deletions mjml/backends/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def _parse_raw_server_params(cls, raw_server_params: Dict[str, Any]) -> Dict[str
else:
auth_type, auth_params = "BASIC", auth_raw
else:
raise ValueError("Invalid AUTH value")
err_msg = "Invalid AUTH value"
raise ValueError(err_msg)

server_params["auth"] = cls._get_auth_instance(
auth_type=auth_type,
Expand All @@ -59,7 +60,8 @@ def _get_auth_instance(cls, auth_type: str, auth_params: Union[List, Tuple, Dict
elif isinstance(auth_params, (list, tuple)):
instance = auth_cls(*auth_params)
else:
raise ValueError("Invalid type of auth_params")
err_msg = "Invalid type of auth_params"
raise ValueError(err_msg)
return instance

def render_mjml_to_html(self, mjml_source: str) -> str:
Expand Down Expand Up @@ -91,18 +93,20 @@ def render_mjml_to_html(self, mjml_source: str) -> str:
f'Line: {e.get("line")} Tag: {e.get("tagName")} Message: {e.get("message")}' for e in errors
]
msg_str = "\n".join(msg_lines)
raise RuntimeError(f"MJML compile error (via MJML HTTP server): {msg_str}")
err_msg = f"MJML compile error (via MJML HTTP server): {msg_str}"
raise RuntimeError(err_msg)

return force_str(data["html"])
else:
msg = (
f"[code={response.status_code}, request_id={data.get('request_id', '')}] "
f"{data.get('message', 'Unknown error.')}"
)
raise RuntimeError(f"MJML compile error (via MJML HTTP server): {msg}")
msg = (
f"[code={response.status_code}, request_id={data.get('request_id', '')}] "
f"{data.get('message', 'Unknown error.')}"
)
err_msg = f"MJML compile error (via MJML HTTP server): {msg}"
raise RuntimeError(err_msg)

raise RuntimeError(
err_msg = (
"MJML compile error (via MJML HTTP server): no working server\n"
f"Number of servers: {len(self._servers_params)}\n"
f"Timeouts: {timeouts}"
)
raise RuntimeError(err_msg)
7 changes: 4 additions & 3 deletions mjml/backends/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,18 @@ def render_mjml_to_html(self, mjml_source: str) -> str:
result = force_str(self._socket_recvall(s, result_len))
if ok:
return result
else:
raise RuntimeError(f"MJML compile error (via MJML TCP server): {result}")
err_msg = f"MJML compile error (via MJML TCP server): {result}"
raise RuntimeError(err_msg)
except socket.timeout:
timeouts += 1
finally:
s.close()
raise RuntimeError(
err_msg = (
"MJML compile error (via MJML TCP server): no working server\n"
f"Number of servers: {len(self._servers_params)}\n"
f"Timeouts: {timeouts}"
)
raise RuntimeError(err_msg)

@classmethod
def _socket_recvall(cls, sock: socket.socket, n: int) -> Optional[bytes]:
Expand Down
36 changes: 26 additions & 10 deletions mjml/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
# old deprecated settings

MJML_BACKEND_MODE = getattr(settings, "MJML_BACKEND_MODE", None)
if MJML_BACKEND_MODE is not None:
assert MJML_BACKEND_MODE in {"cmd", "tcpserver", "httpserver"}
if MJML_BACKEND_MODE is not None and MJML_BACKEND_MODE not in {"cmd", "tcpserver", "httpserver"}:
err_msg = f"Invalid value of MJML_BACKEND_MODE: {MJML_BACKEND_MODE}; allowed values: cmd, tcpserver, httpserver"
raise ValueError(err_msg)

# cmd backend mode configs
MJML_EXEC_CMD = getattr(settings, "MJML_EXEC_CMD", None)
Expand All @@ -15,19 +16,34 @@
# tcpserver backend mode configs
MJML_TCPSERVERS = getattr(settings, "MJML_TCPSERVERS", None)
if MJML_TCPSERVERS is not None:
assert isinstance(MJML_TCPSERVERS, (list, tuple))
if not isinstance(MJML_TCPSERVERS, (list, tuple)):
err_msg = f"Invalid type of MJML_TCPSERVERS: {type(MJML_TCPSERVERS)}; allowed types: list, tuple"
raise ValueError(err_msg)

for t in MJML_TCPSERVERS:
assert isinstance(t, (list, tuple)) and len(t) == 2 and isinstance(t[0], str) and isinstance(t[1], int)
if not (isinstance(t, (list, tuple)) and len(t) == 2 and isinstance(t[0], str) and isinstance(t[1], int)):
err_msg = "Invalid value of MJML_TCPSERVERS"
raise ValueError(err_msg)

# httpserver backend mode configs
MJML_HTTPSERVERS = getattr(settings, "MJML_HTTPSERVERS", None)
if MJML_HTTPSERVERS is not None:
assert isinstance(MJML_HTTPSERVERS, (list, tuple))
if not isinstance(MJML_HTTPSERVERS, (list, tuple)):
err_msg = f"Invalid type of MJML_HTTPSERVERS: {type(MJML_HTTPSERVERS)}; allowed types: list, tuple"
raise ValueError(err_msg)

for t in MJML_HTTPSERVERS:
assert isinstance(t, dict)
assert "URL" in t and isinstance(t["URL"], str)
if not (isinstance(t, dict) and "URL" in t and isinstance(t["URL"], str)):
err_msg = "Invalid value of MJML_HTTPSERVERS"
raise ValueError(err_msg)

if "HTTP_AUTH" in t:
http_auth = t["HTTP_AUTH"]
assert isinstance(http_auth, (type(None), list, tuple))
if http_auth is not None:
assert len(http_auth) == 2 and isinstance(http_auth[0], str) and isinstance(http_auth[1], str)
if not isinstance(http_auth, (type(None), list, tuple)):
err_msg = "Invalid value of HTTP_AUTH in MJML_HTTPSERVERS"
raise ValueError(err_msg)
if http_auth is not None and not (
len(http_auth) == 2 and isinstance(http_auth[0], str) and isinstance(http_auth[1], str)
):
err_msg = "Invalid value of HTTP_AUTH in MJML_HTTPSERVERS"
raise ValueError(err_msg)
3 changes: 2 additions & 1 deletion mjml/templatetags/mjml.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def mjml(parser, token) -> MJMLRenderNode:
parser.delete_first_token()
tokens = token.split_contents()
if len(tokens) != 1:
raise template.TemplateSyntaxError(f"'{tokens[0]!r}' tag doesn't receive any arguments.")
err_msg = f"'{tokens[0]!r}' tag doesn't receive any arguments."
raise template.TemplateSyntaxError(err_msg)
renderer = apps.get_app_config(MJMLConfig.name).get_renderer()
return MJMLRenderNode(nodelist, renderer=renderer)
41 changes: 39 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,49 @@ line-length = 118
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"UP", # pyupgrade
"I", # isort
"B", # flake8-bugbear
"SIM", # flake8-simplify
"T20", # flake8-print
"BLE", # flake8-blind-except
"C4", # flake8-comprehensions
"I", # isort
"A", # flake8-builtins
"COM", # flake8-commas
"DJ", # flake8-django
"S", # flake8-bandit
"EM", # flake8-errmsg
"FA", # flake8-future-annotations
"ISC", # flake8-implicit-str-concat
"LOG", # flake8-logging
"G", # flake8-logging-format
"PIE", # flake8-pie
"PYI", # flake8-pyi
"Q", # flake8-quotes
"RSE", # flake8-raise
"RET", # flake8-return
"TID", # flake8-tidy-imports
"TCH", # flake8-type-checking
"ARG", # flake8-unused-arguments
"PTH", # flake8-use-pathlib
"TD", # flake8-todos
"FLY", # flynt
"PERF", # Perflint
"UP", # pyupgrade
"C90", # mccabe
]
ignore = [
"S105",
"S603",
"S607",
"TD003",
"TD002",
]

[tool.ruff.lint.pyupgrade]
# Preserve types, even if a file imports `from __future__ import annotations`.
keep-runtime-typing = true

[tool.ruff.lint.mccabe]
max-complexity = 10
2 changes: 1 addition & 1 deletion tests/settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pathlib import Path

BASE_DIR = Path(__file__).absolute().parent
BASE_DIR = Path(__file__).resolve().parent

SECRET_KEY = "test"

Expand Down
6 changes: 3 additions & 3 deletions tests/test_backends_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def test_http_server_error(self) -> None:
@mock.patch("requests.post")
def test_http_auth(self, post_mock) -> None:
with safe_change_mjml_settings() as mjml_app_config:
for server_conf in mjml_settings.MJML_HTTPSERVERS: # todo fix
for server_conf in mjml_settings.MJML_HTTPSERVERS: # type: ignore # TODO: fix
server_conf["HTTP_AUTH"] = ("testuser", "testpassword")
mjml_app_config.ready()

Expand All @@ -92,8 +92,8 @@ def test_http_auth(self, post_mock) -> None:
"html": "html_string",
"mjml": "mjml_string",
"mjml_version": "4.5.1",
}
)
},
),
)
response._content = content
response.encoding = "utf-8"
Expand Down
2 changes: 1 addition & 1 deletion tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from .tools import MJMLFixtures, render_tpl

# class TestMJMLApps(TestCase): # todo rewrite
# class TestMJMLApps(TestCase): # TODO rewrite # noqa
# def test_check_mjml_command(self) -> None:
# with safe_change_mjml_settings() as mjml_app_config:
# mjml_settings.MJML_EXEC_CMD = '/no_mjml_exec_test'
Expand Down
14 changes: 8 additions & 6 deletions tests/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ def _terminate_processes(cls) -> None:

@classmethod
def _start_tcp_servers(cls) -> None:
root_dir = os.path.dirname(settings.BASE_DIR)
tcpserver_path = os.path.join(root_dir, "mjml-tcpserver", "tcpserver.js")
root_dir = settings.BASE_DIR.parent
tcpserver_path = root_dir / "mjml-tcpserver/tcpserver.js"
env = os.environ.copy()
env["NODE_PATH"] = root_dir
for host, port in mjml_settings.MJML_TCPSERVERS: # todo fix
for host, port in mjml_settings.MJML_TCPSERVERS: # type: ignore # TODO: fix
p = subprocess.Popen(
[
"node",
Expand All @@ -90,7 +90,7 @@ def _stop_tcp_servers(cls) -> None:
@classmethod
def _start_http_servers(cls) -> None:
env = os.environ.copy()
for server_conf in mjml_settings.MJML_HTTPSERVERS: # todo fix
for server_conf in mjml_settings.MJML_HTTPSERVERS: # type: ignore # TODO: fix
parsed = urlparse(server_conf["URL"])
host, port = parsed.netloc.split(":")
p = subprocess.Popen(
Expand Down Expand Up @@ -119,7 +119,8 @@ def setUpClass(cls) -> None:
elif cls.SERVER_TYPE == "httpserver":
cls._start_http_servers()
else:
raise RuntimeError("Invalid SERVER_TYPE: {}", cls.SERVER_TYPE)
err_msg = f"Invalid SERVER_TYPE: {cls.SERVER_TYPE}"
raise RuntimeError(err_msg)

@classmethod
def tearDownClass(cls) -> None:
Expand All @@ -128,7 +129,8 @@ def tearDownClass(cls) -> None:
elif cls.SERVER_TYPE == "httpserver":
cls._stop_http_servers()
else:
raise RuntimeError("Invalid SERVER_TYPE: {}", cls.SERVER_TYPE)
err_msg = f"Invalid SERVER_TYPE: {cls.SERVER_TYPE}"
raise RuntimeError(err_msg)
super().tearDownClass() # type: ignore


Expand Down

0 comments on commit 6483ecc

Please sign in to comment.