view service/mqtt_to_rdf/inference/candidate_binding.py @ 1727:23e6154e6c11

file moves
author drewp@bigasterisk.com
date Tue, 20 Jun 2023 23:26:24 -0700
parents service/mqtt_to_rdf/candidate_binding.py@3cf7f313b285
children
line wrap: on
line source

import logging
from dataclasses import dataclass
from typing import Dict, Iterable, Iterator, Union

from rdflib import Graph
from rdflib.term import Node, Variable

from inference.inference_types import (BindableTerm, BindingUnknown, RuleUnboundBnode, Triple)

log = logging.getLogger('cbind')
INDENT = '    '


class BindingConflict(ValueError):  # might be the same as `Inconsistent`
    pass


@dataclass
class CandidateBinding:
    binding: Dict[BindableTerm, Node]

    def __post_init__(self):
        for n in self.binding.values():
            if isinstance(n, RuleUnboundBnode):
                raise TypeError(repr(self))

    def __repr__(self):
        b = " ".join("%r=%r" % (var, value) for var, value in sorted(self.binding.items()))
        return f'CandidateBinding({b})'

    def key(self):
        """note this is only good for the current value, and self.binding is mutable"""
        return tuple(sorted(self.binding.items()))

    def apply(self, g: Union[Graph, Iterable[Triple]], returnBoundStatementsOnly=True) -> Iterator[Triple]:
        for stmt in g:
            try:
                bound = (
                    self.applyTerm(stmt[0], returnBoundStatementsOnly),  #
                    self.applyTerm(stmt[1], returnBoundStatementsOnly),  #
                    self.applyTerm(stmt[2], returnBoundStatementsOnly))
            except BindingUnknown:
                if log.isEnabledFor(logging.DEBUG):
                    log.debug(f'{INDENT*7} CB.apply cant bind {stmt} using {self.binding}')

                continue
            if log.isEnabledFor(logging.DEBUG):
                log.debug(f'{INDENT*7} CB.apply took {stmt} to {bound}')

            yield bound

    def applyTerm(self, term: Node, failUnbound=True):
        if isinstance(term, (Variable, RuleUnboundBnode)):
            if term in self.binding:
                return self.binding[term]
            else:
                if failUnbound:
                    raise BindingUnknown()
        return term

    def addNewBindings(self, newBindings: 'CandidateBinding'):
        for k, v in newBindings.binding.items():
            if k in self.binding and self.binding[k] != v:
                raise BindingConflict(f'thought {k} would be {self.binding[k]} but another Evaluation said it should be {v}')
            self.binding[k] = v

    def copy(self):
        return CandidateBinding(self.binding.copy())

    def contains(self, term: BindableTerm):
        return term in self.binding