class question(knowledge_base.knowledge_entity_list): r''' This represents one question in a question_base. It takes care of lookup parameters and caching and delegates the work of actually asking the user a question to the user_question object by calling its 'ask' method passing the format parameters. ''' not_found = unique.unique('question.not_found') def __init__(self, name, params, answer_param, user_question): super(question, self).__init__(name) self.params = tuple(params) self.answer_param = answer_param try: self.answer_param_position = list(params).index(answer_param) except ValueError: raise ValueError("question %s: answer parameter, %s, " "not in params list: %s" % (answer_param, params)) self.input_param_positions = \ tuple(filter(lambda i: i != self.answer_param_position, range(len(self.params)))) self.user_question = user_question self.cache = {} def __repr__(self): return "<question %s(%s): $%s = %s>" % \ (self.name, ', '.join('$' + p for p in self.params), self.answer_param, repr(self.user_question)) def set_knowledge_base(self, question_base): self.knowledge_base = question_base self.user_question.set_question_base(question_base) def lookup(self, bindings, pat_context, patterns): input_params = tuple( (self.params[i], unicode(patterns[i].as_data(pat_context))) for i in self.input_param_positions) format_params = dict(input_params) ans = self.cache.get(input_params, self.not_found) if ans is self.not_found: ans = self.cache[input_params] = \ self.user_question.ask(format_params) def gen(): mark = bindings.mark(True) end_done = False try: if patterns[self.answer_param_position] \ .match_data(bindings, pat_context, ans): bindings.end_save_all_undo() end_done = True yield finally: if not end_done: bindings.end_save_all_undo() bindings.undo_to_mark(mark) return contextlib.closing(gen()) def reset(self): self.cache.clear()
$_ignored >>> A_context.bind('_bogus', A_context, 567) False >>> A_context.lookup_data('_bogus') Traceback (most recent call last): ... KeyError: '$_bogus not bound' >>> A_context.lookup_data('_bogus', True) '$_bogus' """ import sys from pyke import pattern, unique _Not_found = unique.unique('Not_found') # Set to a sequence (or frozenset) of variable names to trace their bindings: debug = () class simple_context(object): def __init__(self): self.bindings = {} self.undo_list = [] self.save_all_undo_count = 0 def dump(self): for var_name in sorted(self.bindings.keys()): print("%s: %s" % (var_name, repr(self.lookup_data(var_name, True)))) def bind(self, var_name, var_context, val, val_context = None):
$_ignored >>> A_context.bind('_bogus', A_context, 567) False >>> A_context.lookup_data('_bogus') Traceback (most recent call last): ... KeyError: '$_bogus not bound' >>> A_context.lookup_data('_bogus', True) '$_bogus' """ import sys from pyke import pattern, unique _Not_found = unique.unique('Not_found') # Set to a sequence (or frozenset) of variable names to trace their bindings: debug = () class simple_context(object): def __init__(self): self.bindings = {} self.undo_list = [] self.save_all_undo_count = 0 def dump(self): for var_name in sorted(self.bindings.iterkeys()): print "%s: %s" % (var_name, repr(self.lookup_data(var_name, True))) def bind(self, var_name, var_context, val, val_context = None):
class tracked_object(object, metaclass=metaclass_option1): r''' All classes to be tracked by an object base would be derived from this one: >>> class foo(tracked_object): ... def __init__(self, arg): ... super(foo, self).__init__() ... print("foo.__init__:", arg) ... self.x = arg # should be ignored ... # doctest: +NORMALIZE_WHITESPACE metaclass: name foo , bases (<class 'experimental.metaclass.tracked_object'>,) , dict keys ('__init__', '__module__') And we can keep deriving classes: >>> class bar(foo): ... def __init__(self, arg1, arg2): ... super(bar, self).__init__(arg1) ... print("bar.__init__:", arg1, arg2) ... self.y = arg2 # should be ignored ... # doctest: +NORMALIZE_WHITESPACE metaclass: name bar , bases (<class 'experimental.metaclass.foo'>,) , dict keys ('__init__', '__module__') We can't do the next step directly in the class definition because the knowledge_engine.engine hasn't been created yet and so the object bases don't exist at that point in time. So this simulates adding the knowledge_base to the class later, after the knowledge_engine.engine and object bases have been created. >>> foo.knowledge_base = 'foo base' >>> bar.knowledge_base = 'bar base' And now we create some instances (shouldn't see any attribute change notifications here!): >>> f = foo(44) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS tracked_object.__setattr__ called on object <experimental.metaclass.foo object at 0x...> with property _ignore_setattr and value True tracked_object.__setattr__ called on object <experimental.metaclass.foo object at 0x...> with property knowledgebase and value None foo.__init__: 44 tracked_object.__setattr__ called on object <experimental.metaclass.foo object at 0x...> with property x and value 44 add instance <experimental.metaclass.foo object at 0x...> to foo base >>> b = bar(55, 66) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS tracked_object.__setattr__ called on object <experimental.metaclass.bar object at 0x...> with property _ignore_setattr and value True tracked_object.__setattr__ called on object <experimental.metaclass.bar object at 0x...> with property knowledgebase and value None foo.__init__: 55 tracked_object.__setattr__ called on object <experimental.metaclass.bar object at 0x...> with property x and value 55 bar.__init__: 55 66 tracked_object.__setattr__ called on object <experimental.metaclass.bar object at 0x...> with property y and value 66 add instance <experimental.metaclass.bar object at 0x...> to bar base And modify some attributes: >>> f.x = 'y' # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS tracked_object.__setattr__ called on object <experimental.metaclass.foo object at 0x...> with property x and value y tracked_object.__setattr__: notify foo base of attribute change: (<experimental.metaclass.foo object at 0x...>, x, y) >>> b.y = 'z' # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS tracked_object.__setattr__ called on object <experimental.metaclass.bar object at 0x...> with property y and value z tracked_object.__setattr__: notify bar base of attribute change: (<experimental.metaclass.bar object at 0x...>, y, z) >>> b.y = 'z' # should be ignored ... # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS tracked_object.__setattr__ called on object <experimental.metaclass.bar object at 0x...> with property y and value z >>> b.z = "wasn't set" # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS tracked_object.__setattr__ called on object <experimental.metaclass.bar object at 0x...> with property z and value wasn't set tracked_object.__setattr__: notify bar base of attribute change: (<experimental.metaclass.bar object at 0x...>, z, wasn't set) ''' _not_bound = unique('_not_bound') # a value that should != any other value! def __init__(self): self._ignore_setattr = True self.knowledgebase = None def __setattr__(self, attr, value): # This gets called when any attribute is changed. We would need to # figure out how to ignore attribute setting by the __init__ # function... # # Also the check to see if the attribute has actually changed by doing # a '!=' check could theoretically lead to problems. For example this # would fail to change the attribute to another value that wasn't # identical to the first, but '==' to it: for example, 4 and 4.0. print("tracked_object.__setattr__ called on object %s with property %s and value %s" % (self, attr, value)) if getattr(self, attr, self._not_bound) != value: super(tracked_object, self).__setattr__(attr, value) if not hasattr(self, '_ignore_setattr'): print("tracked_object.__setattr__: notify", self.knowledge_base, \ "of attribute change: (%s, %s, %s)" % (self, attr, value))