Skip to content

Commit

Permalink
pypyr.steps.assert custom msg
Browse files Browse the repository at this point in the history
Allow user to set custom error msg when assert evals False.
Closes #295
  • Loading branch information
yaythomas committed Oct 4, 2022
1 parent a7d4b7d commit 458413a
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 26 deletions.
62 changes: 36 additions & 26 deletions pypyr/steps/assert.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
# logger means the log level will be set correctly
logger = logging.getLogger(__name__)

sentinel = object()


def run_step(context):
"""Assert that something is True or equal to something else.
Expand All @@ -17,6 +19,7 @@ def run_step(context):
- this (any): mandatory. If assert['equals'] not specified,
eval as boolean.
- equals (any): optional. Any type.
- msg (str): optional. Error message if assert evaluates False.
Takes one of three input forms:
assert: evaluate me
Expand Down Expand Up @@ -56,29 +59,33 @@ def run_step(context):

is_equals_there = False
is_this_there = False
msg = sentinel
if isinstance(assert_context, dict):
is_this_there = 'this' in assert_context
if is_this_there:
assert_this = assert_context['this']
else:
assert_this = assert_context
assert_this = assert_context.get('this', sentinel)
assert_equals = assert_context.get('equals', sentinel)
msg = assert_context.get('msg', sentinel)

is_equals_there = 'equals' in assert_context
if is_equals_there:
if not is_this_there:
if assert_equals is not sentinel:
is_equals_there = True
if assert_this is sentinel:
raise KeyNotInContextError(
"you have to set assert.this to use assert.equals.")
assert_equals = assert_context['equals']
else:
is_this_there = True

# compare assertThis to assertEquals
logger.debug("comparing assert['this'] to assert['equals'].")
assert_result = (assert_this == assert_equals)
else:
# nothing to compare means treat assert or assert.this as a bool.
if is_this_there:
logger.debug("evaluating assert['this'] as a boolean.")
else:
if assert_this is sentinel:
logger.debug("assert is a dict but contains no `this`. "
"evaluating assert value as a boolean.")
assert_this = assert_context
else:
is_this_there = True
logger.debug("evaluating assert['this'] as a boolean.")

assert_result = cast_to_bool(assert_this)
else:
# assert key has a non-dict value so eval directly as a bool.
Expand All @@ -88,20 +95,23 @@ def run_step(context):
logger.info("assert evaluated to %s", assert_result)

if not assert_result:
if is_equals_there:
# emit type to help user, but not the actual field contents.
type_this = type(assert_this).__name__
type_equals = type(assert_equals).__name__
error_text = (
f"assert assert['this'] is of type {type_this} "
f"and does not equal assert['equals'] of type {type_equals}.")
else:
og_assert = (context['assert']['this']
if is_this_there else context['assert'])
# original literal hard-coded in pipe, so presumably not a
# sensitive value.
error_text = (
f"assert {og_assert} evaluated to False.")
error_text = msg
if msg is sentinel:
# build a default err msg when input didn't specify an err msg
if is_equals_there:
# emit type to help user, but not the actual field contents.
type_this = type(assert_this).__name__
type_equals = type(assert_equals).__name__
error_text = (
f"assert assert['this'] is of type {type_this} "
"and does not equal assert['equals'] of type "
f"{type_equals}.")
else:
og_assert = (context['assert']['this']
if is_this_there else context['assert'])
# original literal hard-coded in pipe, so presumably not a
# sensitive value.
error_text = f"assert {og_assert} evaluated to False."
raise AssertionError(error_text)

logger.debug("done")
21 changes: 21 additions & 0 deletions tests/unit/pypyr/steps/assert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,30 @@ def test_assert_raises_on_assertthis_not_equals_dict_to_dict():
"assert assert['this'] is of type dict and does "
"not equal assert['equals'] of type dict.")


def test_assert_raises_on_assertthis_with_msg():
"""Assert this uses custom error message."""
context = Context({'assert': {'this': False,
'msg': "arb"}})
with pytest.raises(AssertionError) as err_info:
assert_step.run_step(context)

assert str(err_info.value) == "arb"

# region substitutions


def test_assert_raises_on_assertthis_with_msg_substitutions():
"""Assert this uses custom error message."""
context = Context({'k1': 'arb',
'assert': {'this': False,
'msg': 'x{k1}x'}})
with pytest.raises(AssertionError) as err_info:
assert_step.run_step(context)

assert str(err_info.value) == "xarbx"


def test_assert_passes_on_bare_assert_substitutions():
"""Assert bare boolean substitutes to int passes."""
context = Context({'k1': 33, 'assert': '{k1}'})
Expand Down

0 comments on commit 458413a

Please sign in to comment.