diff --git a/owlapy/abstracts/abstract_owl_reasoner.py b/owlapy/abstracts/abstract_owl_reasoner.py index 374be0e..987c3f2 100644 --- a/owlapy/abstracts/abstract_owl_reasoner.py +++ b/owlapy/abstracts/abstract_owl_reasoner.py @@ -201,13 +201,14 @@ def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyE pass @abstractmethod - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000) -> Iterable[OWLNamedIndividual]: """Gets the individuals which are instances of the specified class expression. Args: ce: The class expression whose instances are to be retrieved. direct: Specifies if the direct instances should be retrieved (True), or if all instances should be retrieved (False). + timeout: Time limit in seconds until results must be returned, else empty set is returned. Returns: If direct is True, each named individual j where the set of reasoner axioms entails diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index 4edcf91..4fdef03 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -1,6 +1,8 @@ """OWL Reasoner""" import operator import logging +import threading + import owlready2 from collections import defaultdict @@ -35,6 +37,18 @@ _P = TypeVar('_P', bound=OWLPropertyExpression) +def run_with_timeout(func, timeout, args=(), kwargs={}): + result = [None] + thread = threading.Thread(target=lambda: result.append(func(*args, **kwargs))) + thread.start() + thread.join(timeout) + if thread.is_alive(): + print(f"{func.__self__.__class__.__name__}.instances timed out! Timeout limit is currently set to {timeout} " + f"seconds\nReturning empty results...") + return [] + return result[-1] + + class OntologyReasoner(AbstractOWLReasonerEx): __slots__ = '_ontology', '_world' # TODO: CD: We will remove owlready2 from owlapy @@ -198,7 +212,7 @@ def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyE else: raise NotImplementedError(pe) - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: + def _instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: if direct: if isinstance(ce, OWLClass): c_x: owlready2.ThingClass = self._world[ce.str] @@ -226,6 +240,9 @@ def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OW else: raise NotImplementedError("instances for complex class expressions not implemented", ce) + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000): + return run_with_timeout(self._instances, timeout, (ce, direct)) + def _sub_classes_recursive(self, ce: OWLClassExpression, seen_set: Set, only_named: bool = True) \ -> Iterable[OWLClassExpression]: @@ -690,7 +707,7 @@ def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyE -> Iterable[OWLNamedIndividual]: yield from self._base_reasoner.object_property_values(ind, pe, direct) - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: + def _instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: if direct: if not self.__warned & 2: logger.warning("direct not implemented") @@ -698,6 +715,9 @@ def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OW temp = self._find_instances(ce) yield from temp + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000): + return run_with_timeout(self._instances, timeout, (ce, direct)) + def sub_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) \ -> Iterable[OWLClassExpression]: yield from self._base_reasoner.sub_classes(ce, direct=direct, only_named=only_named) @@ -1216,10 +1236,9 @@ def __init__(self, ontology: Union[SyncOntology, str], reasoner="HermiT"): else: raise NotImplementedError("Not implemented") + self.reasoner = reasoner - self.reasoner=reasoner - - def instances(self, ce: OWLClassExpression, direct=False) -> Set[OWLNamedIndividual]: + def _instances(self, ce: OWLClassExpression, direct=False) -> Set[OWLNamedIndividual]: """ Get the instances for a given class expression using HermiT. @@ -1230,12 +1249,15 @@ def instances(self, ce: OWLClassExpression, direct=False) -> Set[OWLNamedIndivid Returns: set: A set of individuals classified by the given class expression. """ - mapped_ce=self.mapper.map_(ce) + mapped_ce = self.mapper.map_(ce) instances = self._owlapi_reasoner.getInstances(mapped_ce, direct) flattended_instances = instances.getFlattened() assert str(type(flattended_instances)) == "" return {self.mapper.map_(ind) for ind in flattended_instances} + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000): + return run_with_timeout(self._instances, timeout, (ce, direct)) + def equivalent_classes(self, ce: OWLClassExpression) -> List[OWLClassExpression]: """ Gets the set of named classes that are equivalent to the specified class expression with diff --git a/owlapy/owlapi_mapper.py b/owlapy/owlapi_mapper.py index fc0a397..2186040 100644 --- a/owlapy/owlapi_mapper.py +++ b/owlapy/owlapi_mapper.py @@ -3,8 +3,7 @@ import jpype.imports from owlapy import owl_expression_to_manchester, manchester_to_owl_expression -from owlapy.class_expression import OWLClassExpression, OWLDataOneOf, OWLFacetRestriction, OWLDatatypeRestriction, \ - OWLNothing, OWLClass +from owlapy.class_expression import OWLClassExpression, OWLDataOneOf, OWLFacetRestriction, OWLDatatypeRestriction from owlapy.iri import IRI from owlapy.owl_axiom import OWLDeclarationAxiom, OWLAnnotation, OWLAnnotationProperty, OWLClassAssertionAxiom, \ OWLDataPropertyAssertionAxiom, OWLDataPropertyDomainAxiom, OWLDataPropertyRangeAxiom, OWLObjectPropertyDomainAxiom, \ @@ -111,6 +110,7 @@ def __init__(self, ontology: _SO): ontology_set.add(self.ontology) bidi_provider = BidirectionalShortFormProviderAdapter(self.manager, ontology_set, SimpleShortFormProvider()) entity_checker = ShortFormEntityChecker(bidi_provider) + bidi_provider.add(self.manager.getOWLDataFactory().getOWLNothing()) self.parser = ManchesterOWLSyntaxClassExpressionParser(self.manager.getOWLDataFactory(), entity_checker) self.renderer = ManchesterOWLSyntaxOWLObjectRendererImpl() @@ -152,8 +152,6 @@ def _(self, e): @map_.register def _(self, e: OWLClassExpression): - if isinstance(e, OWLClass) and e.str == OWLNothing.str: - return OWLClassImpl(self.map_(e.iri)) return self.parser.parse(owl_expression_to_manchester(e)) @map_.register(OWLAnonymousClassExpressionImpl)