Skip to content

Commit

Permalink
Fixed Issue [#40](#40)
Browse files Browse the repository at this point in the history
Fixed badly-formatted dates in the changelog
Added ability for pySHACL to track and monitor its evaluation path during validation
- This allows for the validator to detect two different scenarios:
  - A recursive shape has triggered an infinitely-recursive validation, back out
  - Evaluation Path too deep (error generated, prevents python recursion depth errors)
Added a test for Issue #40
  • Loading branch information
ashleysommer committed Jan 31, 2020
1 parent 96a1b06 commit b85ce14
Show file tree
Hide file tree
Showing 19 changed files with 910 additions and 407 deletions.
356 changes: 185 additions & 171 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyshacl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
from pyshacl.validate import validate, Validator

# version compliant with https://www.python.org/dev/peps/pep-0440/
__version__ = '0.11.3.post1'
__version__ = '0.11.4'

__all__ = ['validate', 'Validator', '__version__']
1 change: 1 addition & 0 deletions pyshacl/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
parser.add_argument('-o', '--output', dest='output', nargs='?', type=argparse.FileType('w'),
help='Send output to a file (defaults to stdout).',
default=sys.stdout)
#parser.add_argument('-h', '--help', action="help", help='Show this help text.')


def main():
Expand Down
29 changes: 28 additions & 1 deletion pyshacl/constraints/constraint_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,36 @@ def shacl_constraint_class(cls):
return NotImplementedError() # pragma: no cover

@abc.abstractmethod
def evaluate(self, target_graph, focus_value_nodes):
def evaluate(self, target_graph, focus_value_nodes, _evaluation_path):
return NotImplementedError() # pragma: no cover

def __str__(self):
c_name = str(self.__class__.__name__)
shape_id = str(self.shape)
return "<{} on {}>".format(c_name, shape_id)

def recursion_triggers(self, _evaluation_path):
shape = self.shape
eval_length = len(_evaluation_path)
maybe_recursive = []
if eval_length >= 6:
_shape, _self = _evaluation_path[eval_length - 2:]
if _shape is not shape or _self is not self:
raise RuntimeError("Bad evaluation path construction")
seen_before = [i for i, x in enumerate(_evaluation_path[:eval_length-2]) if x is shape] #_evaluation_path.index(shape, 0, eval_length - 2)
for s in seen_before:
for i, p in enumerate(_evaluation_path[s + 1:-2]):
if isinstance(p, ConstraintComponent):
if p.shape is shape and p.__class__ == self.__class__:
try:
next_shape = _evaluation_path[s + 1 + i + 1]
maybe_recursive.append(next_shape)
except IndexError:
pass
break
return maybe_recursive


def make_v_result_description(self, datagraph, focus_node, severity, value_node=None, result_path=None,
constraint_component=None, source_constraint=None, extra_messages=None):
"""
Expand Down
4 changes: 2 additions & 2 deletions pyshacl/constraints/core/cardinality_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def constraint_name(cls):
def shacl_constraint_class(cls):
return SH_MinCountConstraintComponent

def evaluate(self, target_graph, focus_value_nodes):
def evaluate(self, target_graph, focus_value_nodes, _evaluation_path):
"""
:type focus_value_nodes: dict
Expand Down Expand Up @@ -132,7 +132,7 @@ def constraint_name(cls):
def shacl_constraint_class(cls):
return SH_MaxCountConstraintComponent

def evaluate(self, target_graph, focus_value_nodes):
def evaluate(self, target_graph, focus_value_nodes, _evaluation_path):
"""
:type focus_value_nodes: dict
Expand Down
Loading

0 comments on commit b85ce14

Please sign in to comment.