def _all_default(d, default, seen=None): """ ANY VALUE NOT SET WILL BE SET BY THE default THIS IS RECURSIVE """ if default is None: return if _get(default, CLASS) is Data: default = object.__getattribute__(default, SLOT) # REACH IN AND GET THE dict # Log = _late_import() # Log.error("strictly dict (or object) allowed: got {{type}}", type=_get(default, CLASS).__name__) for k, default_value in default.items(): default_value = unwrap( default_value ) # TWO DIFFERENT Dicts CAN SHARE id() BECAUSE THEY ARE SHORT LIVED if is_data(d): existing_value = d.get(k) else: existing_value = _get_attr(d, [k]) if existing_value == None: if default_value != None: if _get(default_value, CLASS) in data_types: df = seen.get(id(default_value)) if df is not None: _set_attr(d, [k], df) else: copy_dict = {} seen[id(default_value)] = copy_dict _set_attr(d, [k], copy_dict) _all_default(copy_dict, default_value, seen) else: # ASSUME PRIMITIVE (OR LIST, WHICH WE DO NOT COPY) try: _set_attr(d, [k], default_value) except Exception as e: if PATH_NOT_FOUND not in e: get_logger().error( "Can not set attribute {{name}}", name=k, cause=e) elif is_list(existing_value) or is_list(default_value): _set_attr(d, [k], None) _set_attr(d, [k], listwrap(existing_value) + listwrap(default_value)) elif (hasattr(existing_value, "__setattr__") or _get(existing_value, CLASS) in data_types) and _get( default_value, CLASS) in data_types: df = seen.get(id(default_value)) if df is not None: _set_attr(d, [k], df) else: seen[id(default_value)] = existing_value _all_default(existing_value, default_value, seen)
def _iadd(self, other): """ RECURSIVE ADDITION OF DATA PROPERTIES * LISTS ARE CONCATENATED * SETS ARE UNIONED * NUMBERS ARE ADDED """ if not _get(other, CLASS) in data_types: get_logger().error("Expecting Data") d = from_data(self) for ok, ov in other.items(): sv = d.get(ok) if sv == None: d[ok] = deepcopy(ov) elif isinstance(ov, (Decimal, float, long, int)): if _get(sv, CLASS) in data_types: get_logger().error( "can not add {{stype}} with {{otype}", stype=_get(sv, CLASS).__name__, otype=_get(ov, CLASS).__name__, ) elif is_list(sv): d[ok].append(ov) else: d[ok] = sv + ov elif is_list(ov): d[ok] = listwrap(sv) + ov elif _get(ov, CLASS) in data_types: if _get(sv, CLASS) in data_types: _iadd(sv, ov) elif is_list(sv): d[ok].append(ov) else: get_logger().error( "can not add {{stype}} with {{otype}", stype=_get(sv, CLASS).__name__, otype=_get(ov, CLASS).__name__, ) else: if _get(sv, CLASS) in data_types: get_logger().error( "can not add {{stype}} with {{otype}", stype=_get(sv, CLASS).__name__, otype=_get(ov, CLASS).__name__, ) else: d[ok].append(ov) return self
def __getitem__(self, key): if key == None: return Null if key == ".": output = self._internal_dict if _get(output, CLASS) in data_types: return self else: return output key = text(key) d = self._internal_dict if key.find(".") >= 0: seq = _split_field(key) for n in seq: if _get(d, CLASS) is NullType: d = NullType( d, n) # OH DEAR, Null TREATS n AS PATH, NOT LITERAL elif is_list(d): d = [_getdefault(dd, n) for dd in d] else: d = _getdefault(d, n) # EVERYTHING ELSE TREATS n AS LITERAL return wrap(d) else: o = d.get(key) if o == None: return NullType(d, key) return wrap(o)
def __getitem__(self, key): if key == None: return Null if key == ".": output = self._internal_dict if _get(output, CLASS) in data_types: return self else: return output key = text_type(key) d = self._internal_dict if key.find(".") >= 0: seq = _split_field(key) for n in seq: if _get(d, CLASS) is NullType: d = NullType(d, n) # OH DEAR, Null TREATS n AS PATH, NOT LITERAL elif is_list(d): d = [_getdefault(dd, n) for dd in d] else: d = _getdefault(d, n) # EVERYTHING ELSE TREATS n AS LITERAL return wrap(d) else: o = d.get(key) if o == None: return NullType(d, key) return wrap(o)
def pop(self, key, default=Null): if key == None: return Null if key == ".": raise NotImplemented() key = text(key) d = self._internal_dict if key.find(".") >= 0: seq = _split_field(key) for n in seq[:-1]: if _get(d, CLASS) is NullType: d = NullType( d, n) # OH DEAR, Null TREATS n AS PATH, NOT LITERAL elif is_list(d): d = [_getdefault(dd, n) for dd in d] else: d = _getdefault(d, n) # EVERYTHING ELSE TREATS n AS LITERAL key = seq[-1] o = d.get(key) if o == None: if default is Null: return NullType(d, key) return default d[key] = None return to_data(o)
def _all_default(d, default, seen=None): """ ANY VALUE NOT SET WILL BE SET BY THE default THIS IS RECURSIVE """ if default is None: return if _get(default, CLASS) is Data: default = object.__getattribute__(default, SLOT) # REACH IN AND GET THE dict # Log = _late_import() # Log.error("strictly dict (or object) allowed: got {{type}}", type=_get(default, CLASS).__name__) for k, default_value in default.items(): default_value = unwrap(default_value) # TWO DIFFERENT Dicts CAN SHARE id() BECAUSE THEY ARE SHORT LIVED existing_value = _get_attr(d, [k]) if existing_value == None: if default_value != None: if _get(default_value, CLASS) in data_types: df = seen.get(id(default_value)) if df is not None: _set_attr(d, [k], df) else: copy_dict = {} seen[id(default_value)] = copy_dict _set_attr(d, [k], copy_dict) _all_default(copy_dict, default_value, seen) else: # ASSUME PRIMITIVE (OR LIST, WHICH WE DO NOT COPY) try: _set_attr(d, [k], default_value) except Exception as e: if PATH_NOT_FOUND not in e: get_logger().error("Can not set attribute {{name}}", name=k, cause=e) elif is_list(existing_value) or is_list(default_value): _set_attr(d, [k], None) _set_attr(d, [k], listwrap(existing_value) + listwrap(default_value)) elif (hasattr(existing_value, "__setattr__") or _get(existing_value, CLASS) in data_types) and _get(default_value, CLASS) in data_types: df = seen.get(id(default_value)) if df is not None: _set_attr(d, [k], df) else: seen[id(default_value)] = existing_value _all_default(existing_value, default_value, seen)
def _iadd(self, other): if not _get(other, CLASS) in data_types: get_logger().error("Expecting a Mapping") d = unwrap(self) for ok, ov in other.items(): sv = d.get(ok) if sv == None: d[ok] = deepcopy(ov) elif isinstance(ov, (Decimal, float, long, int)): if _get(sv, CLASS) in data_types: get_logger().error( "can not add {{stype}} with {{otype}", stype=_get(sv, CLASS).__name__, otype=_get(ov, CLASS).__name__ ) elif is_list(sv): d[ok].append(ov) else: d[ok] = sv + ov elif is_list(ov): d[ok] = listwrap(sv) + ov elif _get(ov, CLASS) in data_types: if _get(sv, CLASS) in data_types: _iadd(sv, ov) elif is_list(sv): d[ok].append(ov) else: get_logger().error( "can not add {{stype}} with {{otype}", stype=_get(sv, CLASS).__name__, otype=_get(ov, CLASS).__name__ ) else: if _get(sv, CLASS) in data_types: get_logger().error( "can not add {{stype}} with {{otype}", stype=_get(sv, CLASS).__name__, otype=_get(ov, CLASS).__name__ ) else: d[ok].append(ov) return self
def unwraplist(v): """ LISTS WITH ZERO AND ONE element MAP TO None AND element RESPECTIVELY """ if is_list(v): if len(v) == 0: return None elif len(v) == 1: return unwrap(v[0]) else: return unwrap(v) else: return unwrap(v)
def _str(value, depth): """ FOR DEBUGGING POSSIBLY RECURSIVE STRUCTURES """ output = [] if depth > 0 and _get(value, CLASS) in data_types: for k, v in value.items(): output.append(str(k) + "=" + _str(v, depth - 1)) return "{" + ",\n".join(output) + "}" elif depth > 0 and is_list(value): for v in value: output.append(_str(v, depth - 1)) return "[" + ",\n".join(output) + "]" else: return str(type(value))
def _str(value, depth): """ FOR DEBUGGING POSSIBLY RECURSIVE STRUCTURES """ output = [] if depth >0 and _get(value, CLASS) in data_types: for k, v in value.items(): output.append(str(k) + "=" + _str(v, depth - 1)) return "{" + ",\n".join(output) + "}" elif depth >0 and is_list(value): for v in value: output.append(_str(v, depth-1)) return "[" + ",\n".join(output) + "]" else: return str(type(value))
def listwrap(value): """ PERFORMS THE FOLLOWING TRANSLATION None -> [] value -> [value] [...] -> [...] (unchanged list) ## MOTIVATION ## OFTEN IT IS NICE TO ALLOW FUNCTION PARAMETERS TO BE ASSIGNED A VALUE, OR A list-OF-VALUES, OR NULL. CHECKING FOR WHICH THE CALLER USED IS TEDIOUS. INSTEAD WE CAST FROM THOSE THREE CASES TO THE SINGLE CASE OF A LIST # BEFORE def do_it(a): if a is None: return if not isinstance(a, list): a=[a] for x in a: # do something # AFTER def do_it(a): for x in listwrap(a): # do something """ if value == None: return FlatList() elif is_list(value): if isinstance(value, list): return list_to_data(value) else: return value elif is_many(value): return list_to_data(list(value)) else: return list_to_data([from_data(value)])
def listwrap(value): """ PERFORMS THE FOLLOWING TRANSLATION None -> [] value -> [value] [...] -> [...] (unchanged list) ##MOTIVATION## OFTEN IT IS NICE TO ALLOW FUNCTION PARAMETERS TO BE ASSIGNED A VALUE, OR A list-OF-VALUES, OR NULL. CHECKING FOR WHICH THE CALLER USED IS TEDIOUS. INSTEAD WE CAST FROM THOSE THREE CASES TO THE SINGLE CASE OF A LIST # BEFORE def do_it(a): if a is None: return if not isinstance(a, list): a=[a] for x in a: # do something # AFTER def do_it(a): for x in listwrap(a): # do something """ if value == None: return FlatList() elif is_list(value): return wrap(value) elif isinstance(value, set): return wrap(list(value)) else: return wrap([unwrap(value)])