Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-406 #407

Merged
merged 7 commits into from
Feb 7, 2024
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
6 changes: 3 additions & 3 deletions docs/sections/user_guide/cli/mode_template.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ The examples in this section use atparse-formatted template file ``atparse.txt``
.. code-block:: text

$ uw template translate --input-file atparse.txt
{{greeting}}, {{recipient}}!
{{ greeting }}, {{ recipient }}!
maddenp-noaa marked this conversation as resolved.
Show resolved Hide resolved

Shell redirection via ``|``, ``>``, et al. may also be used to stream output to a file, another process, etc.

Expand All @@ -287,11 +287,11 @@ The examples in this section use atparse-formatted template file ``atparse.txt``

.. code-block:: jinja

{{greeting}}, {{recipient}}!
{{ greeting }}, {{ recipient }}!

* With the ``--dry-run`` flag specified, nothing is written to ``stdout`` (or to a file if ``--output-file`` is specified), but a report of what would have been written is logged to ``stderr``:

.. code-block:: text

$ uw template translate --input-file atparse.txt --dry-run
[2024-01-03T16:41:13] INFO {{greeting}}, {{recipient}}!
[2024-02-06T21:53:43] INFO {{ greeting }}, {{ recipient }}!
19 changes: 6 additions & 13 deletions src/uwtools/config/atparse_to_jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
"""

import re
from typing import IO, Any, Generator

from uwtools.logging import log
from uwtools.utils.file import OptionalPath, readable, writable
Expand All @@ -23,21 +22,15 @@ def convert(
:param dry_run: Run in dry-run mode?
"""

def lines() -> Generator[str, Any, Any]:
with readable(input_file) as f_in:
for line in f_in.read().strip().split("\n"):
yield _replace(line.strip())

def write(f_out: IO) -> None:
for line in lines():
print(_replace(line.strip()), file=f_out)

with readable(input_file) as f:
lines = f.readlines()
jinja2 = "".join(_replace(line) for line in lines)
if dry_run:
for line in lines():
for line in jinja2.removesuffix("\n").split("\n"):
log.info(line)
else:
with writable(output_file) as f:
write(f)
f.write(jinja2)


def _replace(atline: str) -> str:
Expand All @@ -54,5 +47,5 @@ def _replace(atline: str) -> str:
# Set maxsplits to 1 so only first ] is captured, which should be the
# bracket closing @[.
after_atparse = atline.split("@[", 1)[1].split("]", 1)[1]
atline = "".join([before_atparse, "{{", within_atparse, "}}", after_atparse])
atline = "".join([before_atparse, "{{ ", within_atparse, " }}", after_atparse])
return atline
87 changes: 61 additions & 26 deletions src/uwtools/tests/config/test_atparse_to_jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,83 +18,118 @@


@fixture
def atparselines():
return ["@[greeting] to the @[subject]", "@[flowers] are @[color]"]
def txt_atparse():
return """
@[greeting] to the @[subject]
@[flowers] are @[color]
""".strip()


@fixture
def atparsefile(atparselines, tmp_path):
path = tmp_path / "atparse.txt"
with open(path, "w", encoding="utf-8") as f:
for line in atparselines:
print(line, file=f)
return path
def txt_jinja2():
return """
{{ greeting }} to the {{ subject }}
{{ flowers }} are {{ color }}
""".strip()


@fixture
def jinja2txt():
return "{{greeting}} to the {{subject}}\n{{flowers}} are {{color}}\n"
def atparsefile(txt_atparse, tmp_path):
path = tmp_path / "atparse.txt"
with open(path, "w", encoding="utf-8") as f:
print(txt_atparse, file=f)
return path


# Test functions


def test_convert_input_file_to_output_file(atparsefile, capsys, jinja2txt, tmp_path):
def test_convert_input_file_to_output_file(atparsefile, capsys, txt_jinja2, tmp_path):
outfile = tmp_path / "outfile"
atparse_to_jinja2.convert(input_file=atparsefile, output_file=outfile)
with open(outfile, "r", encoding="utf-8") as f:
assert f.read() == jinja2txt
assert f.read().strip() == txt_jinja2
streams = capsys.readouterr()
assert not streams.err
assert not streams.out


def test_convert_input_file_to_logging(atparsefile, caplog, capsys, jinja2txt, tmp_path):
def test_convert_input_file_to_logging(atparsefile, caplog, capsys, txt_jinja2, tmp_path):
log.setLevel(logging.INFO)
outfile = tmp_path / "outfile"
atparse_to_jinja2.convert(input_file=atparsefile, dry_run=True)
streams = capsys.readouterr()
assert "\n".join(record.message for record in caplog.records) == jinja2txt.strip()
assert "\n".join(record.message for record in caplog.records).strip() == txt_jinja2
assert not streams.out
assert not outfile.is_file()


def test_convert_input_file_to_stdout(atparsefile, capsys, jinja2txt):
def test_convert_input_file_to_stdout(atparsefile, capsys, txt_jinja2):
atparse_to_jinja2.convert(input_file=atparsefile)
streams = capsys.readouterr()
assert not streams.err
assert streams.out == jinja2txt
assert streams.out.strip() == txt_jinja2


def test_convert_preserve_whitespace(tmp_path):
maddenp-noaa marked this conversation as resolved.
Show resolved Hide resolved
atparse = """

@[first_entry]
@[second_entry]
@[third_entry]
@[fourth_entry]

@[fifth_entry] @[sixth_entry]

"""
maddenp-noaa marked this conversation as resolved.
Show resolved Hide resolved
infile = tmp_path / "atparse"
with open(infile, "w", encoding="utf-8") as f:
f.write(atparse)
outfile = tmp_path / "jinja2"
atparse_to_jinja2.convert(input_file=infile, output_file=outfile)
expected = """

{{ first_entry }}
{{ second_entry }}
{{ third_entry }}
{{ fourth_entry }}

{{ fifth_entry }} {{ sixth_entry }}

"""
with open(outfile, "r", encoding="utf-8") as f:
assert f.read() == expected


def test_convert_stdin_to_file(atparselines, capsys, jinja2txt, tmp_path):
def test_convert_stdin_to_file(txt_atparse, capsys, txt_jinja2, tmp_path):
outfile = tmp_path / "outfile"
_stdinproxy.cache_clear()
with patch.object(sys, "stdin", new=StringIO("\n".join(atparselines))):
with patch.object(sys, "stdin", new=StringIO(txt_atparse)):
atparse_to_jinja2.convert(output_file=outfile)
with open(outfile, "r", encoding="utf-8") as f:
assert f.read() == jinja2txt
assert f.read().strip() == txt_jinja2
streams = capsys.readouterr()
assert not streams.err
assert not streams.out


def test_convert_stdin_to_logging(atparselines, caplog, jinja2txt, tmp_path):
def test_convert_stdin_to_logging(txt_atparse, caplog, txt_jinja2, tmp_path):
log.setLevel(logging.INFO)
outfile = tmp_path / "outfile"
_stdinproxy.cache_clear()
with patch.object(sys, "stdin", new=StringIO("\n".join(atparselines))):
with patch.object(sys, "stdin", new=StringIO(txt_atparse)):
atparse_to_jinja2.convert(output_file=outfile, dry_run=True)
assert "\n".join(record.message for record in caplog.records) == jinja2txt.strip()
assert "\n".join(record.message for record in caplog.records) == txt_jinja2
assert not outfile.is_file()


def test_convert_stdin_to_stdout(atparselines, capsys, jinja2txt):
def test_convert_stdin_to_stdout(txt_atparse, capsys, txt_jinja2):
_stdinproxy.cache_clear()
with patch.object(sys, "stdin", new=StringIO("\n".join(atparselines))):
with patch.object(sys, "stdin", new=StringIO(txt_atparse)):
atparse_to_jinja2.convert()
streams = capsys.readouterr()
assert not streams.err
assert streams.out == jinja2txt
assert streams.out.strip() == txt_jinja2


def test__replace():
Expand All @@ -103,4 +138,4 @@ def test__replace():
assert atparse_to_jinja2._replace(line_without) == line_without
# A line with atparse syntax should be returned updated to Jinja2 syntax:
line_with = "@[greeting] to the @[subject]"
assert atparse_to_jinja2._replace(line_with) == "{{greeting}} to the {{subject}}"
assert atparse_to_jinja2._replace(line_with) == "{{ greeting }} to the {{ subject }}"
Loading