def test_iteritems_and_items(self): nd = NormalizedDict({'A': 1, 'b': 2, 'C': 3}) iterator = nd.iteritems() assert_false(isinstance(iterator, list)) assert_equals(list(iterator), [('A', 1), ('b', 2), ('C', 3)]) assert_equals(list(iterator), []) assert_equals(list(nd.iteritems()), nd.items())
class DiffResults(object): def __init__(self): self._stats = NormalizedDict() self.column_names = [] @property def rows(self): return (RowStatus(name, statuses) for name, statuses in sorted(self._stats.items())) def add_output(self, path, column=None): self._add_suite(ExecutionResult(path).suite) self.column_names.append(column or path) for stats in self._stats.values(): self._add_missing_statuses(stats) def _add_suite(self, suite): self._add_to_stats(suite) for sub_suite in suite.suites: self._add_suite(sub_suite) for test in suite.tests: self._add_to_stats(test) def _add_to_stats(self, item): stats = self._stats.setdefault(item.longname, []) self._add_missing_statuses(stats) stats.append(ItemStatus(item)) def _add_missing_statuses(self, stats): while len(stats) < len(self.column_names): stats.append(MissingStatus())
def test_keys_values_and_items_are_returned_in_same_order(self): nd = NormalizedDict() for i, c in enumerate('abcdefghijklmnopqrstuvwxyz0123456789!"#%&/()=?'): nd[c.upper()] = i nd[c+str(i)] = 1 assert_equals(nd.items(), zip(nd.keys(), nd.values())) assert_equals(list(nd.iteritems()), zip(nd.iterkeys(), nd.itervalues()))
def test_iteritems_and_items(self): nd = NormalizedDict({"A": 1, "b": 2, "C": 3}) iterator = nd.iteritems() assert_false(isinstance(iterator, list)) assert_equals(list(iterator), [("A", 1), ("b", 2), ("C", 3)]) assert_equals(list(iterator), []) assert_equals(list(nd.iteritems()), nd.items())
def test_iteritems_and_items(self): nd = NormalizedDict({'A': 1, 'b': 2, 'C': 3}) iterator = nd.iteritems() assert_false(isinstance(iterator, list)) assert_equal(list(iterator), [('A', 1), ('b', 2), ('C', 3)]) assert_equal(list(iterator), []) assert_equal(list(nd.iteritems()), nd.items())
def convert_to_bool(self, value, *true_false, **options): if true_false: lists = NormalizedDict({'true': [], 'false': []}) # choose the first list to fill with items # based on given TRUE or FALSE specifier: try: t_or_f_list = lists[true_false[0]] except KeyError: raise ValueError("Expected TRUE or FALSE, not: %s" % repr(true_false[0])) for item in true_false[1:]: if item in lists: #==> is new TRUE or FALSE specifier #==> switch to corresponding list t_or_f_list = lists[item] if t_or_f_list: raise ValueError("Multiple %s lists specfied." % normalize(item).upper()) else: t_or_f_list.append(item) for key, items in lists.items(): if not items: raise ValueError("No %s list specified." % key.upper()) if RobotBool(options.get('normalized', True)): boolcls = normboolclass(**lists) else: boolcls = boolclass(**lists) else: boolcls = options.get('boolclass') or options.get('booltype') if not boolcls: # fallback to robot's default bool conversion return BUILTIN.convert_to_boolean(value) if isstring(boolcls): try: # is a registered bool class name? boolcls = BOOL_CLASSES[boolcls] except KeyError: if '.' not in boolcls: raise ValueError( "No such bool class registered: '%s'" % boolcls) modname, clsname = boolcls.rsplit('.', 1) try: # is an importable 'module.class' string? boolcls = getattr(__import__(modname), clsname) except (ImportError, AttributeError): raise ValueError( "Can't import bool class: '%s'" % boolcls) elif not isboolclass(boolcls): raise TypeError("No bool class: %s" % repr(boolcls)) BUILTIN._log_types(value) return boolcls(value)
def convert_to_bool(self, value, *true_false, **options): if true_false: lists = NormalizedDict({'true': [], 'false': []}) # choose the first list to fill with items # based on given TRUE or FALSE specifier: try: t_or_f_list = lists[true_false[0]] except KeyError: raise ValueError("Expected TRUE or FALSE, not: %s" % repr(true_false[0])) for item in true_false[1:]: if item in lists: #==> is new TRUE or FALSE specifier #==> switch to corresponding list t_or_f_list = lists[item] if t_or_f_list: raise ValueError("Multiple %s lists specfied." % normalize(item).upper()) else: t_or_f_list.append(item) for key, items in lists.items(): if not items: raise ValueError("No %s list specified." % key.upper()) if RobotBool(options.get('normalized', True)): boolcls = normboolclass(**lists) else: boolcls = boolclass(**lists) else: boolcls = options.get('boolclass') or options.get('booltype') if not boolcls: # fallback to robot's default bool conversion return BUILTIN.convert_to_boolean(value) if isstring(boolcls): try: # is a registered bool class name? boolcls = BOOL_CLASSES[boolcls] except KeyError: if '.' not in boolcls: raise ValueError( "No such bool class registered: '%s'" % boolcls) modname, clsname = boolcls.rsplit('.', 1) try: # is an importable 'module.class' string? boolcls = getattr(__import__(modname), clsname) except (ImportError, AttributeError): raise ValueError("Can't import bool class: '%s'" % boolcls) elif not isboolclass(boolcls): raise TypeError("No bool class: %s" % repr(boolcls)) BUILTIN._log_types(value) return boolcls(value)
class VariableStore(object): def __init__(self, variables): self.store = NormalizedDict(ignore='_') self._variables = variables def resolve_delayed(self): for name, value in self.store.items(): try: self._resolve_delayed(name, value) except DataError: pass def _resolve_delayed(self, name, value): if not isinstance(value, DelayedVariable): return value self.store[name] = value.resolve(name, self._variables) return self.store[name] def find(self, name): return self._resolve_delayed(name, self.store[name]) def __getitem__(self, name): return self.find(name) # TODO: __getitem__ vs find def clear(self): self.store.clear() def add(self, name, value, overwrite=True): if overwrite or name not in self.store: self.store[name] = value def remove(self, name): if name in self.store: self.store.pop(name) def __len__(self): return len(self.store) def __iter__(self): return iter(self.store) def __contains__(self, name): return name in self.store
def test_original_keys_are_preserved(self): nd = NormalizedDict({'low': 1, 'UP': 2}) nd['up'] = nd['Spa Ce'] = 3 assert_equals(nd.keys(), ['low', 'Spa Ce', 'UP']) assert_equals(nd.items(), [('low', 1), ('Spa Ce', 3), ('UP', 3)])
class ViewAllTagsDialog(wx.Frame): def __init__(self, controller, frame): wx.Frame.__init__(self, frame, title="View all tags", style=wx.SYSTEM_MENU|wx.CAPTION|wx.CLOSE_BOX|wx.CLIP_CHILDREN|wx.FRAME_FLOAT_ON_PARENT) self.frame = frame self.tree = self.frame.tree self._controller = controller self._results = NormalizedDict() self.selected_tests = list() self.tagged_test_cases = list() self.unique_tags = 0 self.total_test_cases = 0 self._index = -1 self._build_ui() self._make_bindings() def _build_ui(self): self.SetSize((500,400)) parent_x, parent_y = self.frame.GetPosition() parent_size_x, parent_size_y = self.frame.tree.GetSize() self.SetPosition((parent_x+parent_size_x+50,parent_y+50)) self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DFACE)) self.SetSizer(wx.BoxSizer(wx.VERTICAL)) self._build_notebook() self._build_tag_lister() self._build_controls() self._build_footer() def _build_tag_lister(self): panel_tag_vw = wx.Panel(self._notebook) sizer_tag_vw = wx.BoxSizer(wx.VERTICAL) panel_tag_vw.SetSizer(sizer_tag_vw) self._tags_list = TagsListCtrl(panel_tag_vw,style=wx.LC_REPORT) self._tags_list.InsertColumn(0, "Tag", width=200) self._tags_list.InsertColumn(1, "Occurrences", width=25, format=wx.LIST_FORMAT_CENTER) self._tags_list.SetMinSize((450, 250)) self._tags_list.set_dialog(self) sizer_tag_vw.Add(self._tags_list, 1, wx.ALL | wx.EXPAND, 3) self._notebook.AddPage(panel_tag_vw, "The List") def _build_controls(self): self._clear_button = ButtonWithHandler(self, 'Refresh', self.OnClear) self._show_tagged_tests_button = ButtonWithHandler(self, 'Included Tag Search') self._show_excluded_tests_button = ButtonWithHandler(self, 'Excluded Tag Search') controls = wx.BoxSizer(wx.HORIZONTAL) controls.Add(self._show_tagged_tests_button, 0, wx.ALL, 3) controls.Add(self._show_excluded_tests_button, 0, wx.ALL, 3) controls.Add(self._clear_button, 0, wx.ALL, 3) self.Sizer.Add(controls, 0, wx.ALL | wx.EXPAND, 3) def _build_footer(self): footer = wx.BoxSizer(wx.HORIZONTAL) self._footer_text = wx.StaticText(self, -1, '') footer.Add(self._footer_text) self.Sizer.Add(footer, 0, wx.ALL, 3) def _build_notebook(self): self._notebook = wx.Notebook(self, wx.ID_ANY, style=wx.NB_TOP) self.Sizer.Add(self._notebook, 1, wx.ALL | wx.EXPAND, 3) def _make_bindings(self): self.Bind(wx.EVT_CLOSE, self._close_dialog) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnTagSelected) self._tags_list.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnRightClick) def _execute(self): self._clear_search_results() self._search_for_tags() self.tagged_test_cases = list() self.unique_tags = 0 for tag_name, tests in self._results: self._tags_list.SetClientData(self.unique_tags, (tests,tag_name)) self._tags_list.InsertStringItem(self.unique_tags, unicode(tag_name)) self.tagged_test_cases += tests self._tags_list.SetStringItem(self.unique_tags, 1, str(len(tests))) self.unique_tags += 1 self._tags_list.SetColumnWidth(1,wx.LIST_AUTOSIZE_USEHEADER) self._tags_list.setResizeColumn(1) self.tagged_test_cases = list(set(self.tagged_test_cases)) self.update_footer() def update_footer(self): footer_string = "Total tests %d, Tests with tags %d, Unique tags %d, Currently selected tests %d" % \ (self.total_test_cases, len(self.tagged_test_cases), self.unique_tags, len(self.selected_tests)) self._footer_text.SetLabel(footer_string) def show_dialog(self): self._execute() if not self.IsShown(): self.Show() self.Raise() def _clear_search_results(self): self.selected_tests = list() self._tags_list.ClearAll() def _add_view_components(self): pass def _search_for_tags(self): self._unique_tags = NormalizedDict() self._tagit = dict() self._test_cases = list() for test in self.frame._controller.all_testcases(): self._test_cases.append(test) for tag in test.tags: if tag.is_empty() or len(unicode(tag).strip()) == 0: continue else: tag_name = unicode(tag) if self._unique_tags.has_key(tag_name): self._unique_tags[tag_name].append(test) self._tagit[tag_name].append(tag) else: self._unique_tags.set(tag_name, [test]) self._tagit[tag_name] = [tag] self.total_test_cases = len(self._test_cases) self._results = sorted(self._unique_tags.items(), key=lambda x: len(x[1]), reverse=True) def GetListCtrl(self): return self._tags_list def OnColClick(self, event): event.Skip() def _add_checked_tags_into_list(self): tags = [] for tests,tag_name in self._tags_list.get_checked_items(): tags.append(tag_name) return tags def OnIncludedTagSearch(self, event): included_tags = self._add_checked_tags_into_list() RideOpenTagSearch(includes=' '.join(included_tags), excludes="").publish() def OnExcludedTagSearch(self, event): excluded_tags = self._add_checked_tags_into_list() RideOpenTagSearch(includes="", excludes=' '.join(excluded_tags)).publish() def OnClear(self, event): self._execute() for tag_name, tests in self._results: self.tree.DeselectTests(tests) for item in self.tree.GetItemChildren(): if not isinstance(item.GetData(), ResourceRootHandler or ResourceFileHandler): self.tree.CollapseAllSubNodes(item) self.update_footer() def OnSelectAll(self, event): all_tests = [] for tag_name, tests in self._results: all_tests += tests self.tree.SelectTests(all_tests) self._tags_list.CheckAll() def OnRightClick(self, event): self._index = event.GetIndex() self.tree._popup_creator.show(self, PopupMenuItems(self, ["Select all", "Clear", "---", "Rename", "Delete", "---", "Show tests with this tag", "Show tests without this tag"]), self._controller) def OnShowTestsWithThisTag(self, event): if self._index == -1: return tests,tag_name = self._tags_list.GetClientData(self._index) RideOpenTagSearch(includes=tag_name, excludes="").publish() def OnShowTestsWithoutThisTag(self, event): if self._index == -1: return tests,tag_name = self._tags_list.GetClientData(self._index) RideOpenTagSearch(includes="", excludes=tag_name).publish() def OnRename(self, event): if self._index == -1: return tests,tag_name = self._tags_list.GetClientData(self._index) tags_to_rename = self._tagit[tag_name] name = wx.GetTextFromUser(message="Renaming tag '%s'." % tag_name, default_value=tag_name, caption='Rename') if name: for tag in tags_to_rename: tag.controller.execute(ChangeTag(tag, name)) self._execute() for tag_name, tests in self._results: self.tree.DeselectTests(tests) def OnDelete(self, event): if self._index == -1: return tests,tag_name = self._tags_list.GetClientData(self._index) tags_to_delete = self._tagit[tag_name] if wx.MessageBox("Delete a tag '%s' ?" % tag_name, caption='Confirm', style=wx.YES_NO | wx.ICON_QUESTION) == wx.YES: for tag in tags_to_delete: tag.execute(DeleteTag()) self._execute() for tag_name, tests in self._results: self.tree.DeselectTests(tests) def _close_dialog(self, event): if event.CanVeto(): self.Hide() else: self.Destroy() def OnTagSelected(self, event): item = self._tags_list.GetItem(event.GetIndex()) def item_in_kw_list_checked(self, index, flag): self.selected_tests = list() if flag == False: tests, tag_name = self._tags_list.GetClientData(index) self.tree.DeselectTests(tests) if self._tags_list.get_number_of_checked_items() > 0: for tests,tag_name in self._tags_list.get_checked_items(): self.selected_tests += tests self.tree.SelectTests(tests) self.selected_tests = list(set(self.selected_tests)) self.update_footer()
def test_original_keys_are_preserved(self): nd = NormalizedDict({'low': 1, 'UP': 2}) nd['up'] = nd['Spa Ce'] = 3 assert_equal(list(nd.keys()), ['low', 'Spa Ce', 'UP']) assert_equal(list(nd.items()), [('low', 1), ('Spa Ce', 3), ('UP', 3)])
def test_original_keys_are_preserved(self): nd = NormalizedDict({"low": 1, "UP": 2}) nd["up"] = nd["Spa Ce"] = 3 assert_equals(nd.keys(), ["low", "Spa Ce", "UP"]) assert_equals(nd.items(), [("low", 1), ("Spa Ce", 3), ("UP", 3)])
class VariableStore(object): def __init__(self, variables): self.data = NormalizedDict(ignore='_') self._variables = variables def resolve_delayed(self): for name, value in self.data.items(): try: self._resolve_delayed(name, value) except DataError: pass def _resolve_delayed(self, name, value): if not isinstance(value, VariableTableValueBase): return value try: self.data[name] = value.resolve(self._variables) except DataError as err: # Recursive resolving may have already removed variable. if name in self: self.remove(name) value.report_error(err) raise_not_found('${%s}' % name, self.data, "Variable '${%s}' not found." % name) return self.data[name] def find(self, name): return self._resolve_delayed(name, self.data[name]) def __getitem__(self, name): return self.find(name) # TODO: __getitem__ vs find def clear(self): self.data.clear() def add(self, name, value, overwrite=True, decorated=True): if decorated: name, value = self._undecorate(name, value) if overwrite or name not in self.data: self.data[name] = value def _undecorate(self, name, value): validate_var(name) if name[0] == '@': if not is_list_like(value): self._raise_cannot_set_type(name, value, 'list') value = list(value) if name[0] == '&': if not is_dict_like(value): self._raise_cannot_set_type(name, value, 'dictionary') value = DotDict(value) return name[2:-1], value def _raise_cannot_set_type(self, name, value, expected): raise DataError("Cannot set variable '%s': Expected %s-like value, " "got %s." % (name, expected, type_name(value))) def remove(self, name): if name in self.data: self.data.pop(name) def __len__(self): return len(self.data) def __iter__(self): return iter(self.data) def __contains__(self, name): return name in self.data
class Applications: _database = DataBasePaths(True).getConnectedFile() def __init__(self): self._apps = NormalizedDict() self._old_apps = NormalizedDict() for alias, url in self._get_aliases_and_urls_from_db(): self._old_apps[alias] = url def _get_aliases_and_urls_from_db(self): items = [] for connection in self._read_lines(): items.append(connection.split('\t')) return items def _read_lines(self): if os.path.exists(self._database): f = open(self._database, 'rb') data = f.read().splitlines() f.close() return data return [] def add(self, alias, app): self._apps[alias] = app self._old_apps[alias] = app.rmi_url self._store() def _store(self): data = [ '%s\t%s' % (alias, url) for alias, url in self._old_apps.items() ] self._write('\n'.join(data)) print "*TRACE* Stored to connected applications database: ", data def _write(self, data): f = open(self._database, 'wb') f.write(data) f.close() def has_connected_to_application(self, alias): return self._apps.has_key(alias) def get_application(self, alias): return self._apps[alias] def get_applications(self): return self._apps.values() def get_aliases(self): return self._apps.keys() def delete(self, alias): del (self._apps[normalize(alias)]) del (self._old_apps[normalize(alias)]) self._store() def delete_all_connected(self): for alias in self._apps.keys(): self.delete(alias) def get_alias_for(self, application): for alias, app in self._apps.items(): if app == application: return alias return None def get_url(self, alias): for name, url in self._get_aliases_and_urls_from_db(): if eq(name, alias): return url return None
class VariableStore(object): def __init__(self, variables): self.data = NormalizedDict(ignore='_') self._variables = variables def resolve_delayed(self, item=None): if item: return self._resolve_delayed(*item) for name, value in list(self.data.items()): try: self._resolve_delayed(name, value) except DataError: pass def _resolve_delayed(self, name, value): if not self._is_resolvable(value): return value try: self.data[name] = value.resolve(self._variables) except DataError as err: # Recursive resolving may have already removed variable. if name in self: self.remove(name) value.report_error(err) variable_not_found('${%s}' % name, self.data, "Variable '${%s}' not found." % name) return self.data[name] def _is_resolvable(self, value): try: # isinstance can throw an exception in ironpython and jython return isinstance(value, VariableTableValueBase) except Exception: return False def __getitem__(self, name): return self._resolve_delayed(name, self.data[name]) def update(self, store): self.data.update(store.data) def clear(self): self.data.clear() def add(self, name, value, overwrite=True, decorated=True): if decorated: name, value = self._undecorate(name, value) if overwrite or name not in self.data: self.data[name] = value def _undecorate(self, name, value): validate_var(name) if name[0] == '@': if not is_list_like(value): self._raise_cannot_set_type(name, value, 'list') value = list(value) if name[0] == '&': if not is_dict_like(value): self._raise_cannot_set_type(name, value, 'dictionary') value = DotDict(value) return name[2:-1], value def _raise_cannot_set_type(self, name, value, expected): raise VariableError("Cannot set variable '%s': Expected %s-like value, " "got %s." % (name, expected, type_name(value))) def remove(self, name): if name in self.data: self.data.pop(name) def __len__(self): return len(self.data) def __iter__(self): return iter(self.data) def __contains__(self, name): return name in self.data def as_dict(self, decoration=True): if decoration: variables = (self._decorate(name, self[name]) for name in self) else: variables = self.data return NormalizedDict(variables, ignore='_') def _decorate(self, name, value): if is_dict_like(value): name = '&{%s}' % name elif is_list_like(value): name = '@{%s}' % name else: name = '${%s}' % name return name, value
class Applications: _database = DataBasePaths(True).getConnectedFile() def __init__(self): self._apps = NormalizedDict() self._old_apps = NormalizedDict() for alias, url in self._get_aliases_and_urls_from_db(): self._old_apps[alias] = url def _get_aliases_and_urls_from_db(self): items = [] for connection in self._read_lines(): items.append(connection.rsplit('\t', 1)) return items def _read_lines(self): if os.path.exists(self._database): f = open(self._database, 'rb') data = f.read().splitlines() f.close() return [line for line in data if self._is_valid_connection(line)] return [] def _is_valid_connection(self, line): return len(line.rsplit('\t', 1)) == 2 def add(self, alias, app): self._apps[alias] = app self._old_apps[alias] = app.rmi_url self._store() def _store(self): data = ['%s\t%s' % (alias, url) for alias, url in self._old_apps.items()] data_txt = '\n'.join(data) self._write(data_txt) print "*TRACE* Stored to connected applications database: \n%s" % data_txt def _write(self, data): f = open(self._database, 'wb') f.write(data) f.close() def has_connected_to_application(self, alias): return self._apps.has_key(alias) def get_application(self, alias): return self._apps[alias] def get_applications(self): return self._apps.values() def get_aliases(self): return self._apps.keys() def delete(self, alias): del(self._apps[normalize(alias)]) del(self._old_apps[normalize(alias)]) self._store() def delete_all_connected(self): for alias in self._apps.keys(): self.delete(alias) def get_alias_for(self, application): for alias, app in self._apps.items(): if app == application: return alias return None def get_url(self, alias): for name, url in self._get_aliases_and_urls_from_db(): if eq(name, alias): return url return None
class VariableStore(object): def __init__(self, variables): self.data = NormalizedDict(ignore='_') self._variables = variables def resolve_delayed(self): for name, value in self.data.items(): try: self._resolve_delayed(name, value) except DataError: pass def _resolve_delayed(self, name, value): if not isinstance(value, VariableTableValueBase): return value try: self.data[name] = value.resolve(self._variables) except DataError as err: # Recursive resolving may have already removed variable. if name in self: self.remove(name) value.report_error(err) raise_not_found('${%s}' % name, self.data, "Variable '${%s}' not found." % name) return self.data[name] def __getitem__(self, name): return self._resolve_delayed(name, self.data[name]) def update(self, store): self.data.update(store.data) def clear(self): self.data.clear() def add(self, name, value, overwrite=True, decorated=True): if decorated: name, value = self._undecorate(name, value) if overwrite or name not in self.data: self.data[name] = value def _undecorate(self, name, value): validate_var(name) if name[0] == '@': if not is_list_like(value): self._raise_cannot_set_type(name, value, 'list') value = list(value) if name[0] == '&': if not is_dict_like(value): self._raise_cannot_set_type(name, value, 'dictionary') value = DotDict(value) return name[2:-1], value def _raise_cannot_set_type(self, name, value, expected): raise DataError("Cannot set variable '%s': Expected %s-like value, " "got %s." % (name, expected, type_name(value))) def remove(self, name): if name in self.data: self.data.pop(name) def __len__(self): return len(self.data) def __iter__(self): return iter(self.data) def __contains__(self, name): return name in self.data def as_dict(self): variables = (self._decorate(name, self[name]) for name in self) return NormalizedDict(variables, ignore='_') def _decorate(self, name, value): if is_dict_like(value): name = '&{%s}' % name elif is_list_like(value): name = '@{%s}' % name else: name = '${%s}' % name return name, value
class VariableStore: def __init__(self, variables): self.data = NormalizedDict(ignore='_') self._variables = variables def resolve_delayed(self, item=None): if item: return self._resolve_delayed(*item) for name, value in list(self.data.items()): try: self._resolve_delayed(name, value) except DataError: pass def _resolve_delayed(self, name, value): if not self._is_resolvable(value): return value try: self.data[name] = value.resolve(self._variables) except DataError as err: # Recursive resolving may have already removed variable. if name in self.data: self.data.pop(name) value.report_error(err) variable_not_found('${%s}' % name, self.data) return self.data[name] def _is_resolvable(self, value): try: return isinstance(value, Resolvable) except Exception: return False def __getitem__(self, name): if name not in self.data: variable_not_found('${%s}' % name, self.data) return self._resolve_delayed(name, self.data[name]) def get(self, name, default=NOT_SET, decorated=True): try: if decorated: name = self._undecorate(name) return self[name] except VariableError: if default is NOT_SET: raise return default def update(self, store): self.data.update(store.data) def clear(self): self.data.clear() def add(self, name, value, overwrite=True, decorated=True): if decorated: name, value = self._undecorate_and_validate(name, value) if (overwrite or name not in self.data or isinstance(self.data[name], GlobalVariableValue)): self.data[name] = value def _undecorate(self, name): if not is_assign(name): raise VariableError("Invalid variable name '%s'." % name) return name[2:-1] def _undecorate_and_validate(self, name, value): undecorated = self._undecorate(name) if name[0] == '@': if not is_list_like(value): self._raise_cannot_set_type(name, value, 'list') value = list(value) if name[0] == '&': if not is_dict_like(value): self._raise_cannot_set_type(name, value, 'dictionary') value = DotDict(value) return undecorated, value def _raise_cannot_set_type(self, name, value, expected): raise VariableError( "Cannot set variable '%s': Expected %s-like value, got %s." % (name, expected, type_name(value))) def __len__(self): return len(self.data) def __iter__(self): return iter(self.data) def __contains__(self, name): return name in self.data def as_dict(self, decoration=True): if decoration: variables = (self._decorate(name, self[name]) for name in self) else: variables = self.data return NormalizedDict(variables, ignore='_') def _decorate(self, name, value): if is_dict_like(value): name = '&{%s}' % name elif is_list_like(value): name = '@{%s}' % name else: name = '${%s}' % name return name, value