Skip to content

Commit ef4f5a0

Browse files
authored
Enable debug logging (#111)
* Formatting, timezone, type hints, docstrings * Rename logging function * Add tests for debug logging by retrieving message if MESSAGE_ID isn't correct; update pre-commit hooks * Fix duplication of the log handlers * Enable flake8 and add configs to pyproject.toml, apply flake8 linter
1 parent f918e16 commit ef4f5a0

File tree

4 files changed

+248
-38
lines changed

4 files changed

+248
-38
lines changed

.pre-commit-config.yaml

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,16 @@ repos:
6666
args: [--write]
6767

6868
- repo: https://github.com/python-jsonschema/check-jsonschema
69-
rev: 0.29.4
69+
rev: 0.30.0
7070
hooks:
7171
- id: check-github-workflows
7272

73+
- repo: https://github.com/pre-commit/mirrors-autopep8
74+
rev: v2.0.4
75+
hooks:
76+
- id: autopep8
77+
exclude: ^docs/
78+
7379
- repo: https://github.com/akaihola/darker
7480
rev: v2.1.1
7581
hooks:
@@ -85,25 +91,19 @@ repos:
8591
- --remove-unused-variable
8692
- --ignore-init-module-imports
8793

88-
- repo: https://github.com/pre-commit/mirrors-autopep8
89-
rev: v2.0.4
94+
- repo: https://github.com/pycqa/flake8
95+
rev: 7.1.1
9096
hooks:
91-
- id: autopep8
97+
- id: flake8
98+
additional_dependencies:
99+
- radon
100+
- flake8-docstrings
101+
- Flake8-pyproject
92102
exclude: ^docs/
93103

94-
# - repo: https://github.com/pycqa/flake8
95-
# rev: 7.1.1
96-
# hooks:
97-
# - id: flake8
98-
# additional_dependencies:
99-
# - radon
100-
# - flake8-docstrings
101-
# - Flake8-pyproject
102-
# exclude: ^docs/
103-
104104

105105
- repo: https://github.com/PyCQA/pylint
106-
rev: v3.3.1
106+
rev: v3.3.2
107107
hooks:
108108
- id: pylint
109109
args:
@@ -117,7 +117,7 @@ repos:
117117

118118
- repo: https://github.com/charliermarsh/ruff-pre-commit
119119
# Ruff version.
120-
rev: v0.7.2
120+
rev: v0.8.2
121121
hooks:
122122
# Run the linter.
123123
- id: ruff
@@ -141,12 +141,12 @@ repos:
141141
exclude: ^samples/
142142

143143
- repo: https://github.com/RobertCraigie/pyright-python
144-
rev: v1.1.388
144+
rev: v1.1.390
145145
hooks:
146146
- id: pyright
147147

148148
- repo: https://github.com/PyCQA/bandit
149-
rev: 1.7.10
149+
rev: 1.8.0
150150
hooks:
151151
- id: bandit
152152
args: ["-c", "pyproject.toml", "-r", "."]
@@ -155,7 +155,7 @@ repos:
155155
additional_dependencies: [".[toml]"]
156156

157157
- repo: https://github.com/crate-ci/typos
158-
rev: v1.27.0
158+
rev: typos-dict-v0.11.37
159159
hooks:
160160
- id: typos
161161

mailjet_rest/client.py

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@
3131
import json
3232
import logging
3333
import re
34+
import sys
35+
from datetime import datetime
36+
from datetime import timezone
3437
from re import Match
3538
from typing import TYPE_CHECKING
3639
from typing import Any
40+
from typing import Callable
3741

3842
import requests # type: ignore[import-untyped]
3943
from requests.compat import urljoin # type: ignore[import-untyped]
4044

41-
from .utils.version import get_version
45+
from mailjet_rest.utils.version import get_version
4246

4347

4448
if TYPE_CHECKING:
@@ -548,28 +552,71 @@ def build_url(
548552
return url
549553

550554

551-
def parse_response(response: Response, debug: bool = False) -> Any:
552-
"""Parse the response from an API request.
555+
def logging_handler(
556+
to_file: bool = False,
557+
) -> logging.Logger:
558+
"""Create and configure a logger for logging API requests.
553559
554-
This function extracts the JSON data from the response and logs debug information if the `debug` flag is set to True.
560+
This function creates a logger object and configures it to handle both
561+
standard output (stdout) and a file if the `to_file` parameter is set to True.
562+
The logger is set to log at the DEBUG level and uses a custom formatter to
563+
include the log level and message.
555564
556565
Parameters:
557-
response (requests.models.Response): The response object from the API request.
558-
debug (bool, optional): A flag indicating whether debug information should be logged. Defaults to False.
566+
to_file (bool): A flag indicating whether to log to a file. If True, logs will be written to a file.
567+
Defaults to False.
559568
560569
Returns:
561-
Any: The JSON data extracted from the response.
570+
logging.Logger: A configured logger object for logging API requests.
571+
"""
572+
logger = logging.getLogger()
573+
logger.setLevel(logging.DEBUG)
574+
formatter = logging.Formatter("%(levelname)s | %(message)s")
575+
576+
if to_file:
577+
now = datetime.now(tz=timezone.utc)
578+
date_time = now.strftime("%Y%m%d_%H%M%S")
579+
580+
log_file = f"{date_time}.log"
581+
file_handler = logging.FileHandler(log_file)
582+
file_handler.setFormatter(formatter)
583+
logger.addHandler(file_handler)
584+
585+
stdout_handler = logging.StreamHandler(sys.stdout)
586+
stdout_handler.setFormatter(formatter)
587+
logger.addHandler(stdout_handler)
588+
589+
return logger
590+
591+
592+
def parse_response(
593+
response: Response,
594+
log: Callable,
595+
debug: bool = False,
596+
) -> Any:
597+
"""Parse the response from an API request and return the JSON data.
598+
599+
Parameters:
600+
response (Response): The response object from the API request.
601+
log (Callable): A function or method that logs debug information.
602+
debug (bool): A flag indicating whether debug mode is enabled. Defaults to False.
603+
604+
Returns:
605+
Any: The JSON data from the API response.
562606
"""
563607
data = response.json()
564608

565609
if debug:
566-
logging.debug("REQUEST: %s", response.request.url)
567-
logging.debug("REQUEST_HEADERS: %s", response.request.headers)
568-
logging.debug("REQUEST_CONTENT: %s", response.request.body)
569-
570-
logging.debug("RESPONSE: %s", response.content)
571-
logging.debug("RESP_HEADERS: %s", response.headers)
572-
logging.debug("RESP_CODE: %s", response.status_code)
610+
lgr = log()
611+
lgr.debug("REQUEST: %s", response.request.url)
612+
lgr.debug("REQUEST_HEADERS: %s", response.request.headers)
613+
lgr.debug("REQUEST_CONTENT: %s", response.request.body)
614+
615+
lgr.debug("RESPONSE: %s", response.content)
616+
lgr.debug("RESP_HEADERS: %s", response.headers)
617+
lgr.debug("RESP_CODE: %s", response.status_code)
618+
# Clear logger handlers to prevent making log duplications
619+
logging.getLogger().handlers.clear()
573620

574621
return data
575622

pyproject.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,17 @@ ignore-overlong-task-comments = true
276276
[tool.ruff.lint.pydocstyle]
277277
convention = "google"
278278

279+
[tool.flake8]
280+
exclude = ["samples/*"]
281+
# TODO: D100 - create docstrings for modules test_client.py and test_version.py
282+
ignore = ['E501', "D100"]
283+
extend-ignore = "W503"
284+
per-file-ignores = [
285+
'__init__.py:F401',
286+
]
287+
max-line-length = 88
288+
count = true
289+
279290
[tool.mypy]
280291
strict = true
281292
# Adapted from this StackOverflow post:

0 commit comments

Comments
 (0)