# HG changeset patch # User Thinker K.F. Li # Date 1308110603 -28800 # Node ID 2f9e7f03dbf7350b502f661b3735808df9a38802 # Parent fe1ebf0c3d40921d3c3fddc70e55db3ea7f60f28 Support predicates in structpath.py diff -r fe1ebf0c3d40 -r 2f9e7f03dbf7 paraspace/structpath.py --- a/paraspace/structpath.py Wed Jun 15 02:33:54 2011 +0800 +++ b/paraspace/structpath.py Wed Jun 15 12:03:23 2011 +0800 @@ -1,6 +1,10 @@ ## # Implement a xpath liked query language. # +# \section predicate Predicate +# Structpath uses syntax of Python for predicate. That means you must use +# '==' instead of '='. +# class context(object): all_classes = None class_instances = None @@ -49,6 +53,46 @@ return part == '..' +def _split_attr_pred(part): + try: + lq_idx = part.index('[') + except: + return part, '' + return part[:lq_idx].strip(), part[lq_idx:].strip() + + +def _eval_obj_sub_pred(ctx, obj, sub_pred): + ns_global = {} + for attr in dir(obj): + if attr.startswith('_'): + continue + v = _obj_attr(obj, attr) + ns_global[attr] = v + pass + + truth_v = eval(sub_pred, ns_global) + return truth_v + + +def _split_pred_into_subs(pred): + striped_pred = pred.strip(' []\n\r') + subs = striped_pred.split('][') + return subs + + +def _eval_obj_pred(ctx, obj, pred): + subs = _split_pred_into_subs(pred) + + for sub in subs: + if not sub: + continue + + if not _eval_obj_sub_pred(ctx, obj, sub): + return False + pass + return True + + def _obj_attr(obj, attrname): if isinstance(obj, list): idx = int(attrname) @@ -60,17 +104,22 @@ def _handle_path_part_obj(ctx, part, obj): - if _is_parent_name(part): + attr, pred = _split_attr_pred(part) + + if _is_parent_name(attr): new_objs = [ctx.get_parent(obj)] - elif _is_class(part): - class_name = _class_name(part) + elif _is_class(attr): + class_name = _class_name(attr) new_objs = ctx.class_instances[class_name] else: try: - new_objs = [_obj_attr(obj, part)] + new_objs = [_obj_attr(obj, attr)] except AttributeError: return [] pass + + new_objs = filter((lambda x: _eval_obj_pred(ctx, x, pred)), new_objs) + return new_objs diff -r fe1ebf0c3d40 -r 2f9e7f03dbf7 paraspace/tests/structpath_test.py --- a/paraspace/tests/structpath_test.py Wed Jun 15 02:33:54 2011 +0800 +++ b/paraspace/tests/structpath_test.py Wed Jun 15 12:03:23 2011 +0800 @@ -6,6 +6,9 @@ class wheel(object): + + def num_next(self): + return self.num + 1 pass @@ -25,8 +28,11 @@ root = ctx.root root.wheels = [wheel(), wheel(), wheel(), wheel()] + sn = 1 for w in root.wheels: w.parent = root + w.num = sn + sn = sn + 1 pass root.handle = handle() root.handle.parent = root @@ -104,3 +110,21 @@ wheels = structpath.find_objs_path(car_ctx, '.handle/wheels/1') assert len(wheels) == 0 pass + + +def structpath_pred_test(): + car_ctx = _build_car() + root = car_ctx.root + + wheels = structpath.find_objs_path(car_ctx, '.wheel[num == 2]') + assert len(wheels) == 1 + a_wheel = wheels[0] + assert a_wheel.num == 2 + assert a_wheel is root.wheels[1] + + wheels = structpath.find_objs_path(car_ctx, '.wheel[num_next() == 2]') + assert len(wheels) == 1 + a_wheel = wheels[0] + assert a_wheel.num == 1 + assert a_wheel is root.wheels[0] + pass