def __init__(self, raw_pattern, raw_previous, weight, alternates, method, rulename): """ Create a new Rule object based on information supplied to the @rule decorator. Arguments: raw_pattern - simplified regular expression string supplied to @rule raw_previous - simplified regular expression string supplied to @rule weight - weight supplied to @rule alternates - dictionary of variable names and values that can be substituted in the patterns method - reference to method decorated by @rule rulename - modulename.classname.methodname, used to make better error messages Raises PatternError, PatternVariableNotFoundError, PatternVariableValueError """ try: previous = "" if not raw_pattern: raise PatternError("Empty string found") self.pattern = Pattern(raw_pattern, alternates) previous = "previous " self.previous = Pattern(raw_previous, alternates) except (TypeError, PatternError, PatternVariableValueError, PatternVariableNotFoundError) as e: msg = " in {0}pattern of {1}".format(previous, rulename) e.args = (e.args[0] + msg,) + e.args[1:] raise self.weight = weight self.method = method self.rulename = rulename
def _parse_alternates(self, alternates, script_class_name): """Construct Pattern objects for all the values in the alternates instance variable (hopefully a dictionary) of a Script subclass, and construct a dictionary of the keys from alternates and the pattern object. Wrap that in another dictionary keyed by 'a' so it can be used by %a:varname in other patterns. """ valid = {} k = "" try: for k, v in alternates.items(): valid[k] = Pattern(v, simple=True).formatted_pattern except Exception as e: msg = " in alternates" if k: msg += '["{0}"]'.format(k) msg += " of {0}".format(script_class_name) e.args = (e.args[0] + msg,) + e.args[1:] raise return {"a": valid}
class Rule(object): """ Pattern matching and response rule. Describes one method decorated by @rule. Parses the simplified regular expression strings, raising PatternError if there is an error. Can match the pattern and previous_pattern against tokenized input (a Target) and return a Match object. Public instance variables: pattern - the Pattern object to match against the current message previous - the Pattern object to match against the previous reply weight - the weight, given to @rule method - a reference to the decorated method rulename - modulename.classname.methodname, for error messages Public methods: match - given current message and reply history, return a Match object if the patterns match or None if they don't full set of comparison operators - to enable sorting first by weight then score of the two patterns """ def __init__(self, raw_pattern, raw_previous, weight, alternates, method, rulename): """ Create a new Rule object based on information supplied to the @rule decorator. Arguments: raw_pattern - simplified regular expression string supplied to @rule raw_previous - simplified regular expression string supplied to @rule weight - weight supplied to @rule alternates - dictionary of variable names and values that can be substituted in the patterns method - reference to method decorated by @rule rulename - modulename.classname.methodname, used to make better error messages Raises PatternError, PatternVariableNotFoundError, PatternVariableValueError """ try: previous = "" if not raw_pattern: raise PatternError("Empty string found") self.pattern = Pattern(raw_pattern, alternates) previous = "previous " self.previous = Pattern(raw_previous, alternates) except (TypeError, PatternError, PatternVariableValueError, PatternVariableNotFoundError) as e: msg = " in {0}pattern of {1}".format(previous, rulename) e.args = (e.args[0] + msg,) + e.args[1:] raise self.weight = weight self.method = method self.rulename = rulename def match(self, target, history, variables): """ Return a Match object if the targets match the patterns for this rule, or None if they don't. Arguments: target - a Target object for the user's message history - a deque object containing Targets for previous replies variables - User and Bot variables for the PatternParser to substitute into the patterns """ m = self.pattern.match(target.normalized, variables) if m is None: return None mp = None reply_target = None if self.previous: if not history: return None reply_target = history[0] mp = self.previous.match(reply_target.normalized, variables) if mp is None: return None return Match(m, mp, target, reply_target) def __lt__(self, other): """ Full set of comparison operators. The weight passed to @rule is the most significant, followed by the complexity of the pattern and the complexity of the previous pattern. """ return (self.weight < other.weight or (self.weight == other.weight and self.pattern.score < other.pattern.score) or (self.weight == other.weight and self.pattern.score == other.pattern.score and self.previous.score < other.previous.score)) def __eq__(self, other): return (self.weight == other.weight and self.pattern.score == other.pattern.score and self.previous.score == other.previous.score) def __gt__(self, other): return not (self == other or self < other) def __le__(self, other): return self < other or self == other def __ge__(self, other): return self > other or self == other def __ne__(self, other): return not self == other