From 20679d9fd281043166010ef4d6bf3cd88a84e50f Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Fri, 4 Oct 2024 12:42:02 -0400 Subject: [PATCH 1/4] Type most constraint inits This first mostly-mechanically implemented pass didn't type `__init__` for these classes, because of extra signature review becoming entailed: * `ClosedConstraintComponent` * `InConstraintComponent` * `StringBasedConstraintBase` Signed-off-by: Alex Nelson --- pyshacl/constraints/advanced/__init__.py | 2 +- pyshacl/constraints/constraint_component.py | 2 +- pyshacl/constraints/core/cardinality_constraints.py | 5 +++-- pyshacl/constraints/core/logical_constraints.py | 9 +++++---- pyshacl/constraints/core/other_constraints.py | 3 ++- pyshacl/constraints/core/property_pair_constraints.py | 9 +++++---- pyshacl/constraints/core/string_based_constraints.py | 11 ++++++----- pyshacl/constraints/core/value_constraints.py | 7 ++++--- pyshacl/constraints/core/value_range_constraints.py | 9 +++++---- .../constraints/sparql/sparql_based_constraints.py | 3 ++- 10 files changed, 34 insertions(+), 26 deletions(-) diff --git a/pyshacl/constraints/advanced/__init__.py b/pyshacl/constraints/advanced/__init__.py index 841bdc9..cdeac73 100644 --- a/pyshacl/constraints/advanced/__init__.py +++ b/pyshacl/constraints/advanced/__init__.py @@ -25,7 +25,7 @@ class ExpressionConstraint(ConstraintComponent): shacl_constraint_component = SH_ExpressionConstraintComponent - def __init__(self, shape: 'Shape'): + def __init__(self, shape: 'Shape') -> None: super(ExpressionConstraint, self).__init__(shape) self.expr_nodes = list(self.shape.objects(SH_expression)) if len(self.expr_nodes) < 1: diff --git a/pyshacl/constraints/constraint_component.py b/pyshacl/constraints/constraint_component.py index a70452e..2787cd6 100644 --- a/pyshacl/constraints/constraint_component.py +++ b/pyshacl/constraints/constraint_component.py @@ -58,7 +58,7 @@ class ConstraintComponent(object, metaclass=abc.ABCMeta): shacl_constraint_component: URIRef = URIRef("urn:notimplemented") - def __init__(self, shape: 'Shape'): + def __init__(self, shape: 'Shape') -> None: """ :param shape: diff --git a/pyshacl/constraints/core/cardinality_constraints.py b/pyshacl/constraints/core/cardinality_constraints.py index 18b63d2..7ed272e 100644 --- a/pyshacl/constraints/core/cardinality_constraints.py +++ b/pyshacl/constraints/core/cardinality_constraints.py @@ -12,6 +12,7 @@ from pyshacl.errors import ConstraintLoadError from pyshacl.pytypes import GraphLike, RDFNode, SHACLExecutor from pyshacl.rdfutil import stringify_node +from pyshacl.shape import Shape XSD_integer = XSD.integer SH_minCount = SH.minCount @@ -32,7 +33,7 @@ class MinCountConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_MinCountConstraintComponent - def __init__(self, shape, min_count_objects: Optional[List[RDFNode]] = None): + def __init__(self, shape: Shape, min_count_objects: Optional[List[RDFNode]] = None) -> None: super(MinCountConstraintComponent, self).__init__(shape) if min_count_objects is None: min_count = list(self.shape.objects(SH_minCount)) @@ -118,7 +119,7 @@ class MaxCountConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_MaxCountConstraintComponent - def __init__(self, shape, max_count_objects: Optional[List[RDFNode]] = None): + def __init__(self, shape: Shape, max_count_objects: Optional[List[RDFNode]] = None) -> None: super(MaxCountConstraintComponent, self).__init__(shape) if max_count_objects is None: max_count = list(self.shape.objects(SH_maxCount)) diff --git a/pyshacl/constraints/core/logical_constraints.py b/pyshacl/constraints/core/logical_constraints.py index 7a27508..3e83847 100644 --- a/pyshacl/constraints/core/logical_constraints.py +++ b/pyshacl/constraints/core/logical_constraints.py @@ -12,6 +12,7 @@ from pyshacl.errors import ConstraintLoadError, ReportableRuntimeError, ShapeRecursionWarning, ValidationFailure from pyshacl.pytypes import GraphLike, SHACLExecutor from pyshacl.rdfutil import stringify_node +from pyshacl.shape import Shape SH_not = SH["not"] SH_and = SH["and"] @@ -37,7 +38,7 @@ class NotConstraintComponent(ConstraintComponent): shape_expecting = True list_taking = False - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(NotConstraintComponent, self).__init__(shape) not_list = list(self.shape.objects(SH_not)) if len(not_list) < 1: @@ -140,7 +141,7 @@ class AndConstraintComponent(ConstraintComponent): shape_expecting = True list_taking = True - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(AndConstraintComponent, self).__init__(shape) and_list = list(self.shape.objects(SH_and)) if len(and_list) < 1: @@ -236,7 +237,7 @@ class OrConstraintComponent(ConstraintComponent): shape_expecting = True list_taking = True - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(OrConstraintComponent, self).__init__(shape) or_list = list(self.shape.objects(SH_or)) if len(or_list) < 1: @@ -332,7 +333,7 @@ class XoneConstraintComponent(ConstraintComponent): shape_expecting = True list_taking = True - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(XoneConstraintComponent, self).__init__(shape) xone_nodes = list(self.shape.objects(SH_xone)) if len(xone_nodes) < 1: diff --git a/pyshacl/constraints/core/other_constraints.py b/pyshacl/constraints/core/other_constraints.py index 342cc3d..8f5ba0f 100644 --- a/pyshacl/constraints/core/other_constraints.py +++ b/pyshacl/constraints/core/other_constraints.py @@ -11,6 +11,7 @@ from pyshacl.errors import ConstraintLoadError, ReportableRuntimeError from pyshacl.pytypes import GraphLike, RDFNode, SHACLExecutor from pyshacl.rdfutil import stringify_node +from pyshacl.shape import Shape SH_InConstraintComponent = SH.InConstraintComponent SH_ClosedConstraintComponent = SH.ClosedConstraintComponent @@ -269,7 +270,7 @@ class HasValueConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_HasValueConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(HasValueConstraintComponent, self).__init__(shape) has_value_set = set(self.shape.objects(SH_hasValue)) if len(has_value_set) < 1: diff --git a/pyshacl/constraints/core/property_pair_constraints.py b/pyshacl/constraints/core/property_pair_constraints.py index 29c3e5a..7ec3f88 100644 --- a/pyshacl/constraints/core/property_pair_constraints.py +++ b/pyshacl/constraints/core/property_pair_constraints.py @@ -12,6 +12,7 @@ from pyshacl.helper.path_helper import shacl_path_to_sparql_path from pyshacl.pytypes import GraphLike, SHACLExecutor from pyshacl.rdfutil import stringify_node +from pyshacl.shape import Shape SH_equals = SH.equals SH_disjoint = SH.disjoint @@ -35,7 +36,7 @@ class EqualsConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_EqualsConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(EqualsConstraintComponent, self).__init__(shape) property_compare_set = set(self.shape.objects(SH_equals)) if len(property_compare_set) < 1: @@ -162,7 +163,7 @@ class DisjointConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_DisjointConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(DisjointConstraintComponent, self).__init__(shape) property_compare_set = set(self.shape.objects(SH_disjoint)) if len(property_compare_set) < 1: @@ -282,7 +283,7 @@ class LessThanConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_LessThanConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(LessThanConstraintComponent, self).__init__(shape) property_compare_set = set(self.shape.objects(SH_lessThan)) if len(property_compare_set) < 1: @@ -433,7 +434,7 @@ class LessThanOrEqualsConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_LessThanOrEqualsConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(LessThanOrEqualsConstraintComponent, self).__init__(shape) property_compare_set = set(self.shape.objects(SH_lessThanOrEquals)) if len(property_compare_set) < 1: diff --git a/pyshacl/constraints/core/string_based_constraints.py b/pyshacl/constraints/core/string_based_constraints.py index 5adf9a1..9af6ad5 100644 --- a/pyshacl/constraints/core/string_based_constraints.py +++ b/pyshacl/constraints/core/string_based_constraints.py @@ -13,6 +13,7 @@ from pyshacl.errors import ConstraintLoadError, ReportableRuntimeError from pyshacl.pytypes import GraphLike, SHACLExecutor from pyshacl.rdfutil import stringify_node +from pyshacl.shape import Shape RDF_langString = RDF.langString XSD_string = XSD.string @@ -98,7 +99,7 @@ class MinLengthConstraintComponent(StringBasedConstraintBase): shacl_constraint_component = SH_MinLengthConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(MinLengthConstraintComponent, self).__init__(shape) self.allow_multi_rules = False patterns_found = list(self.shape.objects(SH_minLength)) @@ -179,7 +180,7 @@ class MaxLengthConstraintComponent(StringBasedConstraintBase): shacl_constraint_component = SH_MaxLengthConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(MaxLengthConstraintComponent, self).__init__(shape) self.allow_multi_rules = False patterns_found = list(self.shape.objects(SH_maxLength)) @@ -257,7 +258,7 @@ class PatternConstraintComponent(StringBasedConstraintBase): shacl_constraint_component = SH_PatternConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(PatternConstraintComponent, self).__init__(shape) patterns_found = list(self.shape.objects(SH_pattern)) if len(patterns_found) < 1: @@ -341,7 +342,7 @@ class LanguageInConstraintComponent(StringBasedConstraintBase): shape_expecting = False list_taking = True - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(LanguageInConstraintComponent, self).__init__(shape) self.allow_multi_rules = False language_ins_found = list(self.shape.objects(SH_languageIn)) @@ -424,7 +425,7 @@ class UniqueLangConstraintComponent(StringBasedConstraintBase): shacl_constraint_component = SH_UniqueLangConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(UniqueLangConstraintComponent, self).__init__(shape) self.allow_multi_rules = False is_unique_lang = set(self.shape.objects(SH_uniqueLang)) diff --git a/pyshacl/constraints/core/value_constraints.py b/pyshacl/constraints/core/value_constraints.py index ce83eb7..1f13a2e 100644 --- a/pyshacl/constraints/core/value_constraints.py +++ b/pyshacl/constraints/core/value_constraints.py @@ -29,6 +29,7 @@ from pyshacl.errors import ConstraintLoadError from pyshacl.pytypes import GraphLike, SHACLExecutor from pyshacl.rdfutil import stringify_node +from pyshacl.shape import Shape RDF_langString = RDF.langString RDFS_Datatype = RDFS.Datatype @@ -59,7 +60,7 @@ class ClassConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_ClassConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(ClassConstraintComponent, self).__init__(shape) class_rules = list(self.shape.objects(SH_class)) if len(class_rules) < 1: @@ -168,7 +169,7 @@ class DatatypeConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_DatatypeConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(DatatypeConstraintComponent, self).__init__(shape) datatype_rules = list(self.shape.objects(SH_datatype)) if len(datatype_rules) < 1: @@ -276,7 +277,7 @@ class NodeKindConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_NodeKindConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(NodeKindConstraintComponent, self).__init__(shape) nodekind_rules = list(self.shape.objects(SH_nodeKind)) if len(nodekind_rules) < 1: diff --git a/pyshacl/constraints/core/value_range_constraints.py b/pyshacl/constraints/core/value_range_constraints.py index a4b285e..59fb332 100644 --- a/pyshacl/constraints/core/value_range_constraints.py +++ b/pyshacl/constraints/core/value_range_constraints.py @@ -13,6 +13,7 @@ from pyshacl.pytypes import GraphLike, SHACLExecutor from pyshacl.rdfutil import stringify_node from pyshacl.rdfutil.compare import compare_literal +from pyshacl.shape import Shape SH_MinExclusiveConstraintComponent = SH.MinExclusiveConstraintComponent SH_MinInclusiveConstraintComponent = SH.MinInclusiveConstraintComponent @@ -34,7 +35,7 @@ class MinExclusiveConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_MinExclusiveConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(MinExclusiveConstraintComponent, self).__init__(shape) min_vals = list(self.shape.objects(SH_minExclusive)) if len(min_vals) < 1: @@ -124,7 +125,7 @@ class MinInclusiveConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_MinInclusiveConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(MinInclusiveConstraintComponent, self).__init__(shape) min_vals = list(self.shape.objects(SH_minInclusive)) if len(min_vals) < 1: @@ -214,7 +215,7 @@ class MaxExclusiveConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_MaxExclusiveConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(MaxExclusiveConstraintComponent, self).__init__(shape) max_vals = list(self.shape.objects(SH_maxExclusive)) if len(max_vals) < 1: @@ -304,7 +305,7 @@ class MaxInclusiveConstraintComponent(ConstraintComponent): shacl_constraint_component = SH_MaxInclusiveConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(MaxInclusiveConstraintComponent, self).__init__(shape) max_vals = list(self.shape.objects(SH_maxInclusive)) if len(max_vals) < 1: diff --git a/pyshacl/constraints/sparql/sparql_based_constraints.py b/pyshacl/constraints/sparql/sparql_based_constraints.py index 4fc8bf4..52a4b52 100644 --- a/pyshacl/constraints/sparql/sparql_based_constraints.py +++ b/pyshacl/constraints/sparql/sparql_based_constraints.py @@ -11,6 +11,7 @@ from pyshacl.errors import ConstraintLoadError, ValidationFailure from pyshacl.helper import get_query_helper_cls from pyshacl.pytypes import GraphLike, SHACLExecutor +from pyshacl.shape import Shape SH_sparql = SH.sparql SH_SPARQLConstraintComponent = SH.SPARQLConstraintComponent @@ -25,7 +26,7 @@ class SPARQLBasedConstraint(ConstraintComponent): shacl_constraint_component = SH_SPARQLConstraintComponent - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(SPARQLBasedConstraint, self).__init__(shape) sg = self.shape.sg.graph sparql_node_list = set(self.shape.objects(SH_sparql)) From 247570eebffc4135bd0c55a453f824ed64596409 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Fri, 4 Oct 2024 12:59:39 -0400 Subject: [PATCH 2/4] Type InConstraintComponent Note: A revision away from an inlined iterator is due to a difference between pySHACL's RDFNode and RDFLib's Node. Signed-off-by: Alex Nelson --- pyshacl/constraints/core/other_constraints.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/pyshacl/constraints/core/other_constraints.py b/pyshacl/constraints/core/other_constraints.py index 8f5ba0f..5473a2c 100644 --- a/pyshacl/constraints/core/other_constraints.py +++ b/pyshacl/constraints/core/other_constraints.py @@ -2,9 +2,11 @@ """ https://www.w3.org/TR/shacl/#core-components-others """ -from typing import Dict, List, cast +import logging +from typing import Dict, List, Set, cast import rdflib +from rdflib.term import IdentifiedNode from pyshacl.constraints.constraint_component import ConstraintComponent from pyshacl.consts import RDFS, SH, RDF_type, SH_property @@ -35,24 +37,29 @@ class InConstraintComponent(ConstraintComponent): shape_expecting = False list_taking = True - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(InConstraintComponent, self).__init__(shape) - in_vals = list(self.shape.objects(SH_in)) - if len(in_vals) < 1: + in_val_lists: List[IdentifiedNode] = list(self.shape.objects(SH_in)) + if len(in_val_lists) < 1: raise ConstraintLoadError( "InConstraintComponent must have at least one sh:in predicate.", "https://www.w3.org/TR/shacl/#InConstraintComponent", ) - elif len(in_vals) > 1: + elif len(in_val_lists) > 1: raise ConstraintLoadError( "InConstraintComponent must have at most one sh:in predicate.", "https://www.w3.org/TR/shacl/#InConstraintComponent", ) - self.in_list = in_vals[0] + self.in_list: IdentifiedNode = in_val_lists[0] sg = self.shape.sg.graph - in_vals = set(sg.items(self.in_list)) - self.in_vals = in_vals + self.in_vals: Set[RDFNode] = set() + for item in sg.items(self.in_list): + if not isinstance(item, (rdflib.BNode, rdflib.Literal, rdflib.URIRef)): + logging.debug("item = %r.", item) + logging.debug("type(item) = %r.", type(item)) + raise TypeError("item in sh:in predicate is neither URIRef, BNode, or Literal.") + self.in_vals.add(item) @classmethod def constraint_parameters(cls): From 162827a43be5c5aa1756b4f251557c7aed033c34 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Fri, 4 Oct 2024 14:08:43 -0400 Subject: [PATCH 3/4] Type ClosedConstraintComponent Note: A revision away from `IdentifiedNode` is due to `URIRef` and `BNode` providing `.n3()`, while the parent class `IdentifiedNode` does not. Signed-off-by: Alex Nelson --- pyshacl/constraints/core/other_constraints.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pyshacl/constraints/core/other_constraints.py b/pyshacl/constraints/core/other_constraints.py index 5473a2c..89c0cd4 100644 --- a/pyshacl/constraints/core/other_constraints.py +++ b/pyshacl/constraints/core/other_constraints.py @@ -3,7 +3,7 @@ https://www.w3.org/TR/shacl/#core-components-others """ import logging -from typing import Dict, List, Set, cast +from typing import Dict, List, Set, Union, cast import rdflib from rdflib.term import IdentifiedNode @@ -108,7 +108,7 @@ class ClosedConstraintComponent(ConstraintComponent): ALWAYS_IGNORE = {(RDF_type, RDFS.Resource)} - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(ClosedConstraintComponent, self).__init__(shape) sg = self.shape.sg.graph closed_vals = list(self.shape.objects(SH_closed)) @@ -130,11 +130,17 @@ def __init__(self, shape): ) assert isinstance(closed_vals[0], rdflib.Literal), "sh:closed must take a xsd:boolean literal." self.is_closed = bool(closed_vals[0].value) - self.ignored_props = set() + self.ignored_props: Set[Union[rdflib.BNode, rdflib.Literal, rdflib.URIRef]] = set() for i in ignored_vals: try: items = set(sg.items(i)) for list_item in items: + if not isinstance(list_item, (rdflib.BNode, rdflib.Literal, rdflib.URIRef)): + logging.debug("list_item = %r.", list_item) + logging.debug("type(list_item) = %r.", type(list_item)) + raise TypeError( + "sh:ignoredProperties linked something that is neither URIRef, BNode, or Literal." + ) self.ignored_props.add(list_item) except ValueError: continue From 48bfbcd5139ff4ff2b7efe9a54b5437f5f7ba3a9 Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Fri, 4 Oct 2024 14:52:48 -0400 Subject: [PATCH 4/4] Type StringBasedConstraintBase Note: Extra type assertions are entered because broader type in `StringBasedConstraintBase` can't be narrowed in child classes. Signed-off-by: Alex Nelson --- .../core/string_based_constraints.py | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/pyshacl/constraints/core/string_based_constraints.py b/pyshacl/constraints/core/string_based_constraints.py index 9af6ad5..68c9f85 100644 --- a/pyshacl/constraints/core/string_based_constraints.py +++ b/pyshacl/constraints/core/string_based_constraints.py @@ -2,6 +2,7 @@ """ https://www.w3.org/TR/shacl/#core-components-string """ +import logging import re from typing import Dict, List @@ -11,7 +12,7 @@ from pyshacl.constraints.constraint_component import ConstraintComponent from pyshacl.consts import RDF, SH, XSD_WHOLE_INTEGERS from pyshacl.errors import ConstraintLoadError, ReportableRuntimeError -from pyshacl.pytypes import GraphLike, SHACLExecutor +from pyshacl.pytypes import GraphLike, RDFNode, SHACLExecutor from pyshacl.rdfutil import stringify_node from pyshacl.shape import Shape @@ -38,9 +39,9 @@ class StringBasedConstraintBase(ConstraintComponent): shacl_constraint_component = rdflib.URIRef("urn:notimplemented") - def __init__(self, shape): + def __init__(self, shape: Shape) -> None: super(StringBasedConstraintBase, self).__init__(shape) - self.string_rules = [] + self.string_rules: List[RDFNode] = [] self.allow_multi_rules = True @classmethod @@ -260,18 +261,19 @@ class PatternConstraintComponent(StringBasedConstraintBase): def __init__(self, shape: Shape) -> None: super(PatternConstraintComponent, self).__init__(shape) - patterns_found = list(self.shape.objects(SH_pattern)) + patterns_found: List[RDFNode] = [] + for pattern_found in self.shape.objects(SH_pattern): + if not isinstance(pattern_found, rdflib.Literal): + raise ConstraintLoadError( + "PatternConstraintComponent sh:pattern must be a RDF Literal node.", + "https://www.w3.org/TR/shacl/#PatternConstraintComponent", + ) + patterns_found.append(pattern_found) if len(patterns_found) < 1: raise ConstraintLoadError( "PatternConstraintComponent must have at least one sh:pattern predicate.", "https://www.w3.org/TR/shacl/#PatternConstraintComponent", ) - for p in patterns_found: - if not isinstance(p, rdflib.Literal): - raise ConstraintLoadError( - "PatternConstraintComponent sh:pattern must be a RDF Literal node.", - "https://www.w3.org/TR/shacl/#PatternConstraintComponent", - ) self.string_rules = patterns_found flags_found = set(self.shape.objects(SH_flags)) if len(flags_found) > 0: @@ -290,9 +292,19 @@ def constraint_name(cls): def make_generic_messages(self, datagraph: GraphLike, focus_node, value_node) -> List[rdflib.Literal]: if len(self.string_rules) < 2: - m = "Value does not match pattern '{}'".format(str(self.string_rules[0].value)) + string_rule = self.string_rules[0] + assert isinstance(string_rule, rdflib.Literal) + m = "Value does not match pattern '{}'".format(str(string_rule.value)) else: - rules = "', '".join(str(c.value) for c in self.string_rules) + # Inform type system that all the string rules are Literals. + _string_rules: List[rdflib.Literal] = [] + for string_rule in self.string_rules: + if not isinstance(string_rule, rdflib.Literal): + logging.debug("string_rule = %r.", string_rule) + logging.debug("type(string_rule) = %r.", type(string_rule)) + raise TypeError("Non-Literal entered string_rules list.") + _string_rules.append(string_rule) + rules = "', '".join(str(c.value) for c in _string_rules) m = "Value does not match every pattern in ('{}')".format(rules) return [rdflib.Literal(m)]