Skip to content

Commit

Permalink
Avoid ResourceWarning: unclosed file (#395)
Browse files Browse the repository at this point in the history
A number of such warnings showed up when running e.g.
PYTHONWARNINGS=always python build_examples.py
PYTHONWARNINGS=always wireviz ../../examples/demo0?.yml
See #309 (comment)

Fix: All open() calls should be in a "with open() as x" statement
to ensure closing the file when exiting the block in any way.
Otherwise, use the new file_read_text() or file_write_text() functions
to read or write the whole utf-8 text file and closing it.
  • Loading branch information
kvid committed Jul 5, 2024
1 parent ee1bd78 commit 7b52adf
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 17 deletions.
5 changes: 2 additions & 3 deletions src/wireviz/Harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@
)
from wireviz.wv_helper import (
awg_equiv,
file_write_text,
flatten2d,
is_arrow,
mm2_equiv,
open_file_read,
open_file_write,
tuplelist2tsv,
)
from wireviz.wv_html import generate_html_output
Expand Down Expand Up @@ -693,7 +692,7 @@ def output(
# BOM output
bomlist = bom_list(self.bom())
if "tsv" in fmt:
open_file_write(f"{filename}.bom.tsv").write(tuplelist2tsv(bomlist))
file_write_text(f"{filename}.bom.tsv", tuplelist2tsv(bomlist))
if "csv" in fmt:
# TODO: implement CSV output (preferrably using CSV library)
print("CSV output is not yet supported")
Expand Down
4 changes: 2 additions & 2 deletions src/wireviz/wireviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
from wireviz.Harness import Harness
from wireviz.wv_helper import (
expand,
file_read_text,
get_single_key_and_value,
is_arrow,
open_file_read,
smart_file_resolve,
)

Expand Down Expand Up @@ -409,7 +409,7 @@ def _get_yaml_data_and_path(inp: Union[str, Path, Dict]) -> (Dict, Path):
try:
yaml_path = Path(inp).expanduser().resolve(strict=True)
# if no FileNotFoundError exception happens, get file contents
yaml_str = open_file_read(yaml_path).read()
yaml_str = file_read_text(yaml_path)
except (FileNotFoundError, OSError, ValueError) as e:
# if inp is a long YAML string, Pathlib will normally raise
# FileNotFoundError or OSError(errno = ENAMETOOLONG) when
Expand Down
6 changes: 3 additions & 3 deletions src/wireviz/wv_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import wireviz.wireviz as wv
from wireviz import APP_NAME, __version__
from wireviz.wv_helper import open_file_read
from wireviz.wv_helper import file_read_text

format_codes = {
# "c": "csv",
Expand Down Expand Up @@ -111,7 +111,7 @@ def wireviz(file, format, prepend, output_dir, output_name, version):
raise Exception(f"File does not exist:\n{prepend_file}")
print("Prepend file:", prepend_file)

prepend_input += open_file_read(prepend_file).read() + "\n"
prepend_input += file_read_text(prepend_file) + "\n"
else:
prepend_input = ""

Expand All @@ -130,7 +130,7 @@ def wireviz(file, format, prepend, output_dir, output_name, version):
"Output file: ", f"{Path(_output_dir / _output_name)}.{output_formats_str}"
)

yaml_input = open_file_read(file).read()
yaml_input = file_read_text(file)
file_dir = file.parent

yaml_input = prepend_input + yaml_input
Expand Down
21 changes: 17 additions & 4 deletions src/wireviz/wv_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,18 +113,31 @@ def clean_whitespace(inp):


def open_file_read(filename):
"""Open utf-8 encoded text file for reading - remember closing it when finished"""
# TODO: Intelligently determine encoding
return open(filename, "r", encoding="UTF-8")


def open_file_write(filename):
"""Open utf-8 encoded text file for writing - remember closing it when finished"""
return open(filename, "w", encoding="UTF-8")


def open_file_append(filename):
"""Open utf-8 encoded text file for appending - remember closing it when finished"""
return open(filename, "a", encoding="UTF-8")


def file_read_text(filename: str) -> str:
"""Read utf-8 encoded text file, close it, and return the text"""
return Path(filename).read_text(encoding="utf-8")


def file_write_text(filename: str, text: str) -> int:
"""Write utf-8 encoded text file, close it, and return the number of characters written"""
return Path(filename).write_text(text, encoding="utf-8")


def is_arrow(inp):
"""
Matches strings of one or multiple `-` or `=` (but not mixed)
Expand All @@ -144,10 +157,10 @@ def aspect_ratio(image_src):
try:
from PIL import Image

image = Image.open(image_src)
if image.width > 0 and image.height > 0:
return image.width / image.height
print(f"aspect_ratio(): Invalid image size {image.width} x {image.height}")
with Image.open(image_src) as image:
if image.width > 0 and image.height > 0:
return image.width / image.height
print(f"aspect_ratio(): Invalid image size {image.width} x {image.height}")
# ModuleNotFoundError and FileNotFoundError are the most expected, but all are handled equally.
except Exception as error:
print(f"aspect_ratio(): {type(error).__name__}: {error}")
Expand Down
10 changes: 5 additions & 5 deletions src/wireviz/wv_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from wireviz.svgembed import data_URI_base64
from wireviz.wv_gv_html import html_line_breaks
from wireviz.wv_helper import (
file_read_text,
file_write_text,
flatten2d,
open_file_read,
open_file_write,
smart_file_resolve,
)

Expand All @@ -35,14 +35,14 @@ def generate_html_output(
# fall back to built-in simple template if no template was provided
templatefile = Path(__file__).parent / "templates/simple.html"

html = open_file_read(templatefile).read()
html = file_read_text(templatefile)

# embed SVG diagram (only if used)
def svgdata() -> str:
return re.sub(
"^<[?]xml [^?>]*[?]>[^<]*<!DOCTYPE [^>]*>",
"<!-- XML and DOCTYPE declarations from SVG file removed -->",
open_file_read(f"{filename}.tmp.svg").read(),
file_read_text(f"{filename}.tmp.svg"),
1,
)

Expand Down Expand Up @@ -128,4 +128,4 @@ def replacement_if_used(key: str, func: Callable[[], str]) -> None:
pattern = re.compile("|".join(replacements_escaped))
html = pattern.sub(lambda match: replacements[match.group(0)], html)

open_file_write(f"{filename}.html").write(html)
file_write_text(f"{filename}.html", html)

0 comments on commit 7b52adf

Please sign in to comment.