def assert_dict_equal(self, d1, d2, msg=None): """ Uses `np.allclose()` on number arrays. :raises: (AssertionError) Using TestCase's self.failureException """ self.assertIsInstance(d1, dict, "First argument is not a dictionary") self.assertIsInstance(d2, dict, "Second argument is not a dictionary") for node, path in node_generator(d1): # first check that this sub-dict is contained in both dicts try: comp_node = get_by_path(d2, path) except (KeyError, IndexError): standard_msg = "Path {} exists in dict 1, but not dict 2.".format( path) self.fail(self._formatMessage(msg, standard_msg)) self.assertIsInstance( comp_node, dict, "Value at path {} is not a dict.".format(path)) # check that each key in the first is contained in the second for key, val in node.items(): if isinstance(val, dict): continue # this gets tested as its own node # check that the values at this key are equal val_path = path + (key, ) try: comp_val = comp_node[key] except KeyError: standard_msg = "Path {} exists in dict 1, but not dict 2.".format( self._format_path(val_path)) self.fail(self._formatMessage(msg, standard_msg)) if isinstance(val, np.ndarray) or isinstance( comp_val, np.ndarray): if np.array_equal(val, comp_val): continue elif val == comp_val: continue if is_num_list(val) and is_num_list(comp_val): if np.allclose(val, comp_val): continue standard_msg = ("Value comparison failed at path {}.\n" "{} != {}".format(self._format_path(val_path), val, comp_val)) self.fail(self._formatMessage(msg, standard_msg)) # finally, check that keys in the second are in the first for key in comp_node: val_path = path + (key, ) if key not in node: standard_msg = "Path {} exists in dict 2, but not dict 1.".format( self._format_path(val_path)) self.fail(self._formatMessage(msg, standard_msg))
def test_get_by_path(self): # should be able to traverse into a nested dict/list with key array figure = {'data': [{}, {'marker': {'color': ['red', 'blue']}}]} path = ('data', 1, 'marker', 'color') value = get_by_path(figure, path) expected_value = ['red', 'blue'] self.assertEqual(value, expected_value)
def test_get_by_path(self): # should be able to traverse into a nested dict/list with key array figure = {"data": [{}, {"marker": {"color": ["red", "blue"]}}]} path = ("data", 1, "marker", "color") value = get_by_path(figure, path) expected_value = ["red", "blue"] self.assertEqual(value, expected_value)
def get_attributes_dicts(object_name, parent_object_names=()): """ Returns *all* attribute information given the context of parents. The response has the form: { ('some', 'path'): {}, ('some', 'other', 'path'): {}, ... 'additional_attributes': {} } There may be any number of paths mapping to attribute dicts. There will be one attribute dict under 'additional_attributes' which will usually be empty. :param (str|unicode) object_name: The object name whose attributes we want. :param (list[str|unicode]) parent_object_names: Names of parent objects. :return: (dict) """ object_dict = OBJECTS[object_name] # If we patched this object, we may have added hard-coded attrs. additional_attributes = object_dict['additional_attributes'] # We should also one or more paths where attributes are defined. attribute_paths = list(object_dict['attribute_paths']) # shallow copy # If we have parent_names, some of these attribute paths may be invalid. for parent_object_name in reversed(parent_object_names): if parent_object_name in ARRAYS: continue parent_object_dict = OBJECTS[parent_object_name] parent_attribute_paths = parent_object_dict['attribute_paths'] for path in list(attribute_paths): if not _is_valid_sub_path(path, parent_attribute_paths): attribute_paths.remove(path) # We return a dict mapping paths to attributes. We also add in additional # attributes if defined. attributes_dicts = { path: utils.get_by_path(GRAPH_REFERENCE, path) for path in attribute_paths } attributes_dicts['additional_attributes'] = additional_attributes return attributes_dicts
def get_attributes_dicts(object_name, parent_object_names=()): """ Returns *all* attribute information given the context of parents. The response has the form: { ('some', 'path'): {}, ('some', 'other', 'path'): {}, ... 'additional_attributes': {} } There may be any number of paths mapping to attribute dicts. There will be one attribute dict under 'additional_attributes' which will usually be empty. :param (str|unicode) object_name: The object name whose attributes we want. :param (list[str|unicode]) parent_object_names: Names of parent objects. :return: (dict) """ object_dict = OBJECTS[object_name] # If we patched this object, we may have added hard-coded attrs. additional_attributes = object_dict['additional_attributes'] # We should also one or more paths where attributes are defined. attribute_paths = list(object_dict['attribute_paths']) # shallow copy # If we have parent_names, some of these attribute paths may be invalid. for parent_object_name in reversed(parent_object_names): if parent_object_name in ARRAYS: continue parent_object_dict = OBJECTS[parent_object_name] parent_attribute_paths = parent_object_dict['attribute_paths'] for path in list(attribute_paths): if not _is_valid_sub_path(path, parent_attribute_paths): attribute_paths.remove(path) # We return a dict mapping paths to attributes. We also add in additional # attributes if defined. attributes_dicts = {path: utils.get_by_path(GRAPH_REFERENCE, path) for path in attribute_paths} attributes_dicts['additional_attributes'] = additional_attributes return attributes_dicts
def test_numpy_integer_import(self): # should generate a figure with subplots of array and not throw a ValueError import numpy as np import plotly.graph_objects as go from plotly.subplots import make_subplots indices_rows = np.array([1], dtype=np.int) indices_cols = np.array([1], dtype=np.int) fig = make_subplots(rows=1, cols=1) fig.add_trace(go.Scatter(y=[1]), row=indices_rows[0], col=indices_cols[0]) data_path = ("data", 0, "y") value = get_by_path(fig, data_path) expected_value = (1, ) self.assertEqual(value, expected_value)
def get_attributes_dicts(object_name, parent_object_names=()): """ Returns *all* attribute information given the context of parents. The response has the form: { ('some', 'path'): {}, ('some', 'other', 'path'): {}, ... 'additional_attributes': {} } There may be any number of paths mapping to attribute dicts. There will be one attribute dict under 'additional_attributes' which will usually be empty. :param (str|unicode) object_name: The object name whose attributes we want. :param (list[str|unicode]) parent_object_names: Names of parent objects. :return: (dict) """ object_dict = OBJECTS[object_name] # If we patched this object, we may have added hard-coded attrs. additional_attributes = object_dict['additional_attributes'] # We should also one or more paths where attributes are defined. attribute_paths = list(object_dict['attribute_paths']) # shallow copy # Map frame 'data' and 'layout' to previously-defined figure attributes. # Examples of parent_object_names changes: # ['figure', 'frames'] --> ['figure', 'frames'] # ['figure', 'frames', FRAME_NAME] --> ['figure'] # ['figure', 'frames', FRAME_NAME, 'data'] --> ['figure', 'data'] # ['figure', 'frames', FRAME_NAME, 'layout'] --> ['figure', 'layout'] # ['figure', 'frames', FRAME_NAME, 'foo'] --> # ['figure', 'frames', FRAME_NAME, 'foo'] # [FRAME_NAME, 'layout'] --> ['figure', 'layout'] if FRAME_NAME in parent_object_names: len_parent_object_names = len(parent_object_names) index = parent_object_names.index(FRAME_NAME) if len_parent_object_names == index + 1: if object_name in ('data', 'layout'): parent_object_names = ['figure', object_name] elif len_parent_object_names > index + 1: if parent_object_names[index + 1] in ('data', 'layout'): parent_object_names = (['figure'] + list(parent_object_names)[index + 1:]) # If we have parent_names, some of these attribute paths may be invalid. for parent_object_name in reversed(parent_object_names): if parent_object_name in ARRAYS: continue parent_object_dict = OBJECTS[parent_object_name] parent_attribute_paths = parent_object_dict['attribute_paths'] for path in list(attribute_paths): if not _is_valid_sub_path(path, parent_attribute_paths): attribute_paths.remove(path) # We return a dict mapping paths to attributes. We also add in additional # attributes if defined. attributes_dicts = { path: utils.get_by_path(GRAPH_REFERENCE, path) for path in attribute_paths } attributes_dicts['additional_attributes'] = additional_attributes return attributes_dicts
def get_attributes_dicts(object_name, parent_object_names=()): """ Returns *all* attribute information given the context of parents. The response has the form: { ('some', 'path'): {}, ('some', 'other', 'path'): {}, ... 'additional_attributes': {} } There may be any number of paths mapping to attribute dicts. There will be one attribute dict under 'additional_attributes' which will usually be empty. :param (str|unicode) object_name: The object name whose attributes we want. :param (list[str|unicode]) parent_object_names: Names of parent objects. :return: (dict) """ object_dict = OBJECTS[object_name] # If we patched this object, we may have added hard-coded attrs. additional_attributes = object_dict["additional_attributes"] # We should also one or more paths where attributes are defined. attribute_paths = list(object_dict["attribute_paths"]) # shallow copy # Map frame 'data' and 'layout' to previously-defined figure attributes. # Examples of parent_object_names changes: # ['figure', 'frames'] --> ['figure', 'frames'] # ['figure', 'frames', FRAME_NAME] --> ['figure'] # ['figure', 'frames', FRAME_NAME, 'data'] --> ['figure', 'data'] # ['figure', 'frames', FRAME_NAME, 'layout'] --> ['figure', 'layout'] # ['figure', 'frames', FRAME_NAME, 'foo'] --> # ['figure', 'frames', FRAME_NAME, 'foo'] # [FRAME_NAME, 'layout'] --> ['figure', 'layout'] if FRAME_NAME in parent_object_names: len_parent_object_names = len(parent_object_names) index = parent_object_names.index(FRAME_NAME) if len_parent_object_names == index + 1: if object_name in ("data", "layout"): parent_object_names = ["figure", object_name] elif len_parent_object_names > index + 1: if parent_object_names[index + 1] in ("data", "layout"): parent_object_names = ["figure"] + list(parent_object_names)[index + 1 :] # If we have parent_names, some of these attribute paths may be invalid. for parent_object_name in reversed(parent_object_names): if parent_object_name in ARRAYS: continue parent_object_dict = OBJECTS[parent_object_name] parent_attribute_paths = parent_object_dict["attribute_paths"] for path in list(attribute_paths): if not _is_valid_sub_path(path, parent_attribute_paths): attribute_paths.remove(path) # We return a dict mapping paths to attributes. We also add in additional # attributes if defined. attributes_dicts = {path: utils.get_by_path(GRAPH_REFERENCE, path) for path in attribute_paths} attributes_dicts["additional_attributes"] = additional_attributes return attributes_dicts
def assert_dict_equal(self, d1, d2, msg=None): """ Uses `np.allclose()` on number arrays. :raises: (AssertionError) Using TestCase's self.failureException """ self.assertIsInstance(d1, dict, 'First argument is not a dictionary') self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') for node, path in node_generator(d1): # first check that this sub-dict is contained in both dicts try: comp_node = get_by_path(d2, path) except (KeyError, IndexError): standard_msg = ( 'Path {} exists in dict 1, but not dict 2.' .format(path) ) self.fail(self._formatMessage(msg, standard_msg)) self.assertIsInstance( comp_node, dict, 'Value at path {} is not a dict.'.format(path) ) # check that each key in the first is contained in the second for key, val in node.items(): if isinstance(val, dict): continue # this gets tested as its own node # check that the values at this key are equal val_path = path + (key, ) try: comp_val = comp_node[key] except KeyError: standard_msg = ( 'Path {} exists in dict 1, but not dict 2.' .format(self._format_path(val_path)) ) self.fail(self._formatMessage(msg, standard_msg)) if (isinstance(val, np.ndarray) or isinstance(comp_val, np.ndarray)): if np.array_equal(val, comp_val): continue elif val == comp_val: continue if is_num_list(val) and is_num_list(comp_val): if np.allclose(val, comp_val): continue standard_msg = ( 'Value comparison failed at path {}.\n' '{} != {}' .format(self._format_path(val_path), val, comp_val) ) self.fail(self._formatMessage(msg, standard_msg)) # finally, check that keys in the second are in the first for key in comp_node: val_path = path + (key, ) if key not in node: standard_msg = ( 'Path {} exists in dict 2, but not dict 1.' .format(self._format_path(val_path)) ) self.fail(self._formatMessage(msg, standard_msg))