Skip to content

Commit

Permalink
feat(#26): support disable operator chaining
Browse files Browse the repository at this point in the history
  • Loading branch information
h2non committed Mar 21, 2017
1 parent bc5af37 commit 9cdf427
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 28 deletions.
19 changes: 8 additions & 11 deletions grappa/assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,29 @@ def __getattr__(self, name):
Overloads attribute accessor in order to load the assertion
operator dynamically.
"""
# Match chain aliases
# Match operator aliases for chaining
if self._match_alias(name):
self._test._engine.add_keyword(name)
return self

# Match suboperator
# Trigger operator on_access operator event method, if available
if self.is_on_access():
self._on_access()

# Match suboperator, if needed
suboperator = self._match_suboperator(name)
if suboperator:
# Register keyword for suboperator
self._test._engine.add_keyword(name)

# Trigger operator on_access event method, if available
if self.is_on_access():
self._on_access()

# Trigger suboperator on_access event method, if available
if suboperator.is_on_access():
suboperator._on_access()

# Return suboperator proxy
return suboperator

# Trigger operator on_access operator event method, if available
if hasattr(self._op, 'on_access') and not self._accessed:
self._on_access()

# Fallback to parent assertions
# Delegate access to global assertion instance
return getattr(self._test, name)

def __ror__(self, value):
Expand Down
3 changes: 3 additions & 0 deletions grappa/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class Operator(object):
# Chain alias keywords
aliases = tuple()

# Enable/disable operator chaining
chainable = True

# Stores assertion error tips
information = []

Expand Down
3 changes: 3 additions & 0 deletions grappa/operators/none.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class NoneOperator(Operator):
# Operator keywords
operators = ('none',)

# Disable chanined calls
chainable = False

# Expected message templates
expected_message = Operator.Dsl.Message(
'a value that is exactly equal to "None"',
Expand Down
19 changes: 2 additions & 17 deletions grappa/reporters/assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def run(self, error):
subject = self.normalize(
self.from_operator('subject', self.ctx.subject), use_raw=False)

# List of keyword operators DSL
# List of used keyword operators
keywords = []
for keyword in self.ctx.keywords:
if type(keyword) is dict and 'operator' in keyword:
Expand All @@ -50,20 +50,5 @@ def run(self, error):
# Assertion expression value
assertion = self.template.format(subject, self.ctx.style, operators)

# # Expected value
# expected = self.from_operator('expected', self.ctx.expected)
#
# if isinstance(expected, tuple):
# if len(expected) == 0:
# expected = empty
# if len(expected) == 1:
# expected = expected[0]
#
# # Add expected value template, if needed
# if expected is not empty:
# if isinstance(expected, (tuple, list)):
# expected = ', '.join(str(i) for i in expected)
# assertion += ' "{}"'.format(
# self.normalize(expected, use_raw=False))

# Return assertion formatted sentence
return assertion
10 changes: 10 additions & 0 deletions grappa/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ def assertion(subject):
return AssertionProxy(self, operator, wrapper)

def resolve(self, name):
# Check if should stop the call chain
if self.ctx.stop_chain:
raise RuntimeError(
'grappa: test operator "{}" does not allow '
'chained calls.'.format(self.ctx.stop_chain.operator_name))

# Find an assertion operator by name
operator = self.engine.find_operator(name)
if not operator:
Expand All @@ -77,6 +83,10 @@ def resolve(self, name):
# Create operator instance with current context
operator = operator(context=self.ctx, operator_name=name)

# Check chainable operator logic is enabled
if getattr(operator, 'chainable', True) is False:
self.ctx.stop_chain = operator

# Reset context sequence
if self.ctx.reset:
self.engine.reset_keywords()
Expand Down
3 changes: 3 additions & 0 deletions tests/operators/none_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ def test_should_none(should):
None | should.be.none
'foo' | should.not_be.none

with pytest.raises(RuntimeError):
'foo' | should.not_be.none.to.be.equal(None)

with pytest.raises(AssertionError):
False | should.be.none

Expand Down

0 comments on commit 9cdf427

Please sign in to comment.