511ee39152
Purpose: To prevent future divergence of py2 and py3 behavior Note: Changes automatically generated by modernize tool then manually reviewed. Partially implements blueprint: support-python3 Change-Id: I88927a174e35efb8371896f4deb8d3e1158aac18
182 lines
6.5 KiB
Python
182 lines
6.5 KiB
Python
# Copyright (c) 2014 VMware, Inc. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
#
|
|
|
|
from __future__ import print_function
|
|
from __future__ import division
|
|
from __future__ import absolute_import
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from congress.datalog import compile
|
|
from congress.datalog import factset
|
|
from congress.datalog import utility
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class RuleSet(object):
|
|
"""RuleSet
|
|
|
|
Keeps track of all rules for all tables.
|
|
"""
|
|
|
|
# Internally:
|
|
# An index_name looks like this: (p, (2, 4)) which means this index is
|
|
# on table 'p' and it specifies columns 2 and 4.
|
|
#
|
|
# An index_key looks like this: (p, (2, 'abc'), (4, 'def'))
|
|
|
|
def __init__(self):
|
|
self.rules = {}
|
|
self.facts = {}
|
|
|
|
def __str__(self):
|
|
return str(self.rules) + " " + str(self.facts)
|
|
|
|
def add_rule(self, key, rule):
|
|
"""Add a rule to the Ruleset
|
|
|
|
@rule can be a Rule or a Fact. Returns True if add_rule() changes the
|
|
RuleSet.
|
|
"""
|
|
if isinstance(rule, compile.Fact):
|
|
# If the rule is a Fact, then add it to self.facts.
|
|
if key not in self.facts:
|
|
self.facts[key] = factset.FactSet()
|
|
return self.facts[key].add(rule)
|
|
|
|
elif len(rule.body) == 0 and not rule.head.is_negated():
|
|
# If the rule is a Rule, with no body, then it's a Fact, so
|
|
# convert the Rule to a Fact to a Fact and add to self.facts.
|
|
f = compile.Fact(key, (a.name for a in rule.head.arguments))
|
|
if key not in self.facts:
|
|
self.facts[key] = factset.FactSet()
|
|
return self.facts[key].add(f)
|
|
|
|
else:
|
|
# else the rule is a regular rule, so add it to self.rules.
|
|
if key in self.rules:
|
|
return self.rules[key].add(rule)
|
|
else:
|
|
self.rules[key] = utility.OrderedSet([rule])
|
|
return True
|
|
|
|
def discard_rule(self, key, rule):
|
|
"""Remove a rule from the Ruleset
|
|
|
|
@rule can be a Rule or a Fact. Returns True if discard_rule() changes
|
|
the RuleSet.
|
|
"""
|
|
if isinstance(rule, compile.Fact):
|
|
# rule is a Fact, so remove from self.facts
|
|
if key in self.facts:
|
|
changed = self.facts[key].remove(rule)
|
|
if len(self.facts[key]) == 0:
|
|
del self.facts[key]
|
|
return changed
|
|
return False
|
|
|
|
elif not len(rule.body):
|
|
# rule is a Rule, but without a body so it will be in self.facts.
|
|
if key in self.facts:
|
|
fact = compile.Fact(key, [a.name for a in rule.head.arguments])
|
|
changed = self.facts[key].remove(fact)
|
|
if len(self.facts[key]) == 0:
|
|
del self.facts[key]
|
|
return changed
|
|
return False
|
|
|
|
else:
|
|
# rule is a Rule with a body, so remove from self.rules.
|
|
if key in self.rules:
|
|
changed = self.rules[key].discard(rule)
|
|
if len(self.rules[key]) == 0:
|
|
del self.rules[key]
|
|
return changed
|
|
return False
|
|
|
|
def keys(self):
|
|
return list(self.facts.keys()) + list(self.rules.keys())
|
|
|
|
def __contains__(self, key):
|
|
return key in self.facts or key in self.rules
|
|
|
|
def contains(self, key, rule):
|
|
if isinstance(rule, compile.Fact):
|
|
return key in self.facts and rule in self.facts[key]
|
|
elif isinstance(rule, compile.Literal):
|
|
if key not in self.facts:
|
|
return False
|
|
fact = compile.Fact(key, [a.name for a in rule.arguments])
|
|
return fact in self.facts[key]
|
|
elif not len(rule.body):
|
|
if key not in self.facts:
|
|
return False
|
|
fact = compile.Fact(key, [a.name for a in rule.head.arguments])
|
|
return fact in self.facts[key]
|
|
else:
|
|
return key in self.rules and rule in self.rules[key]
|
|
|
|
def get_rules(self, key, match_literal=None):
|
|
facts = []
|
|
|
|
if (match_literal and not match_literal.is_negated() and
|
|
key in self.facts):
|
|
# If the caller supplies a literal to match against, then use an
|
|
# index to find the matching rules.
|
|
bound_arguments = tuple([i for i, arg
|
|
in enumerate(match_literal.arguments)
|
|
if not arg.is_variable()])
|
|
if (bound_arguments and
|
|
not self.facts[key].has_index(bound_arguments)):
|
|
# The index does not exist, so create it.
|
|
self.facts[key].create_index(bound_arguments)
|
|
|
|
partial_fact = tuple(
|
|
[(i, arg.name)
|
|
for i, arg in enumerate(match_literal.arguments)
|
|
if not arg.is_variable()])
|
|
facts = list(self.facts[key].find(partial_fact))
|
|
else:
|
|
# There is no usable match_literal, so get all facts for the
|
|
# table.
|
|
facts = list(self.facts.get(key, ()))
|
|
|
|
# Convert native tuples to Rule objects.
|
|
|
|
# TODO(alex): This is inefficient because it creates Literal and Rule
|
|
# objects. It would be more efficient to change the TopDownTheory and
|
|
# unifier to handle Facts natively.
|
|
fact_rules = []
|
|
for fact in facts:
|
|
# Setting use_modules=False so we don't split up tablenames.
|
|
# This allows us to choose at compile-time whether to split
|
|
# the tablename up.
|
|
literal = compile.Literal(
|
|
key, [compile.Term.create_from_python(x) for x in fact],
|
|
use_modules=False)
|
|
fact_rules.append(compile.Rule(literal, ()))
|
|
|
|
return fact_rules + list(self.rules.get(key, ()))
|
|
|
|
def clear(self):
|
|
self.rules = {}
|
|
self.facts = {}
|
|
|
|
def clear_table(self, table):
|
|
self.rules[table] = utility.OrderedSet()
|
|
self.facts[table] = factset.FactSet()
|