Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions hpdf/hpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import base64
import os.path
import sys
import tempfile
from datetime import datetime
from pathlib import Path
from shutil import copy
from time import sleep
from typing import Optional, List
from typing import Dict, List, Optional

import requests
from requests import Response
Expand Down Expand Up @@ -151,25 +150,28 @@ def get_pdf_from_html(driver, url) -> bytes:
print("html2pdf: executing print command with ChromeDriver.") # noqa: T201
result = driver.execute_cdp_cmd("Page.printToPDF", calculated_print_options)

class Done(Exception): pass
class Done(Exception):
pass

datetime_start = datetime.today()

logs = None
logs: List[Dict[str, str]] = []
try:
while True:
logs = driver.get_log("browser")
for entry_ in logs:
if "HTML2PDF4DOC time" in entry_["message"]:
print("success: HTML2PDF completed its job.")
print("success: HTML2PDF completed its job.") # noqa: T201
raise Done
if (datetime.today() - datetime_start).total_seconds() > 60:
raise TimeoutError
sleep(0.5)
except Done:
pass
except TimeoutError:
print("error: could not receive a successful completion status from HTML2PDF.")
print( # noqa: T201
"error: could not receive a successful completion status from HTML2PDF."
)
sys.exit(1)

print("html2pdf: JS logs from the print session:") # noqa: T201
Expand Down Expand Up @@ -244,19 +246,15 @@ def main():
type=str,
help="Optional path to a cache directory whereto the ChromeDriver is downloaded.",
)
parser.add_argument("paths", nargs='+', help="Paths to input HTML file.")
parser.add_argument("paths", nargs="+", help="Paths to input HTML file.")
args = parser.parse_args()

paths: List[str] = args.paths

path_to_cache_dir: str = (
args.cache_dir
if args.cache_dir is not None
else (
os.path.join(
Path.home(), ".hpdf", "chromedriver"
)
)
else (os.path.join(Path.home(), ".hpdf", "chromedriver"))
)
driver = create_webdriver(args.chromedriver, path_to_cache_dir)

Expand All @@ -265,7 +263,9 @@ def exit_handler():
print("html2pdf: exit handler: quitting the ChromeDriver.") # noqa: T201
driver.quit()

assert len(paths) % 2 == 0, f"Expecting an even number of input/output path arguments: {paths}."
assert len(paths) % 2 == 0, (
f"Expecting an even number of input/output path arguments: {paths}."
)
for current_pair_idx in range(0, 2, len(paths)):
path_to_input_html = paths[current_pair_idx]
path_to_output_pdf = paths[current_pair_idx + 1]
Expand Down
6 changes: 6 additions & 0 deletions requirements.development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ toml
packaging
setuptools

#
# Lint
#
mypy>=0.910
ruff>=0.9

#
# Integration tests
#
Expand Down
103 changes: 103 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
target-version = "py38"
line-length = 80

[lint]
select = [
"A",
"ARG",
"B",
# "BLE",
"C",
"C4",
# "C90" mccabe

# Not clear if this check is useful. Too many commas everywhere.
# "COM",

# "D",
# "DTZ",
# "E",
"EXE",
"ERA",
"F",
# "FBT",
"G",
"I",
"ISC",
"ICN",
# "N",
"PGH",
# "PIE",
"PL", # Pylint
# "PT", # pytest
# "PTH",
"Q", # Quotes
# "RET",
# "RUF",
# "S",
# "SIM",
# "SLF",
"T20",
# "TCH",
# "TRY",
"UP",
"W",
"YTT",
]

ignore = [
# TBD: Unnecessary `list` comprehension (rewrite using `list()`)
"C416",
# TBD: Unnecessary `map` usage (rewrite using a `list` comprehension)
"C417",

# X is too complex
"C901",

"E501", # (line length violations).

# A warning by ruff format:
# warning: The following rules may cause conflicts when used with the formatter: `ISC001`.
"ISC001",

# Too many return statements
"PLR0911",

# Too many branches
"PLR0912",

# Too many arguments in function definition
"PLR0913",

# Too many statements
"PLR0915",

# TBD: Enable: Use `sys.exit()` instead of `exit`
"PLR1722",

# Magic value used in comparison
"PLR2004",

# Use `elif` instead of `else` then `if`, to reduce indentation
"PLR5501",

"UP035", # [*] Import from `collections.abc` instead: `Iterator`
"UP038", # [*] Use `X | Y` in `isinstance` call instead of `(X, Y)` (conflict with Pylint)
]

# Avoid trying to fix flake8-bugbear (`B`) violations.
unfixable = ["B"]

# Skip non UTF-8 test files
# exclude = ["tests/**/invalid_file*"]

# B008 Do not perform function calls in argument defaults.
# The call is performed only once at function definition time.

[lint.per-file-ignores]
# "foo.py" = ["B008"]

# Some of our helpers have deliberatly the name of a standard library module

[lint.flake8-builtins]
builtins-allowed-modules = ["math", "pickle", "string"]
63 changes: 59 additions & 4 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,76 @@ def bootstrap(context):

@task
def build(context):
run_invoke(context, "cd submodules/html2pdf && npm install && npm run build")
run_invoke(
context, "cd submodules/html2pdf && npm install && npm run build"
)


@task
def format_readme(context):
run_invoke(context, """
run_invoke(
context,
"""
prettier
--write --print-width 80 --prose-wrap always --parser=markdown
README.md
""")
""",
)


@task
def lint_ruff_format(context):
result: invoke.runners.Result = run_invoke(
context,
"""
ruff
format
*.py
hpdf/
tests/integration/
""",
)
# Ruff always exits with 0, so we handle the output.
if "reformatted" in result.stdout:
print("invoke: ruff format found issues") # noqa: T201
result.exited = 1
raise invoke.exceptions.UnexpectedExit(result)


@task(aliases=["lr"])
def lint_ruff(context):
run_invoke(
context,
"""
ruff check *.py hpdf/ --fix --cache-dir build/ruff
""",
)


@task(aliases=["lm"])
def lint_mypy(context):
# These checks do not seem to be useful:
# - import
# - misc
run_invoke(
context,
"""
mypy hpdf/
--show-error-codes
--disable-error-code=import
--disable-error-code=misc
--cache-dir=build/mypy
--strict
--python-version=3.8
""",
)


@task(aliases=["l"])
def lint(context):
pass
lint_ruff_format(context)
lint_ruff(context)
lint_mypy(context)


@task
Expand Down
12 changes: 3 additions & 9 deletions tests/integration/check_exists.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,19 @@
else:
if os.path.exists(args.input_path):
print( # noqa: T201
"error: expected path to not exist, but it does: {}".format(
args.input_path
),
f"error: expected path to not exist, but it does: {args.input_path}",
file=sys.stderr,
)
exit(1)
if args.file and os.path.isfile(args.input_path):
print( # noqa: T201
"error: expected path to not exist, but is a file: {}".format(
args.input_path
),
f"error: expected path to not exist, but is a file: {args.input_path}",
file=sys.stderr,
)
exit(1)
if args.dir and os.path.isdir(args.input_path):
print( # noqa: T201
"error: expected path to not exist, but is a directory: {}".format(
args.input_path
),
f"error: expected path to not exist, but is a directory: {args.input_path}",
file=sys.stderr,
)
exit(1)
Expand Down
4 changes: 1 addition & 3 deletions tests/integration/expect_exit.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@
unexpected_exit_code = process.returncode != expected_exit_code
if unexpected_exit_code:
print( # noqa: T201
"error: expect_exit: expected exit code: {}, actual: {}".format(
expected_exit_code, process.returncode
)
f"error: expect_exit: expected exit code: {expected_exit_code}, actual: {process.returncode}"
)

unexpected_content = expect_no_content and len(stdout) > 0
Expand Down