def intersection(self, other): from winnow.options import OptionsSet if isinstance(other, OptionNullWinnowValue): this_options = self._get_options() that_options = other._get_options() options = None if this_options is not None and that_options is not None: options = OptionsSet(this_options).merge( OptionsSet(that_options)).store elif this_options is not None or that_options is not None: options = this_options if this_options is not None else that_options else: pass info = self.get_merged_info(other) info[u"type"] = self.type, if options is not None: info[VALUES_KEY_NAME] = {u"options": options} return self.__class__(info) if isinstance(other, OptionResourceWinnowValue) or isinstance( other, OptionStringWinnowValue): return other.intersection(self) return None
def _merge_option_dicts(source, options_a, options_b, ref_doc_a, ref_doc_b, errors=[]): # expand the options dicts collecting their replaced refs ref_hashes = {} inline_refs(options_a, ref_doc_a, source, ref_hashes) inline_refs(options_b, ref_doc_b, source, ref_hashes) # do the merge set_a = OptionsSet(options_a) set_b = OptionsSet(options_b) try: merged_options = set_a.merge(set_b).store except OptionsExceptionSetWithException as e: merged_options = e.set.store for ex_info in e.exception_infos: key = ex_info[0] values = ex_info[1] msg = """***** Merge Error **** Merging: type: {type_a} path: {path_a} with type: {type_b} path: {path_b} Gave an empty result for the key: {key} When using these values: {values} """.format(type_a=ref_doc_a.get("type"), type_b=ref_doc_b.get("type"), path_a=ref_doc_a.get("path"), path_b=ref_doc_b.get("path"), key=key, values="\n".join( [utils.json_dumps(v.as_json()) for v in values])) errors.append(msg) # un merge unchanged refs by looking at the ref_hashes restore_unchanged_refs(merged_options, ref_hashes) return merged_options
def _find_expanded_ref(reference, doc, source, options, ref_hashes, default_scopes=None): # looks up the contents of a reference if u"~" in reference: ref, internal_path = reference.split(u"~") elif u"#" in reference: ref, internal_path = reference.split(u"#") else: ref = reference internal_path = None if ref == "": referenced_doc = doc else: wv = source.lookup(ref) if wv is None: raise OptionsExceptionReferenceError( "Winnow Reference Error: Failed to find resource %s" % ref) existing_doc = wv.get_doc() referenced_doc = deepcopy(existing_doc) if referenced_doc is None: raise OptionsExceptionReferenceError( "Winnow Reference Error: Cannot find reference %s as ref doc is None" % ref) else: if options is not None: # if the ref also has some options then pre merge them into the reference referenced_options = referenced_doc.get(u"options") if referenced_options is None: referenced_doc[u"options"] = options else: options_a = OptionsSet(referenced_options) options_b = OptionsSet(options) referenced_doc[u"options"] = _merge_option_dicts( source, options_a.store, options_b.store, referenced_doc, doc) ## TODO look again at the the default scopes thing # if default_scopes is not None: # for k, v in referenced_doc[u"options"].iteritems(): # print v # if v.get("scopes") is None: # v["scopes"] = default_scopes new_doc = _extract_internal_path( referenced_doc, internal_path) if internal_path else referenced_doc # TODO revisit this use of doc as the reference doc to use new_doc = inline_refs(new_doc, doc, source, ref_hashes) return new_doc
def _merge_option_dicts(source, options_a, options_b, ref_doc_a, ref_doc_b, errors=[]): # expand the options dicts collecting their replaced refs ref_hashes = {} inline_refs(options_a, ref_doc_a, source, ref_hashes) inline_refs(options_b, ref_doc_b, source, ref_hashes) # do the merge set_a = OptionsSet(options_a) set_b = OptionsSet(options_b) try: merged_options = set_a.merge(set_b).store except OptionsExceptionSetWithException as e: merged_options = e.set.store for ex_info in e.exception_infos: key = ex_info[0] values = ex_info[1] msg = """***** Merge Error **** Merging: type: {type_a} path: {path_a} with type: {type_b} path: {path_b} Gave an empty result for the key: {key} When using these values: {values} """.format(type_a=ref_doc_a.get("type"), type_b=ref_doc_b.get("type"), path_a=ref_doc_a.get("path"), path_b=ref_doc_b.get("path"), key=key, values = "\n".join([utils.json_dumps(v.as_json()) for v in values]) ) errors.append(msg) # un merge unchanged refs by looking at the ref_hashes restore_unchanged_refs(merged_options, ref_hashes) return merged_options
def default_choices(source, scopes): #take a copy of the options doc = source.get_doc() options_dict = deepcopy(source.get_options_dict()) #expand it ref_hashes = {} inline.inline_refs(options_dict, doc, source, ref_hashes) #scope it _trim_out_off_scope(options_dict, set(scopes)) # wrap it in an options set options_set = OptionsSet(options_dict) #get default options set default = options_set.default() return default.store
def issubset(self, other): if isinstance(other, OptionNullWinnowValue): return True from winnow.options import OptionsSet self.check_class(other) other_keys = set(other.values_lookup.keys()) self_keys = set(self.values_lookup.keys()) ## check this values keys if not self_keys.issubset(other_keys): return False ## if self and other have matching values that both have have nested options check them too for key in self_keys: this_options = self.get_value_options(key) that_options = other.get_value_options(key) if this_options is not None and that_options is not None: if not OptionsSet(that_options).allows( OptionsSet(this_options)): return False return True
def _patch_upstream(source, target, options_set): doc = source.get_doc() if u"upstream" in doc.keys(): upstream_delegate = source.get_upstream() if upstream_delegate is None: raise OptionsExceptionReferenceError( "Winnow Reference Error: Cannot find upstream reference %s" % doc[u"upstream"]) else: return options_set upstream_options = OptionsSet(upstream_delegate.get_options_dict()) patched_options_set = options_set.patch(upstream_options) if target is not None: _add_start_if_needed(source, target) target.add_history_action(action=HISTORY_ACTION_PATCH, input=upstream_delegate, output_type=doc.get("type")) return _patch_upstream(upstream_delegate, target, patched_options_set)
def allows(source_a, source_b): set_a = OptionsSet(inline.get_inlined_options(source_a)) set_b = OptionsSet(inline.get_inlined_options(source_b)) return set_a.allows(set_b)
def test_wildcard(self): options_1 = { u"colour": { u"type": VALUE_TYPE_SET_STRING, u"name": u"colour", u"description": u"please choose one of the colours", VALUES_KEY_NAME: [ { u"type": VALUE_TYPE_SET_STRING, u"name": u"red", u"description": u"the colour red", u"image_uri": u"http://something.com/khgfdkyg.png", u"value": u"red", u"options":{ u"apples":{ u"type": VALUE_TYPE_SET_STRING, u"values": [u"cox", u"jazz", u"bramley"] } } }, { u"type": VALUE_TYPE_SET_STRING, u"name": u"blue", u"description": u"the colour blue", u"image_uri": u"http://something.com/khgfdkyg.png", u"value": u"blue" } ] } } options_2 = { u"*/apples": [u"cox", u"jazz"] } set1 = OptionsSet(options_1) set2 = OptionsSet(options_2) self.assertEqual(set1.matcher.options.keys(), [u"colour", u"colour/apples"]) self.assertEqual(set2.matcher.options.keys(), [u"*/apples"]) mega_store_1 = set1.mega_store(set2) mega_store_2 = set2.mega_store(set1) self.assertEqual(mega_store_1.keys(), [u"colour"]) merged = set1.merge(set2).store expected = [ { "description": "the colour blue", "image_uri": "http://something.com/khgfdkyg.png", "name": "blue", "type": "set::string", "value": "blue" }, { "description": "the colour red", "image_uri": "http://something.com/khgfdkyg.png", "name": "red", "options": { "apples": [ "cox", "jazz" ] }, "type": "set::string", "value": "red" } ] self.assertEqual(expected, merged["colour"]["values"])
def disallowed_keys(source_a, source_b): set_a = OptionsSet(inline.get_inlined_options(source_a)) set_b = OptionsSet(inline.get_inlined_options(source_b)) return set_b.disallowed_keys(set_a)
def intersection(self, other): # print " " # print "**************** resource intersection ******************" # print "self keys", self.values_lookup.keys() from winnow.options import OptionsSet self.check_class(other) values = [] if type(other) == OptionNullWinnowValue: # print "a null resource" other_options = other._get_options() if other_options is None: return self self_keys = list(self.values_lookup.keys()) for value_id in self_keys: this_value = self.values_lookup.get(value_id) if isinstance(this_value, dict): new_value = deepcopy(this_value) this_options = self.get_value_options(value_id) else: raise Exception("This shouldn't ever happen") # if this_options is not None: # # new_value[u"options"] = OptionsSet(this_options).merge(OptionsSet(other_options)).store # # values.append(new_value) # # # # if this_options is not None: try: new_value[u"options"] = OptionsSet(this_options).merge( OptionsSet(other_options)).store except OptionsExceptionSetWithException as e: new_value = None if new_value is not None: values.append(new_value) elif type(other) == OptionStringWinnowValue: raise OptionsExceptionIncompatibleTypes( "You cannot merge and resource with a string: %s" % other) else: # take a cope of the the possible values in a lookup table keyed by path all_values = deepcopy(self.values_lookup) all_values.update(deepcopy(other.values_lookup)) # prune and child nodes from each set other_paths = list(other.values_lookup.keys()) self_paths = list(self.values_lookup.keys()) other_paths_pruned = self._prune_child_nodes(other_paths) self_paths_pruned = self._prune_child_nodes(self_paths) # then add values if it or parent is in other intersecting_paths = self._intersection_of_path_sets( other_paths_pruned, self_paths_pruned) # for each intersecting path find the most specific option set on each side and merge them values = [] for path in intersecting_paths: # find best fit values to get merged options this_value = self.values_lookup[self._nearest_match( path, self_paths)] other_value = other.values_lookup[self._nearest_match( path, other_paths)] merged_value = self.munge_values(this_value, other_value) # and then add these options to a copy of the origional value from all_values new_value = all_values[path] if "options" in merged_value: new_value["options"] = merged_value["options"] values.append(new_value) ## if there is no intersection return None if len(values) == 0: return None info = self.get_merged_info(other) info[u"type"] = self.type, info[VALUES_KEY_NAME] = values return self.__class__(info)
def intersection(self, other): # # # print "++++++++++++++++++++++++++++++++++++++ me +++++++++++++++++++++++++++++++++++++++++++" # print self.values_lookup.keys() # from winnow.options import OptionsSet self.check_class(other) values = [] default = None if type(other) == OptionResourceWinnowValue: msg = "You cannot merge a string with a resource: self: %s\n other: %s" % ( self, other) raise OptionsExceptionIncompatibleTypes(msg) elif isinstance(other, OptionNullWinnowValue): other_options = other._get_options() if other_options is None: return self self_keys = list(self.values_lookup.keys()) for value_id in self_keys: this_value = self.values_lookup.get(value_id) if isinstance(this_value, dict): new_value = deepcopy(this_value) this_options = self.get_value_options(value_id) elif isinstance(this_value, unicode): new_value = {"type": u"string", "value": this_value} this_options = None else: raise Exception("This shouldn't ever happen") if this_options is not None: try: new_value[u"options"] = OptionsSet(this_options).merge( OptionsSet(other_options)).store except OptionsExceptionSetWithException as e: new_value = None default = self._default if new_value is not None: values.append(new_value) else: other_keys = list(other.values_lookup.keys()) # self_keys = list(self.values_lookup.keys()) other_keys.sort() this_default_allowed = False that_default_allowed = False # find matching values # get the one to use # add them to the list ## when putting together the intersecting values perform a merge on their nested options for value_id in other_keys: this_value = self.values_lookup.get(value_id) if this_value is None: continue other_value = other.values_lookup[value_id] if self._default == value_id: this_default_allowed = True if other._default == value_id: that_default_allowed = True ## if they are both unicode just add the value if isinstance(other_value, unicode) and isinstance( this_value, unicode): values.append(this_value) elif isinstance(other_value, unicode) and isinstance( this_value, dict): values.append(deepcopy(this_value)) elif isinstance(other_value, dict) and isinstance( this_value, unicode): values.append(deepcopy(other_value)) elif isinstance(other_value, dict) and isinstance( this_value, dict): new_value = self.munge_values(this_value, other_value) values.append(new_value) else: raise Exception("this should never happen") if this_default_allowed and that_default_allowed: default = self._default elif this_default_allowed: default = self._default elif that_default_allowed: default = other._default else: default = None ## if there is no intersection return None if len(values) == 0: return None info = self.get_merged_info(other) info[u"type"] = self.type, info[VALUES_KEY_NAME] = values if default is not None: info[u"default"] = default return self.__class__(info)
class OptionStringWinnowValue(OptionWinnowValue): type = VALUE_TYPE_SET_STRING def validate_single_value(self, value): if not isinstance(value, unicode): raise Exception("should be unicode %s" % value) def __str__(self): return str(self.as_json()) def _set_value_list(self, value_list): self.values_lookup = {} for v in value_list: single_value = v if isinstance(v, unicode) else v[u"value"] self.validate_single_value(single_value) self.values_lookup[single_value] = v @property def default(self): first = self.values_lookup.keys()[0] if self._default is None: return first if not self._default in self.values_lookup: return first return self._default # # @property # def default(self): # from winnow.options import OptionsSet # # default_key = self.get_default_key() # default_value = self.values_lookup[default_key] # new_value = deepcopy(default_value) # # value_options = new_value.get(u"options") if hasattr(new_value, 'get') else None # if value_options is None or value_options == {}: # return self.get_default() # # new_value[u"options"] = OptionsSet(value_options).default().store # # info = self.get_merged_info(self) # info[u"type"] = self.type, # info[VALUES_KEY_NAME] = [new_value] # # return self.__class__(info).as_json() def isdisjoint(self, other): if isinstance(other, OptionNullWinnowValue): return False self.check_class(other) other_keys = set(other.values_lookup.keys()) self_keys = set(self.values_lookup.keys()) return self_keys.isdisjoint(other_keys) def issubset(self, other): if isinstance(other, OptionNullWinnowValue): return True from winnow.options import OptionsSet self.check_class(other) other_keys = set(other.values_lookup.keys()) self_keys = set(self.values_lookup.keys()) ## check this values keys if not self_keys.issubset(other_keys): return False ## if self and other have matching values that both have have nested options check them too for key in self_keys: this_options = self.get_value_options(key) that_options = other.get_value_options(key) if this_options is not None and that_options is not None: if not OptionsSet(that_options).allows( OptionsSet(this_options)): return False return True def intersection(self, other): # # # print "++++++++++++++++++++++++++++++++++++++ me +++++++++++++++++++++++++++++++++++++++++++" # print self.values_lookup.keys() # from winnow.options import OptionsSet self.check_class(other) values = [] default = None if type(other) == OptionResourceWinnowValue: msg = "You cannot merge a string with a resource: self: %s\n other: %s" % ( self, other) raise OptionsExceptionIncompatibleTypes(msg) elif isinstance(other, OptionNullWinnowValue): other_options = other._get_options() if other_options is None: return self self_keys = list(self.values_lookup.keys()) for value_id in self_keys: this_value = self.values_lookup.get(value_id) if isinstance(this_value, dict): new_value = deepcopy(this_value) this_options = self.get_value_options(value_id) elif isinstance(this_value, unicode): new_value = {"type": u"string", "value": this_value} this_options = None else: raise Exception("This shouldn't ever happen") if this_options is not None: try: new_value[u"options"] = OptionsSet(this_options).merge( OptionsSet(other_options)).store except OptionsExceptionSetWithException as e: new_value = None default = self._default if new_value is not None: values.append(new_value) else: other_keys = list(other.values_lookup.keys()) # self_keys = list(self.values_lookup.keys()) other_keys.sort() this_default_allowed = False that_default_allowed = False # find matching values # get the one to use # add them to the list ## when putting together the intersecting values perform a merge on their nested options for value_id in other_keys: this_value = self.values_lookup.get(value_id) if this_value is None: continue other_value = other.values_lookup[value_id] if self._default == value_id: this_default_allowed = True if other._default == value_id: that_default_allowed = True ## if they are both unicode just add the value if isinstance(other_value, unicode) and isinstance( this_value, unicode): values.append(this_value) elif isinstance(other_value, unicode) and isinstance( this_value, dict): values.append(deepcopy(this_value)) elif isinstance(other_value, dict) and isinstance( this_value, unicode): values.append(deepcopy(other_value)) elif isinstance(other_value, dict) and isinstance( this_value, dict): new_value = self.munge_values(this_value, other_value) values.append(new_value) else: raise Exception("this should never happen") if this_default_allowed and that_default_allowed: default = self._default elif this_default_allowed: default = self._default elif that_default_allowed: default = other._default else: default = None ## if there is no intersection return None if len(values) == 0: return None info = self.get_merged_info(other) info[u"type"] = self.type, info[VALUES_KEY_NAME] = values if default is not None: info[u"default"] = default return self.__class__(info) def _get_options(self, value): if isinstance(value, unicode): return None return value.get(u"options") def munge_values(self, this_value, other_value): #WTF is this really meant to do!!!!! from winnow.options import OptionsSet try: new_value = deepcopy(other_value) new_value.update(deepcopy(this_value)) except Exception, e: print "this_value", this_value print "other_value", other_value raise e ## and then merge their options this_options = self._get_options(this_value) that_options = self._get_options(other_value) if this_options is not None and that_options is not None: new_value[u"options"] = OptionsSet(this_options).merge( OptionsSet(that_options)).store elif this_options is not None or that_options is not None: new_value[ u"options"] = this_options if this_options is not None else that_options else: pass return new_value
def test_wildcard(self): options_1 = { u"colour": { u"type": VALUE_TYPE_SET_STRING, u"name": u"colour", u"description": u"please choose one of the colours", VALUES_KEY_NAME: [{ u"type": VALUE_TYPE_SET_STRING, u"name": u"red", u"description": u"the colour red", u"image_uri": u"http://something.com/khgfdkyg.png", u"value": u"red", u"options": { u"apples": { u"type": VALUE_TYPE_SET_STRING, u"values": [u"cox", u"jazz", u"bramley"] } } }, { u"type": VALUE_TYPE_SET_STRING, u"name": u"blue", u"description": u"the colour blue", u"image_uri": u"http://something.com/khgfdkyg.png", u"value": u"blue" }] } } options_2 = {u"*/apples": [u"cox", u"jazz"]} set1 = OptionsSet(options_1) set2 = OptionsSet(options_2) self.assertEqual(set1.matcher.options.keys(), [u"colour", u"colour/apples"]) self.assertEqual(set2.matcher.options.keys(), [u"*/apples"]) mega_store_1 = set1.mega_store(set2) mega_store_2 = set2.mega_store(set1) self.assertEqual(mega_store_1.keys(), [u"colour"]) merged = set1.merge(set2).store expected = [{ "description": "the colour blue", "image_uri": "http://something.com/khgfdkyg.png", "name": "blue", "type": "set::string", "value": "blue" }, { "description": "the colour red", "image_uri": "http://something.com/khgfdkyg.png", "name": "red", "options": { "apples": ["cox", "jazz"] }, "type": "set::string", "value": "red" }] self.assertEqual(expected, merged["colour"]["values"])