From cd1fe7191c30c288d708eb657d96a598e8f779fd Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Tue, 8 Jun 2021 15:58:23 -0400 Subject: [PATCH] Add more tests of custom formatter --- src/tinuous/util.py | 12 +++++++++++- test/test_util.py | 37 ++++++++++++++++++++++++++++++++++++- tox.ini | 1 + 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/tinuous/util.py b/src/tinuous/util.py index 9aa1883..fd62238 100644 --- a/src/tinuous/util.py +++ b/src/tinuous/util.py @@ -48,6 +48,14 @@ def iterfiles(dirpath: Path) -> Iterator[Path]: class LazySlicingFormatter(Formatter): + """ + A `string.Formatter` subclass that: + + - accepts a second set of format kwargs that can refer to the main kwargs + or each other and are only templated as needed + - supports indexing strings & other sequences with slices + """ + def __init__(self, var_defs: Dict[str, str]): self.var_defs: Dict[str, str] = var_defs self.expanded_vars: Dict[str, str] = {} @@ -73,7 +81,9 @@ def get_field( ) -> Any: m = re.match(r"\w+", field_name) assert m, f"format field name {field_name!r} does not start with arg_name" - key = m.group() + key: Union[int, str] = m.group() + if key.isdigit(): + key = int(key) obj = self.get_value(key, args, kwargs) s = field_name[m.end() :] while s: diff --git a/test/test_util.py b/test/test_util.py index 351142a..4ccc510 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -1,5 +1,8 @@ +from types import SimpleNamespace +from typing import Any, Dict import pytest -from tinuous.util import expand_template, parse_slice +from pytest_mock import MockerFixture +from tinuous.util import LazySlicingFormatter, expand_template, parse_slice def test_expand_template() -> None: @@ -58,3 +61,35 @@ def test_expand_template_unused_bad() -> None: ) def test_parse_slice(s: str, sl: slice) -> None: assert parse_slice(s) == sl + + +@pytest.mark.parametrize( + "fmt,args,kwargs,result", + [ + ("{0}", ["foo"], {}, "foo"), + ( + "{foo.bar.baz}", + [], + {"foo": SimpleNamespace(bar=SimpleNamespace(baz="quux"))}, + "quux", + ), + ("{foo[1][2]}", [], {"foo": ["abc", "def", "ghi"]}, "f"), + ("{foo[bar][baz]}", [], {"foo": {"bar": {"baz": "quux"}}}, "quux"), + ], +) +def test_lazy_slicing_formatter_basics( + fmt: str, args: list, kwargs: Dict[str, Any], result: str +) -> None: + assert LazySlicingFormatter({}).format(fmt, *args, **kwargs) == result + + +def test_lazy_slicing_formatter_undef_key() -> None: + with pytest.raises(KeyError): + LazySlicingFormatter({}).format("{foo}", bar=42) + + +def test_lazy_slicing_formatter_var_reuse(mocker: MockerFixture) -> None: + fmter = LazySlicingFormatter({"foo": "bar"}) + spy = mocker.spy(fmter, "format") + assert fmter.format("-{foo}-{foo}-") == "-bar-bar-" + assert spy.call_args_list == [mocker.call("-{foo}-{foo}-"), mocker.call("bar")] diff --git a/tox.ini b/tox.ini index 03fdb72..f6a9bad 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,7 @@ minversion = 3.3.0 deps = pytest~=6.0 pytest-cov~=2.0 + pytest-mock~=3.0 commands = # Basic smoketest: tinuous --help