def main(args=None): import argparse parser = argparse.ArgumentParser( description="Tool to measure resources consumed" " by a group of processes, no matter how hard they fork." " Does that by creating a temp cgroup and running passed command there." ) parser.add_argument("cmdline", nargs="+", help="Command to run and any arguments for it.") parser.add_argument( "-g", "--cgroup", default="bench/tmp", metavar="{ /path | tagged-path }", help="Hierarchy path to create temp-cgroup under" ' ("/" means root cgroup, default: %(default)s).' " Any missing path components will be created." " If relative name is specified, it will be interpreted from /tagged path.", ) parser.add_argument( "-c", "--rcs", default="cpuacct, blkio, memory", metavar="rc1[,rc2,...]", help="Comma-separated list of rc hierarchies to get metrics from (default: %(default)s)." " Should have corresponding path mounted under {}.".format(cg_root), ) parser.add_argument( "-q", "--quiet", action="store_true", help="Redirect stderr/stdout for started pid to /dev/null." ) parser.add_argument("-d", "--debug", action="store_true", help="Verbose operation mode.") opts = parser.parse_args(sys.argv[1:] if args is None else args) global log import logging logging.basicConfig(level=logging.DEBUG if opts.debug else logging.INFO) log = logging.getLogger() # Check all rc tasks-file paths cg_subpath = "tmp.{}".format(cmd_pid) cg_tasks, cg_path = OrderedDict(), join("tagged", opts.cgroup).lstrip("/") for rc in map(bytes.strip, opts.rcs.split(",")): tasks = join(cg_root, rc, cg_path, cg_subpath, "tasks") assert "\n" not in tasks, repr(tasks) os.makedirs(dirname(tasks)) assert exists(tasks), tasks cg_tasks[rc] = tasks # Append cmdline, send data to child data = cg_tasks.values() if opts.quiet: data.append("-") data = "\n".join(it.chain(data, ["\0".join(map(lambda arg: arg.encode("hex"), opts.cmdline))])) cmd_w.write(struct.pack(len_fmt, len(data)) + data) cmd_w.flush() # Wait for signal to start counting mark = cmd_start_r.read(1) ts0 = time() assert mark == ".", repr(mark) cmd_start_r.close() pid, status = os.waitpid(cmd_pid, 0) ts1 = time() err = status >> 8 if status & 0xFF: print("Unclean exit of child pid due to signal: {}".format((status & 0xFF) >> 1)) err = err or 1 # Make sure everything finished running there leftovers = set() for tasks in cg_tasks.values(): with open(tasks) as src: leftovers.update(map(int, src.read().splitlines())) if leftovers: print( "Main pid has finished, but cgroups have leftover threads" " still running: {}".format(", ".join(map(bytes, leftovers))), file=sys.stderr, ) err = err or 1 # Collect/print accounting data acct = OrderedDict() acct["cmd"] = " ".join(opts.cmdline) acct["wall_clock"] = "{:.3f}".format(ts1 - ts0) acct["exit_status"] = "{} {}".format(status >> 8, status & 0xFF >> 1) acct_srcs = OrderedDict() for cg_path in map(dirname, cg_tasks.viewvalues()): for p in os.listdir(cg_path): acct_srcs[p] = join(cg_path, p) acct_nums = OrderedDict( [ ("cpuacct", ["usage", "usage_percpu"]), ( "memory", [ "max_usage_in_bytes", "memsw.max_usage_in_bytes", "kmem.max_usage_in_bytes", "kmem.tcp.max_usage_in_bytes", ], ), ] ) for rc, metrics in acct_nums.viewitems(): for p in metrics: p = "{}.{}".format(rc, p) if p not in acct_srcs: continue with open(acct_srcs[p]) as src: numbers = map(int, src.read().strip().split()) acct[p] = " ".join(map(num_format, numbers)) for p in "time sectors io_merged io_serviced io_wait_time".split(): p = "blkio.{}".format(p) try: src = acct_srcs[p] except KeyError: pass else: with open(src) as src: src = src.read().splitlines() for line in src: line = line.split() if not line or line[0] == "Total": continue t = None try: dev, t, v = line except ValueError: dev, v = line dev = dev_resolve(*map(int, dev.split(":"))) if not dev: continue label = "{}[{}]".format(p, dev) if t: label += "[{}]".format(t) acct[label] = num_format(int(v)) for k, v in acct.viewitems(): print("{}: {}".format(k, v), file=sys.stderr) # Cleanup tmp dirs leftovers = set() for tasks in cg_tasks.values(): tasks_dir = dirname(tasks) try: os.rmdir(tasks_dir) except (OSError, IOError): leftovers.add(tasks_dir) if leftovers: print("Leftover cgroup dirs remaining:{}\n".format("\n ".join([""] + sorted(leftovers))), file=sys.stderr) err = err or 1 return err
class GridParams(object): def __init__(self, seq_len, num_epochs=[50], learning_rate=[1e-4], batch_size=[24], keep_prob=[0.5], beta1=[0.9], concat_revcom_input=[False], inference_method_key=["inferenceA"]): ''' Pass parameters as a with values as list IE: num_epochs=[50,40], learning_rate=[1e-4,1e-5] ''' self.param_dict = OrderedDict([ ('num_epochs', set(num_epochs)), ('learning_rate', set(learning_rate)), ('batch_size', set(batch_size)), ('keep_prob', set(keep_prob)), ('beta1', set(beta1)), ('concat_revcom_input', set(concat_revcom_input)), ('inference_method_key', set(inference_method_key)), ]) cartesian_prod = itertools.product(*self.param_dict.viewvalues()) self.grid_params_list = [] for param_tuple in cartesian_prod: self.grid_params_list.append(ModelParams(seq_len, *param_tuple)) self.num_perms = len(self.grid_params_list)
def test_repr_recursive_values(self): od = OrderedDict() od[42] = od.viewvalues() r = repr(od) # Cannot perform a stronger test, as the contents of the repr # are implementation-dependent. All we can say is that we # want a str result, not an exception of any sort. self.assertIsInstance(r, str) od[42] = od.viewitems() r = repr(od) # Again. self.assertIsInstance(r, str)
class DataTable(object): def __init__(self, iterable=None, headers=None, value_if_missing=None): """ You must pass in an iterable of: 1. dict, where the keys will be counted as the headers ("fields"), 2. list/tuple/generator, where the first will be assumed to be the fields. 3. DataRow, from a previous DataTable. If your list of lists data doesn't have headers ("fields"), make some and pass them into the `headers` parameter. If your data has headers and you pass in headers anyways, headers acts as a filter and selects the subset of headers you want included. If you pass in a header that isn't in the data, there will be an error. --- If your data is CSV, TSV, or similar format, you can even copy-paste it the relevant script for on-the-fly DataTable construction. See the DataTable.fromcsvstring() method for details. """ self.__data = OrderedDict() if iterable is None: # TODO: this exists so that we can create a DataTable # TODO: with no data, but we can make headers # TODO: what's the best way to address this headers issue? if headers is not None: validate_fields(headers) for header in headers: self.__data[header] = [] return if not hasattr(iterable, '__iter__'): raise Exception("DataTable takes an iterable and " "%s is not an iterable" % type(iterable)) iterator = iterable.__iter__() first_row = iterator.next() # also identifies OrderedDict if isinstance(first_row, dict): if not headers: fields = first_row.keys() else: fields = headers validate_fields(fields) for field in fields: self.__data[field] = [first_row[field]] for i, item in enumerate(iterator, 1): for field in self.fields: try: value = item[field] except KeyError: if value_if_missing is not None: self.__data[field].append(value_if_missing) continue missing = self.__data.viewkeys()-item.viewkeys() raise KeyError("Row %s is missing fields: %s" % (i, missing)) except TypeError: raise TypeError("Although the first row of your data " "was a `dict`-like object, " "row %s was: %s" % (i, type(item))) self.__data[field].append(value) elif isinstance(first_row, (list, tuple, GeneratorType)): # identifies namedtuples, and similar, including this library's # DataRow object. in their case, not only will the first row # not be headers, but we must access `._fields` to get # the header information. from then on, they should be the same. if isinstance(first_row, tuple) and hasattr(first_row, '_fields'): if not headers: fields = first_row._fields else: fields = headers validate_fields(fields) for field, value in izip(fields, first_row): self.__data[field] = [value] else: if not headers: fields = list(first_row) else: fields = headers iterator = chain((first_row,), iterator) validate_fields(fields) for field in fields: self.__data[field] = [] for i, item in enumerate(iterator): if not isinstance(item, (list, tuple, GeneratorType)): raise TypeError("Although the first row of your data " "was a `list`, `tuple`, or `generator`" "-like object, row %s was: " "%s" % (i, type(item))) if not hasattr(item, '__len__'): item = tuple(item) if len(self.fields) != len(item): raise Exception("Row %s's length (%s) does not match " "headers' length (%s)" % (i, len(self.fields), len(item))) for field, value in izip(self.fields, item): self.__data[field].append(value) else: raise Exception("Unrecognized row type: %s" % type(first_row)) @property def fields(self): """ A shallow copy of the list of fields in the DataTable. If you modify the DataTable, this list will not update. """ return self.__data.keys() @fields.setter def fields(self, new_fieldnames): """ Overwrite all field names with new field names. Mass renaming. """ if len(new_fieldnames) != len(self.fields): raise Exception("Cannot replace fieldnames (len: %s) with list of " "incorrect length (len: %s)" % (len(new_fieldnames), len(self.fields))) for old_name, new_name in izip(self.fields, new_fieldnames): # use pop instead of `del` in case old_name == new_name self.__data[new_name] = self.__data.pop(old_name) @classmethod def fromcolumns(cls, fields, columns): if len(fields) != len(columns): raise Exception("When constructing .fromcolumns, the number of " "fields (%s) must equal the number of columns (%s)" % (len(fields), len(columns))) new_table = cls() for field, column in izip(fields, columns): new_table[field] = column return new_table @classmethod def fromcsv(cls, path, delimiter=",", headers=None): f = open(path, 'r') reader = UnicodeRW.UnicodeDictReader(f, delimiter=delimiter) new_table = cls(reader, headers=headers) f.close() return new_table @classmethod def fromcsvstring(cls, csvstring, delimiter=",", quotechar="\""): """ Takes one string that represents the entire contents of the CSV file, or similar delimited file. If you have a list of lists, where the first list is the headers, then use the main constructor. If you see an excess of whitespace in the first column of your data, this is probably because you tried to format a triple-quoted string literal nicely. Don't add any padding to the left. NOTE: Please prefix your triple-quoted string literal with `u` or `ur` as necessary. For copy-pasting directly from Excel, use `ur`. For copy-pasting from something Python (or similar) printed, use `ur`. For something just dumped from Python via __repr__ or some other text source that displays escape characters used, use `u`. --- Implementation notes: This solution was inspired by UnicodeRW. cStringIO.StringIO turns the passed string into a file-like (readble) object. The string must be encoded so that StringIO presents encoded text. In UnicodeRW, codecs.getreader('utf-8') reads an encoded file object to product a decoded file object on the fly. We don't need this. We read the StringIO object line by line into csv.reader, which is consumes encoded text and parses the CSV format out of it. Then we decode each cell one by one as we pass it into the data table csv.QUOTE_NONE (as well as the r-prefix on r'''string''') are vital since we're copy-pasting directly from Excel. The string should be treated as "literally" ("raw") as possible. """ if not isinstance(csvstring, basestring): raise Exception("If trying to construct a DataTable with " "a list of lists, just use the main " "constructor. Make sure to include a header row") stringio = StringIO(csvstring.encode('utf-8')) csv_data = csv.reader((line for line in stringio), delimiter=delimiter, dialect=csv.excel, quotechar=quotechar, quoting=csv.QUOTE_NONE) new_datatable = cls((s.decode('utf-8') for s in row) for row in csv_data) for field in new_datatable.fields: new_datatable[field] = parse_column(new_datatable[field]) return new_datatable @classmethod def fromdict(cls, datadict): """ Constructs a new DataTable using a dictionary of the format: {field1: [a,b,c], field2: [d,e,f], field3: [g,h,i]} ... which most closely matches the internal representation of the DataTable. If it is an OrderedDict, the key order will be preserved. """ new_datatable = cls() for field, column in datadict.items(): new_datatable[field] = column return new_datatable @classmethod def fromexcel(cls, path, sheet_name_or_num=0, headers=None): """ Constructs a new DataTable from an Excel file. Specify sheet_name_or_number to load that specific sheet. Headers will be inferred automatically, but if you'd prefer to load only a subset of all the headers, pass in a list of the headers you'd like as `headers`. --- Alternatively, it's quite simple to: reader = ExcelReader('myfile.xls') reader.change_sheet('default') data = DataTable(reader) """ reader = ExcelRW.UnicodeDictReader(path, sheet_name_or_num) return cls(reader, headers=headers) def __add__(self, other_datatable): return self.concat(other_datatable) def __contains__(self, fieldname): return fieldname in self.__data.viewkeys() def __delitem__(self, key): del self.__data[key] def __eq__(self, other): """ Note that there is a bug (in my opinion) where two OrderedDicts are considered equal even if one dict has more key-value pairs after the initial matching set. The line where we compare the length of the two DataTables and the number of keys is meant to protect against this bug. """ if not isinstance(other, DataTable): raise TypeError("Cannot compare DataTables with `%s` " "for equality" % type(other)) if len(self) != len(other) or len(self.fields) != len(other.fields): return False for selfrow, otherrow in izip(self, other): if selfrow != otherrow: return False return True def __getitem__(self, item): """ Pass in a fieldname to retrieve a column: column = dt['column_name'] Or slice the DataTable like a list: sliced = dt[:30:2] """ if isinstance(item, slice): start, stop, step = item.indices(len(self)) sliced_table = DataTable() for field in self.fields: sliced_table[field] = self.__data[field][start:stop:step] return sliced_table elif isinstance(item, (list, tuple)): return [self.__getitem__(colname) for colname in item] elif isinstance(item, basestring): if item not in self: raise KeyError("DataTable does not have column `%s`" % item) return self.__data[item] elif isinstance(item, (int, long)): return self.row(item) else: raise KeyError("DataTable does not support indexing with `%s`" % type(item)) def __len__(self): if not self.__data: return 0 else: return len(self.__data.viewvalues().__iter__().next()) def __repr__(self): return str(self) def __setitem__(self, fieldname, column): """ Sets a column with the specified name to the specified value: dt['new_column'] = [1, 2, 3] 1. If the column name doesn't exist, it will be created. 2. If the column value provided is a tuple, it will be cast to a list. 3. If the column value isn't a list, tuple, or array, it will be assumed that you're trying to set a whole column to some scalar value. For example: dt['another_column'] = True ... will set the entire column, for the length of the table, equal to `True`. """ if not isinstance(column, (list, array)): if isinstance(column, tuple): column = list(column) else: column = [column] * len(self) if self.__data and len(column) != len(self): raise Exception("New column length (%s) must match length " "of table (%s)" % (len(column), len(self))) self.__data[fieldname] = column def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): return self.pretty def __print_table(self, row_delim, header_delim=None, header_pad=u"", pad=u""): """ row_delim default delimiter inserted between columns of every row in the table. header_delim delimiter inserted within the headers. by default takes the value of `row_delim` header_pad put on the sides of the row of headers. pad put on the sides of every row. """ if header_delim is None: header_delim = row_delim num_cols = len(self.fields) accumulator = ((u"%s" + header_delim) * num_cols)[:-len(header_delim)] accumulator = ((header_pad + accumulator + header_pad + u"\n") % tuple(self.fields)) for datarow in self: rowstring = ((u"%s" + row_delim) * num_cols)[:-len(row_delim)] rowstring = (pad + rowstring + pad + u"\n") % tuple(datarow) accumulator += rowstring return accumulator[:-1] @property def html(self): accumulator = u"<table>" accumulator += u"<tr>" + u"".join([u"<th>"+field+u"</th>" for field in self.fields]) + u"</tr>" for datarow in self: accumulator += u"<tr>" + u"".join([u"<td>"+unicode(row)+u"</td>" for row in datarow]) + u"</tr>" return accumulator + u"</table>" @property def jira(self): header, row = u"||", u"|" return self.__print_table(row_delim=row, header_delim=header, header_pad=header, pad=row) # TODO: print a "prettytable" style table @property def pretty(self): return self.t @property def t(self): return self.__print_table(u"\t") def append(self, row): """ Takes a dict, a list/tuple/generator, or a DataRow/namedtuple, and appends it to the "bottom" or "end" of the DataTable. dicts must share the same keys as the DataTable's columns. lists/tuples/generators are simply trusted to be in the correct order and of the correct type (if relevant). If the table being appended to is empty, the columns are inferred from the row being appended. DataRows and namedtuples' `_fields` protected class attribute is checked for the field names. Those are checked against the DataTable and then appended to the relevant columns using those field names. """ if isinstance(row, dict): if self.fields and not set(row.keys()) == set(self.fields): raise Exception("Cannot append a dict to DataTable without " "all keys matching (order being irrelevant).\n" "dict: %s\nDataTable: %s" % (row.keys(), self.fields)) if not self.fields: for field in row.keys(): self.__data[field] = [row[field]] else: for field in self.fields: self.__data[field].append(row[field]) elif isinstance(row, (list, tuple, GeneratorType)): if isinstance(row, tuple) and hasattr(row, '_fields'): fieldnames = row._fields if self.fields and not set(fieldnames) == set(self.fields): raise Exception("Cannot append a Datarow or namedtuple to " "DataTable without all fields matching " "(order being irrelevant).\n" "DataRow/namedtuple: %s\n" "DataTable: %s" % (fieldnames, self.fields)) if not self.fields: for fieldname, value in izip(fieldnames, row): self.__data[fieldname] = [value] else: for fieldname, value in izip(fieldnames, row): self.__data[fieldname].append(value) else: if isinstance(row, GeneratorType): row = tuple(row) if self.fields and not len(row) == len(self.fields): raise Exception("The row being appended does not have the " "correct length. It should have a length " "of %s, but is %s" % (len(self.fields), len(row))) if not self.fields: raise Exception("Can't append a list/tuple/GeneratorType " "as a row if the table doesn't have " "columns defined yet.") # we're just going to hope that the generator's contents are # provided in the right order, and of the right type. for (_, column), element in izip(self.__data.items(), row): column.append(element) else: raise Exception("Unable to append type `%s` to DataTable" % type(row)) def apply(self, func, *fields): """ Applies the function, `func`, to every row in the DataTable. If no fields are supplied, the entire row is passed to `func`. If fields are supplied, the values at all of those fields are passed into func in that order. --- data['diff'] = data.apply(short_diff, 'old_count', 'new_count') """ results = [] for row in self: if not fields: results.append(func(row)) else: if any(field not in self for field in fields): for field in fields: if field not in self: raise Exception("Column `%s` does not exist " "in DataTable" % field) results.append(func(*[row[field] for field in fields])) return results def col(self, col_name_or_num): """ Returns the col at index `colnum` or name `colnum`. """ if isinstance(col_name_or_num, basestring): return self[col_name_or_num] elif isinstance(col_name_or_num, (int, long)): if col_name_or_num > len(self.fields): raise IndexError("Invalid column index `%s` for DataTable" % col_name_or_num) return self.__data[self.fields[col_name_or_num]] def concat(self, other_datatable, inplace=False): """ Concatenates two DataTables together, as long as column names are identical (ignoring order). The resulting DataTable's columns are in the order of the table whose `concat` method was called. """ if not isinstance(other_datatable, DataTable): raise TypeError("`concat` requires a DataTable, not a %s" % type(other_datatable)) # if the self table is empty, we can just return the other table # if we need to do it in place, we just copy over the columns if not self.fields: if inplace: for field in other_datatable.fields: self[field] = other_datatable[field] return self else: return other_datatable if not other_datatable.fields: return self if set(self.fields) != set(other_datatable.fields): raise Exception("Columns do not match:\nself: %s\nother: %s" % (self.fields, other_datatable.fields)) if inplace: for field in self.fields: self.__data[field] = self[field] + other_datatable[field] return self else: new_table = DataTable() for field in self.fields: new_table[field] = self[field] + other_datatable[field] return new_table def copy(self): return self.fromdict(self.__data) def distinct(self, fieldname, key=None): """ Returns the unique values seen at `fieldname`. """ return tuple(unique_everseen(self[fieldname], key=key)) def groupby(self, *groupfields): """ Groups rows in this table according to the unique combinations of `groupfields` combined. """ return GroupbyTable(self, groupfields) # TODO: this is a placeholder and only does a very simple left join. def join(self, right_table, on): keymap = {} for row in right_table: if row[on] in keymap: keymap[row[on]].append(row) else: keymap[row[on]] = [row] new_table = [] for row in self: if row[on] in keymap: left_dict = dict(row.items()) for item in keymap[row[on]]: left_dict_copy = left_dict.copy() left_dict_copy.update(dict(item.items())) new_table.append(left_dict_copy) return DataTable(new_table) def mask(self, masklist): """ `masklist` is an array of Bools or equivalent. This returns a new DataTable using only the rows that were True (or equivalent) in the mask. """ if not hasattr(masklist, '__len__'): masklist = tuple(masklist) if len(masklist) != len(self): raise Exception("Masklist length (%s) must match length " "of DataTable (%s)" % (len(masklist), len(self))) new_datatable = DataTable() for field in self.fields: new_datatable[field] = list(compress(self[field], masklist)) return new_datatable def mutapply(self, function, fieldname): """ Applies `function` in-place to the field name specified. In other words, `mutapply` overwrites column `fieldname` ith the results of applying `function` to each element of that column. """ self[fieldname] = self.apply(function, fieldname) def rename(self, old_fieldname, new_fieldname): """ Renames a specific field, and preserves the underlying order. """ if old_fieldname not in self: raise Exception("DataTable does not have field `%s`" % old_fieldname) if not isinstance(new_fieldname, basestring): raise ValueError("DataTable fields must be strings, not `%s`" % type(new_fieldname)) if old_fieldname == new_fieldname: return new_names = self.fields location = new_names.index(old_fieldname) del new_names[location] new_names.insert(location, new_fieldname) self.fields = new_names def reorder(self, fields_in_new_order): """ Pass in field names in the order you wish them to be swapped. """ if not len(fields_in_new_order) == len(self.fields): raise Exception("Fields to reorder with are not the same length " "(%s) as the original fields (%s)" % (len(fields_in_new_order), len(self.fields))) if not set(fields_in_new_order) == set(self.fields): raise Exception("Fields to reorder with should be the same " "as the original fields") new = OrderedDict() for field in fields_in_new_order: new[field] = self.__data[field] self.__data = new def row(self, rownum): """ Returns the row at index `rownum`. --- Note that the DataRow object returned that represents the data row is constructed on the fly and is a just a shallow copy of the underlying data that does not update dynamically. """ if rownum > len(self): raise IndexError("Invalid row index `%s` for DataTable" % rownum) return datarow_constructor(self.fields)([self[field][rownum] for field in self.fields]) def sample(self, num): """ Returns a new table with rows randomly sampled. We create a mask with `num` True bools, and fill it with False bools until it is the length of the table. We shuffle it, and apply that mask to the table. """ if num > len(self): return self.copy() elif num < 0: raise IndexError("Cannot sample a negative number of rows " "from a DataTable") random_row_mask = ([True] * num) + ([False] * (len(self) - num)) shuffle(random_row_mask) sampled_table = self.mask(random_row_mask) random_col_name = 'random_sorting_column' while random_col_name in sampled_table: random_col_name = '%030x' % randrange(16**30) sampled_table[random_col_name] = [random() for _ in xrange(len(sampled_table))] sampled_table.sort(random_col_name, inplace=True) del sampled_table[random_col_name] return sampled_table def sort(self, fieldname, key=lambda x: x, desc=False, inplace=False): """ This matches Python's built-in sorting signature closely. By default, a new DataTable will be returned and the original will not be mutated. If preferred, specify `inplace=True` in order to mutate the original table. Either way, a reference to the relevant table will be returned. """ try: field_index = tuple(self.fields).index(fieldname) except ValueError: raise ValueError("Sorting on a field that doesn't exist: `%s`" % fieldname) data_cols = izip(*sorted(izip(*[self.__data[field] for field in self.fields]), key=lambda row: key(row[field_index]), reverse=desc)) target_table = self if inplace else DataTable() for field, data_col in izip(self.fields, data_cols): target_table[field] = list(data_col) # Note that sorting in-place still returns a reference # to the table being sorted, for convenience. return target_table def where(self, fieldname, value, negate=False): """ Returns a new DataTable with rows only where the value at `fieldname` == `value`. """ if negate: return self.mask([elem != value for elem in self[fieldname]]) else: return self.mask([elem == value for elem in self[fieldname]]) def wherefunc(self, func, negate=False): """ Applies a function to an entire row and filters the rows based on the boolean output of that function. """ if negate: return self.mask([not func(item) for item in self]) else: return self.mask([func(item) for item in self]) def wherein(self, fieldname, collection, negate=False): """ Returns a new DataTable with rows only where the value at `fieldname` is contained within `collection`. """ if negate: return self.mask([elem not in collection for elem in self[fieldname]]) else: return self.mask([elem in collection for elem in self[fieldname]]) def wheregreater(self, fieldname, value): """ Returns a new DataTable with rows only where the value at `fieldname` > `value`. """ return self.mask([elem > value for elem in self[fieldname]]) def whereless(self, fieldname, value): """ Returns a new DataTable with rows only where the value at `fieldname` < `value`. """ return self.mask([elem < value for elem in self[fieldname]]) def wherenot(self, fieldname, value): """ Logical opposite of `where`. """ return self.where(fieldname, value, negate=True) def wherenotfunc(self, func): """ Logical opposite of `wherefunc`. """ return self.wherefunc(func, negate=True) def wherenotin(self, fieldname, value): """ Logical opposite of `wherein`. """ return self.wherein(fieldname, value, negate=True) def writecsv(self, path, delimiter=","): writer = UnicodeRW.UnicodeWriter(open(path, 'wb'), self.fields, delimiter=delimiter, lineterminator=u"\n") writer.writerow(self.fields) writer.writerows(self) writer.close() def writexlsx(self, path, sheetname="default"): """ Writes this table to an .xlsx file at the specified path. If you'd like to specify a sheetname, you may do so. If you'd like to write one workbook with different DataTables for each sheet, import the `excel` function from acrylic. You can see that code in `utils.py`. Note that the outgoing file is an .xlsx file, so it'd make sense to name that way. """ writer = ExcelRW.UnicodeWriter(path) writer.set_active_sheet(sheetname) writer.writerow(self.fields) writer.writerows(self) writer.save() def __iter__(self): datarow = datarow_constructor(self.fields) for values in izip(*[self.__data[field] for field in self.fields]): yield datarow(values)
class DotMap(MutableMapping, OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() self._dynamic = True if kwargs: if "_dynamic" in kwargs: self._dynamic = kwargs["_dynamic"] if args: d = args[0] # for recursive assignment handling trackedIDs = {id(d): self} if isinstance(d, dict): for k, v in self.__call_items(d): if isinstance(v, dict): if id(v) in trackedIDs: v = trackedIDs[id(v)] else: v = self.__class__(v, _dynamic=self._dynamic) trackedIDs[id(v)] = v if type(v) is list: l = [] for i in v: n = i if isinstance(i, dict): n = self.__class__(i, _dynamic=self._dynamic) l.append(n) v = l self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): if k is not "_dynamic": self._map[k] = v def __call_items(self, obj): if hasattr(obj, "iteritems") and ismethod(getattr(obj, "iteritems")): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): return self._map[k] def __setattr__(self, k, v): if k in { "_map", "_dynamic", "_ipython_canary_method_should_not_exist_", }: super(DotMap, self).__setattr__(k, v) else: self[k] = v def __getattr__(self, k): if k in { "_map", "_dynamic", "_ipython_canary_method_should_not_exist_", }: return super(DotMap, self).__getattr__(k) try: v = super(self.__class__, self).__getattribute__(k) return v except AttributeError: pass return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __add__(self, other): if self.empty(): return other else: self_type = type(self).__name__ other_type = type(other).__name__ msg = "unsupported operand type(s) for +: '{}' and '{}'" raise TypeError(msg.format(self_type, other_type)) def __str__(self): items = [] for k, v in self.__call_items(self._map): # recursive assignment case if id(v) == id(self): items.append("{0}={1}(...)".format(k, self.__class__.__name__)) else: seperator = "\n" if isinstance(v, DotMap) else " " attr_str = f"{k}:{seperator}{v}" attr_str = self._indent(attr_str, 2) items.append(attr_str) joined = "\n".join(items) return joined def __repr__(self): return str(self) def toDict(self): d = {} for k, v in self.items(): if issubclass(type(v), DotMap): # bizarre recursive assignment support if id(v) == id(self): v = d else: v = v.toDict() elif type(v) in (list, tuple): l = [] for i in v: n = i if issubclass(type(i), DotMap): n = i.toDict() l.append(n) if type(v) is tuple: v = tuple(l) else: v = l d[k] = v return d def pprint(self, pformat="dict"): if pformat == "json": print(dumps(self.toDict(), indent=4, sort_keys=True)) else: pprint(self.toDict()) def empty(self): return not any(self) # proper dict subclassing def values(self): return self._map.values() # # ipython support def __dir__(self): return self.keys() @classmethod def parseOther(self, other): if issubclass(type(other), DotMap): return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return self.__class__(self) def __copy__(self): return self.copy() def __deepcopy__(self, memo=None): return self.copy() def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): return self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = cls() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d) # bannerStr def _getListStr(self, items): out = "[" mid = "" for i in items: mid += " {}\n".format(i) if mid != "": mid = "\n" + mid out += mid out += "]" return out def _getValueStr(self, k, v): outV = v multiLine = len(str(v).split("\n")) > 1 if multiLine: # push to next line outV = "\n" + v if type(v) is list: outV = self._getListStr(v) out = "{} {}".format(k, outV) return out def _getSubMapDotList(self, pre, name, subMap): outList = [] if pre == "": pre = name else: pre = "{}.{}".format(pre, name) def stamp(pre, k, v): valStr = self._getValueStr(k, v) return "{}.{}".format(pre, valStr) for k, v in subMap.items(): if isinstance(v, DotMap) and v != DotMap(): subList = self._getSubMapDotList(pre, k, v) outList.extend(subList) else: outList.append(stamp(pre, k, v)) return outList def _getSubMapStr(self, name, subMap): outList = ["== {} ==".format(name)] for k, v in subMap.items(): if isinstance(v, self.__class__) and v != self.__class__(): # break down to dots subList = self._getSubMapDotList("", k, v) # add the divit # subList = ['> {}'.format(i) for i in subList] outList.extend(subList) else: out = self._getValueStr(k, v) # out = '> {}'.format(out) out = "{}".format(out) outList.append(out) finalOut = "\n".join(outList) return finalOut def bannerStr(self): lines = [] previous = None for k, v in self.items(): if previous == self.__class__.__name__: lines.append("-") out = "" if isinstance(v, self.__class__): name = k subMap = v out = self._getSubMapStr(name, subMap) lines.append(out) previous = self.__class__.__name__ else: out = self._getValueStr(k, v) lines.append(out) previous = "other" lines.append("--") s = "\n".join(lines) return s def _indent(self, s_, num_spaces): s = s_.split("\n") if len(s) == 1: return s_ first = s.pop(0) s = [(num_spaces * " ") + line for line in s] s = "\n".join(s) s = first + "\n" + s return s
class Reference(object): def __init__(self, fasta, chrom, output, prefix): self.__log = False self.__logger = '' self.start_logging() self.__fasta = fasta self.__chrom = chrom self.__chromdict = OrderedDict() self.__fastadict = defaultdict(list) self.__output = output self.__prefix = prefix def start_logging(self): self.__log = True self.__logger = logging.getLogger('pipeline.createReference') def show_log(self, level, message): if self.__log: if level == 'debug': self.__logger.debug(message) elif level == 'info': self.__logger.info(message) elif level == 'warning': self.__logger.warning(message) elif level == 'error': self.__logger.error(message) elif level == 'critical': self.__logger.critical(message) else: print message def prepare_chromdict(self): self.show_log('info', 'chromosome which are kept and names are changed:') for entry in readFile_getList_withSep(self.__chrom, '\t'): self.show_log('info', '{0} -> {1}'.format(entry[0], entry[1])) if entry[0][0] != '>': entry[0] = '>{0}'.format(entry[0]) if entry[1][0] != '>': entry[1] = '>{0}'.format(entry[1]) self.__chromdict[entry[0]] = entry[1] def process_Fasta(self): self.show_log('info', 'Reading the fasta file') chromremove = set() linecount = 0 chromid = '' with get_fileobject(self.__fasta, 'r') as filein: for line in filein: if line.startswith('>'): try: chromid = self.__chromdict[line.rstrip('\n')] except KeyError: chromid = '' chromremove.add(line.rstrip('\n')) linecount += 1 elif chromid != '': self.__fastadict[chromid].append(line) else: linecount += 1 self.show_log('info', 'chromosomes removed: {0}'.format(len(chromremove))) self.show_log('info', 'lines removed: {0}'.format(linecount)) def writeFasta(self): self.show_log('info', 'Writing the new fasta files') chromfolder = '{0}chromosomes{1}'.format(self.__output, sep) if not create_Directory(chromfolder): self.show_log('error', "Can't create directory: {0}".format(chromfolder)) exit(2) fastafile = '{0}{1}.fa'.format(self.__output, self.__prefix) self.show_log('info', 'Fasta file: {0}'.format(fastafile)) self.show_log('info', 'Chromosome folder: {0}'.format(chromfolder)) for chrom in self.__chromdict.viewvalues(): if len(self.__fastadict[chrom]) != 0: chromfilename = '{0}{1}.fa'.format(chromfolder, chrom.lstrip('>')) self.show_log('info', "Writing '{0}'".format(chromfilename)) write_list([chrom+'\n'] + self.__fastadict[chrom], chromfilename) write_list([chrom+'\n'] + self.__fastadict[chrom], fastafile, 'a')
class UnitModel(Q.QAbstractItemModel): """Unit model.""" def __init__(self, file_descriptors, file_format=None): """ Create model. Arguments: file_descriptors: File descriptors proxy object. file_format (Optional[str]): File format. Defaults to *None*. """ super(UnitModel, self).__init__() self._file_descriptors = file_descriptors self._units2file = OrderedDict() self._units2stat = OrderedDict() self._emb2ext = OrderedDict() self._ext2emb = OrderedDict() self._extfiles = {} file_dict = {} for handle, info in self._file_descriptors.handle2info.viewitems(): self._units2file[handle] = info.filename self._units2stat[handle] = info.embedded file_dict[info.filename] = handle unit = -10 for fileuid in external_files(file_format): filename = external_file(fileuid) if not filename: continue self._extfiles[fileuid] = filename if fileuid in file_dict: continue self._units2file[unit] = fileuid unit = unit - 1 def file2unit(self, filename, udefault=None, umin=None, umax=None): """Get unit for given file.""" return self._file_descriptors.file2unit(filename, udefault, umin, umax) def addItem(self, filename, udefault=None, umin=None, umax=None): """ Add item to model and units dictionary. Arguments: filename (str): File name. udefault (Optional[int]): Default file descriptor value. Defaults to *None*. umin (Optional[int]): Minimum file descriptor value. Defaults to *None*. umax (Optional[int]): Maximum file descriptor value. Defaults to *None*. Returns: int: File descriptor. Raises: ValueError: if unit can not be registered in [min, max] range. Note: For now, items are always added as external files. """ self.beginInsertRows(Q.QModelIndex(), 0, 0) unit = self.file2unit(filename, udefault, umin, umax) self._units2file[unit] = filename self.endInsertRows() return unit def rowCount(self, parent=Q.QModelIndex()): """ Get number of rows. Arguments: parent (Optional[QModelIndex]): Parent model index. Defaults to invalid model index. Returns: int: Rows count. """ if parent.isValid(): return 0 return len(self._units2file) # pragma pylint: disable=unused-argument, no-self-use def columnCount(self, parent=Q.QModelIndex()): """ Get number of columns. Arguments: parent (Optional[QModelIndex]): Parent model index. Defaults to invalid model index. Returns: int: Columns count. """ return 1 def index(self, row, column, parent=Q.QModelIndex()): """ Get model index for given *row* *column* and *parent*. Arguments: row (int): Row index. column (int): Column index. parent (Optional[QModelIndex]): Parent model index. Defaults to invalid model index. Returns: QModelIndex: Corresponding model index. """ return self.createIndex(row, column) if \ self.hasIndex(row, column, parent) else Q.QModelIndex() def data(self, index, role=Q.Qt.DisplayRole): """ Get data stored by model index for given role. Arguments: index (QModelIndex): Model index. role (Optional[int]): Role of the data. Defaults to *Qt.DisplayRole*. Returns: any: Data stored by model index. """ # pragma pylint: disable=too-many-branches value = None if index.isValid(): unit = self._units2file.keys()[index.row()] filename = None if unit == -1 else self._units2file[unit] if role in (Role.IdRole, ): value = unit elif role in (Role.ValidityRole, ): if unit == -1: value = False else: if filename in self._extfiles: value = self._extfiles.get(filename) is not None else: value = filename is not None elif role in (Role.ReferenceRole, ): if unit == -1: value = False else: value = filename in self._extfiles elif role in (Q.Qt.DisplayRole, Q.Qt.EditRole, Q.Qt.ToolTipRole, Role.CustomRole): undefstr = translate("DataFiles", "undefined") embedstr = translate("DataFiles", "embedded") if unit == -1: value = "<{}>".format(undefstr) # pragma pylint: disable=redefined-variable-type else: if filename in self._extfiles: # for external files 'filename' is its UID if role in (Q.Qt.DisplayRole, Q.Qt.EditRole): value = self._extfiles.get(filename) elif role in (Q.Qt.ToolTipRole, ): value = self._extfiles.get(filename) + \ " ({})".format(filename) else: # Role.CustomRole value = filename else: if role in (Q.Qt.DisplayRole, Q.Qt.EditRole): if filename is not None: state = self._units2stat.get(unit, False) value = os.path.basename(filename) if state: value = value + " ({})".format(embedstr) else: value = undefstr if unit is not None: value = "{} ".format(unit) + value value = "<{}>".format(value) else: # Qt.ToolTipRole, Role.CustomRole value = filename return value # pragma pylint: disable=unused-argument, no-self-use def parent(self, index): """ Get parent model index for the given one. Arguments: index (QModelIndex): Model index. Returns: QModelIndex: Parent model index. """ return Q.QModelIndex() def emb2ext(self, embname, extname): """ Unembed file from the study. Arguments: embname (str): Source path for embedded data file. extname (str): Destination path to put the file to. Returns: str: New file path (external). Raises: ValueError: If external file name is already in use (except when it is a back conversion). """ return self._conversionTemplate(embname, lambda _: extname, self._ext2emb, self._emb2ext, False) def ext2emb(self, extname): """ Embed file to the study. Arguments: extname (str): Source path of the data file. Returns: str: New file path (embedded). Raises: ValueError: If internal file name is already in use (except when it is a back conversion). """ return self._conversionTemplate(extname, self._file_descriptors.ext2emb, self._emb2ext, self._ext2emb, True) def _conversionTemplate(self, oldname, gen_newname, register, unregister, newstate): """ Template for converting file from embedded to external and back. Arguments: oldname (str): Source file path. gen_newname (function): Used to generate new file path. register (dict): Dictionary where to register the resulting conversion. unregister (dict): Dictionary where to unregister a previous conversion. newstate (bool): New embedded state flag. Returns: str: New file path. Raises: ValueError: If new file path is already in use under another entry. """ [unit] = [k for k, v in self._units2file.iteritems() if v == oldname] oldstate = self._units2stat.get(unit, False) if oldstate == newstate: return oldname newname = gen_newname(oldname) # if not a back conversion if oldname not in unregister: # error if the name already exists if newname in self._units2file.viewvalues(): errmsg = translate( "DataFiles", "File {0} is already in use elsewhere in " "the study. It cannot be reused under a " "different file entry.").format(newname) raise ValueError(errmsg) # register register[newname] = oldname # unregister if a back conversion if oldname in unregister: unregister.pop(oldname) # replace the old name by the new one in the dictionary self._units2file[unit] = newname self._units2stat[unit] = newstate self.modelReset.emit() return newname def transferFile(self, filename): """ Called at register time when an external file is embedded / unembedded. Argument: filename (str): File path in its new status. Note: - From external to embedded, the file is copied. - From embedded to external, the file is moved. """ if filename in self._ext2emb: assert filename not in self._emb2ext move_file(self._ext2emb[filename], filename) return if filename in self._emb2ext: assert filename not in self._ext2emb copy_file(self._emb2ext[filename], filename) def other_unit_search(self, filename): """ Looks for `filename` among child and parent stages, but not the current stage. Arguments: filename (str): full path of the file. Returns: int: logical unit of the file if found, else *None*. """ return self._file_descriptors.other_unit_search(filename) def unit_conflict(self, unit, filename): """ Looks for a unit conflict in parent and child stages, i.e. two files sharing the same unit. Arguments: unit (int): logical unit to test. filename (str): full file path to test. Returns: bool: *True* if there is no conflict, *False* otherwise. """ return self._file_descriptors.unit_conflict(unit, filename) def file_conflict(self, unit, filename): """ Looks for a file conflict in parent and child stages, i.e. two different units sharing the same file. Arguments: unit (int): logical unit to test. filename (str): full file path to test. Returns: bool: *True* if there is no conflict, *False* otherwise. """ return self._file_descriptors.file_conflict(unit, filename) def basename_conflict(self, filename): """ Looks for a basename conflict, i.e. a file with the same basename in the current stage. Arguments: filename (str): file path. Note: Due to Salome launcher not supporting two files with the same basename, we have to forbide it for two files of the same stage. """ return self._file_descriptors.basename_conflict(filename)
class Config(MutableMapping, OrderedDict): @classmethod def load(cls, file_path): with open(file_path) as f: params = yaml.load(f.read(), Loader=yaml.FullLoader) # We expand ~ in those yaml entries with `path` # on their keys for making # config files more platform-independent params = { key: (os.path.expanduser(value) if "path" in key and value is not None else value) for key, value in params.items() } return cls(params) def dump(self, file_path): with open(file_path, "w") as f: d = self.to_dict() f.write(yaml.dump(d)) def __init__(self, *args, **kwargs): self._map = OrderedDict() if args: d = args[0] # for recursive assignment handling trackedIDs = {id(d): self} if isinstance(d, dict): for k, v in self.__call_items(d): if isinstance(v, dict): if id(v) in trackedIDs: v = trackedIDs[id(v)] else: v = self.__class__(v) trackedIDs[id(v)] = v if type(v) is list: l = [] for i in v: n = i if isinstance(i, dict): n = self.__class__(i) l.append(n) v = l self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): self._map[k] = v _path_state = list() def __call_items(self, obj): if hasattr(obj, "iteritems") and ismethod(getattr(obj, "iteritems")): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): # print('Called __setitem__') if (k in self._map and not self._map[k] is None and not isinstance(v, type(self._map[k]))): if v is not None: raise ValueError( f"Updating existing value {type(self._map[k])} " f"with different type ({type(v)}).") split_path = k.split(".") current_option = self._map for p in split_path[:-1]: current_option = current_option[p] current_option[split_path[-1]] = v def __getitem__(self, k): split_path = k.split(".") current_option = self._map for p in split_path: if p not in current_option: raise KeyError(p) current_option = current_option[p] return current_option def __setattr__(self, k, v): if k in {"_map", "_ipython_canary_method_should_not_exist_"}: super(Config, self).__setattr__(k, v) else: self[k].update(v) def __getattr__(self, k): if k in {"_map", "_ipython_canary_method_should_not_exist_"}: return super(Config, self).__getattr__(k) try: v = super(self.__class__, self).__getattribute__(k) return v except AttributeError: self._path_state.append(k) pass return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __add__(self, other): if self.empty(): return other else: self_type = type(self).__name__ other_type = type(other).__name__ msg = "unsupported operand type(s) for +: '{}' and '{}'" raise TypeError(msg.format(self_type, other_type)) def __str__(self): items = [] for k, v in self.__call_items(self._map): # recursive assignment case if id(v) == id(self): items.append("{0}={1}(...)".format(k, self.__class__.__name__)) else: items.append("{0}={1}".format(k, repr(v))) joined = ", ".join(items) out = "{0}({1})".format(self.__class__.__name__, joined) return out def __repr__(self): return str(self) def to_dict(self, flatten=False, parent_key="", sep="."): d = {} for k, v in self.items(): if issubclass(type(v), Config): # bizarre recursive assignment support if id(v) == id(self): v = d else: v = v.to_dict() elif type(v) in (list, tuple): l = [] for i in v: n = i if issubclass(type(i), Config): n = i.to_dict() l.append(n) if type(v) is tuple: v = tuple(l) else: v = l d[k] = v if flatten: d = flatten_dict(d, parent_key=parent_key, sep=sep) return d def pprint(self, ): pprint(self.to_dict()) def empty(self): return not any(self) # proper dict subclassing def values(self): return self._map.values() # ipython support def __dir__(self): return list(self.keys()) def _ipython_key_completions_(self): return list(self.keys()) @classmethod def parseOther(cls, other): if issubclass(type(other), Config): return other._map else: return other def __cmp__(self, other): other = Config.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = Config.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = Config.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = Config.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = Config.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = Config.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = Config.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return self.__class__(self) def __copy__(self): return self.copy() def __deepcopy__(self, memo=None): return self.copy() def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) == 1: for key, value in args[0].items(): if key in self and isinstance(self[key], dict): if value is None: self[key] = value else: self[key].update(value) else: pass raise ValueError() elif len(args) > 1: raise NotImplementedError # self._map.update(*args) else: raise NotImplementedError def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = cls() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d) # bannerStr def _getListStr(self, items): out = "[" mid = "" for i in items: mid += " {}\n".format(i) if mid != "": mid = "\n" + mid out += mid out += "]" return out def _getValueStr(self, k, v): outV = v multiLine = len(str(v).split("\n")) > 1 if multiLine: # push to next line outV = "\n" + v if type(v) is list: outV = self._getListStr(v) out = "{} {}".format(k, outV) return out def _getSubMapDotList(self, pre, name, subMap): outList = [] if pre == "": pre = name else: pre = "{}.{}".format(pre, name) def stamp(pre, k, v): valStr = self._getValueStr(k, v) return "{}.{}".format(pre, valStr) for k, v in subMap.items(): if isinstance(v, Config) and v != Config(): subList = self._getSubMapDotList(pre, k, v) outList.extend(subList) else: outList.append(stamp(pre, k, v)) return outList def _getSubMapStr(self, name, subMap): outList = ["== {} ==".format(name)] for k, v in subMap.items(): if isinstance(v, self.__class__) and v != self.__class__(): # break down to dots subList = self._getSubMapDotList("", k, v) # add the divit # subList = ['> {}'.format(i) for i in subList] outList.extend(subList) else: out = self._getValueStr(k, v) # out = '> {}'.format(out) out = "{}".format(out) outList.append(out) finalOut = "\n".join(outList) return finalOut def bannerStr(self): lines = [] previous = None for k, v in self.items(): if previous == self.__class__.__name__: lines.append("-") out = "" if isinstance(v, self.__class__): name = k subMap = v out = self._getSubMapStr(name, subMap) lines.append(out) previous = self.__class__.__name__ else: out = self._getValueStr(k, v) lines.append(out) previous = "other" lines.append("--") s = "\n".join(lines) return s
class DotMap(MutableMapping, OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() if args: assert len(args) == 1 d = args[0] if isinstance(d, dict): for k, v in self.__call_items(d): if isinstance(v, dict): v = DotMap(v) if isinstance(v, list): l = [] for i in v: n = i if isinstance(i, dict): n = DotMap(i) l.append(n) v = l self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map and k != IPYTHON_CANNARY: # automatically extend to new DotMap self[k] = DotMap() return self._map[k] def __setattr__(self, k, v): if k in {'_map', IPYTHON_CANNARY}: super(DotMap, self).__setattr__(k, v) else: self[k] = v def __getattr__(self, k): if k == {'_map', IPYTHON_CANNARY}: super(DotMap, self).__getattr__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k, v in self.__call_items(self._map): if id(v) == id(self): items.append('{0}=DotMap(...)'.format(k)) else: items.append('{0}={1}'.format(k, repr(v))) joined = ', '.join(items) out = '{0}({1})'.format(self.__class__.__name__, joined) return out __repr__ = __str__ def toDict(self): d = {} for k, v in self.items(): if isinstance(v, DotMap): if id(v) == id(self): v = d else: v = v.toDict() elif isinstance(v, (list, tuple)): l = [] for i in v: n = i if type(i) is DotMap: n = i.toDict() l.append(n) if isinstance(v, tuple): v = tuple(l) else: v = l d[k] = v return d def empty(self): return (not any(self)) def values(self): return self._map.values() # ipython support def __dir__(self): return self.keys() @classmethod def parse_other(cls, other): if isinstance(other, DotMap): return other._map return other def __cmp__(self, other): other = DotMap.parse_other(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parse_other(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parse_other(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parse_other(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parse_other(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parse_other(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parse_other(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return DotMap(self) def __copy__(self): return self.copy() def __deepcopy__(self, memo=None): return self.copy() def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d)
class DotMap(MutableMapping, OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() self._dynamic = kwargs.pop('_dynamic', True) self._prevent_method_masking = kwargs.pop('_prevent_method_masking', False) trackedIDs = kwargs.pop('_trackedIDs', {}) if args: d = args[0] # for recursive assignment handling trackedIDs[id(d)] = self src = [] if isinstance(d, MutableMapping): src = self.__call_items(d) elif isinstance(d, Iterable): src = d for k, v in src: if self._prevent_method_masking and k in reserved_keys: raise KeyError('"{}" is reserved'.format(k)) if isinstance(v, dict): idv = id(v) if idv in trackedIDs: v = trackedIDs[idv] else: trackedIDs[idv] = v v = self.__class__(v, _dynamic=self._dynamic, _prevent_method_masking=self. _prevent_method_masking, _trackedIDs=trackedIDs) if type(v) is list: l = [] for i in v: n = i if isinstance(i, dict): idi = id(i) if idi in trackedIDs: n = trackedIDs[idi] else: trackedIDs[idi] = i n = self.__class__( i, _dynamic=self._dynamic, _prevent_method_masking=self. _prevent_method_masking) l.append(n) v = l self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): if self._prevent_method_masking and k in reserved_keys: raise KeyError('"{}" is reserved'.format(k)) self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map and self._dynamic and k != '_ipython_canary_method_should_not_exist_': # automatically extend to new DotMap self[k] = self.__class__() return self._map[k] def __setattr__(self, k, v): if k in { '_map', '_dynamic', '_ipython_canary_method_should_not_exist_', '_prevent_method_masking' }: super(DotMap, self).__setattr__(k, v) elif self._prevent_method_masking and k in reserved_keys: raise KeyError('"{}" is reserved'.format(k)) else: self[k] = v def __getattr__(self, k): if k.startswith('__') and k.endswith('__'): raise AttributeError(k) if k in { '_map', '_dynamic', '_ipython_canary_method_should_not_exist_' }: return super(DotMap, self).__getattr__(k) try: v = super(self.__class__, self).__getattribute__(k) return v except AttributeError: pass return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __add__(self, other): if self.empty(): return other else: self_type = type(self).__name__ other_type = type(other).__name__ msg = "unsupported operand type(s) for +: '{}' and '{}'" raise TypeError(msg.format(self_type, other_type)) def __str__(self, seen=None): items = [] seen = {id(self)} if seen is None else seen for k, v in self.__call_items(self._map): # circular assignment case if isinstance(v, self.__class__): if id(v) in seen: items.append('{0}={1}(...)'.format( k, self.__class__.__name__)) else: seen.add(id(v)) items.append('{0}={1}'.format(k, v.__str__(seen))) else: items.append('{0}={1}'.format(k, repr(v))) joined = ', '.join(items) out = '{0}({1})'.format(self.__class__.__name__, joined) return out def __repr__(self): return str(self) def toDict(self, seen=None): if seen is None: seen = {} d = {} seen[id(self)] = d for k, v in self.items(): if issubclass(type(v), DotMap): idv = id(v) if idv in seen: v = seen[idv] else: v = v.toDict(seen=seen) elif type(v) in (list, tuple): l = [] for i in v: n = i if issubclass(type(i), DotMap): idv = id(n) if idv in seen: n = seen[idv] else: n = i.toDict(seen=seen) l.append(n) if type(v) is tuple: v = tuple(l) else: v = l d[k] = v return d def pprint(self, pformat='dict'): if pformat == 'json': print(dumps(self.toDict(), indent=4, sort_keys=True)) else: pprint(self.toDict()) def empty(self): return (not any(self)) # proper dict subclassing def values(self): return self._map.values() # ipython support def __dir__(self): return self.keys() @classmethod def parseOther(self, other): if issubclass(type(other), DotMap): return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return self.__class__(self) def __copy__(self): return self.copy() def __deepcopy__(self, memo=None): return self.copy() def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): return self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = cls() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d) # bannerStr def _getListStr(self, items): out = '[' mid = '' for i in items: mid += ' {}\n'.format(i) if mid != '': mid = '\n' + mid out += mid out += ']' return out def _getValueStr(self, k, v): outV = v multiLine = len(str(v).split('\n')) > 1 if multiLine: # push to next line outV = '\n' + v if type(v) is list: outV = self._getListStr(v) out = '{} {}'.format(k, outV) return out def _getSubMapDotList(self, pre, name, subMap): outList = [] if pre == '': pre = name else: pre = '{}.{}'.format(pre, name) def stamp(pre, k, v): valStr = self._getValueStr(k, v) return '{}.{}'.format(pre, valStr) for k, v in subMap.items(): if isinstance(v, DotMap) and v != DotMap(): subList = self._getSubMapDotList(pre, k, v) outList.extend(subList) else: outList.append(stamp(pre, k, v)) return outList def _getSubMapStr(self, name, subMap): outList = ['== {} =='.format(name)] for k, v in subMap.items(): if isinstance(v, self.__class__) and v != self.__class__(): # break down to dots subList = self._getSubMapDotList('', k, v) # add the divit # subList = ['> {}'.format(i) for i in subList] outList.extend(subList) else: out = self._getValueStr(k, v) # out = '> {}'.format(out) out = '{}'.format(out) outList.append(out) finalOut = '\n'.join(outList) return finalOut def bannerStr(self): lines = [] previous = None for k, v in self.items(): if previous == self.__class__.__name__: lines.append('-') out = '' if isinstance(v, self.__class__): name = k subMap = v out = self._getSubMapStr(name, subMap) lines.append(out) previous = self.__class__.__name__ else: out = self._getValueStr(k, v) lines.append(out) previous = 'other' lines.append('--') s = '\n'.join(lines) return s
class DotMap(OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() self._dynamic = True # mettendo False non funzionano più i test di default. E' normale in quanto si aspettano la creazione dinamica dei figli # =================================== if LORETO: global MY_DICT_TYPES # global var per la classe self._dynamic = False # mettendo False non funzionano più i test di default. E' normale in quanto si aspettano la creazione dinamica dei figli MY_DICT_TYPES = [dict, DotMap] # by Loreto (DEFAULT dictionary) # =================================== if kwargs: if '_dynamic' in kwargs: self._dynamic = kwargs['_dynamic'] if args: d = args[0] if isinstance(d, dict): for k,v in self.__call_items(d): if type(v) is dict: v = DotMap(v, _dynamic=self._dynamic) if type(v) is list: l = [] for i in v: n = i if type(i) is dict: n = DotMap(i, _dynamic=self._dynamic) l.append(n) v = l self._map[k] = v if kwargs: for k,v in self.__call_items(kwargs): if k is not '_dynamic': self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map and self._dynamic and k != '_ipython_canary_method_should_not_exist_': # automatically extend to new DotMap self[k] = DotMap() return self._map[k] def __setattr__(self, k, v): if k in {'_map','_dynamic', '_ipython_canary_method_should_not_exist_'}: super(DotMap, self).__setattr__(k,v) else: self[k] = v def __getattr__(self, k): if k == {'_map','_dynamic','_ipython_canary_method_should_not_exist_'}: super(DotMap, self).__getattr__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k,v in self.__call_items(self._map): # bizarre recursive assignment situation (why someone would do this is beyond me) if id(v) == id(self): items.append('{0}=DotMap(...)'.format(k)) else: items.append('{0}={1}'.format(k, repr(v))) out = 'DotMap({0})'.format(', '.join(items)) return out def __repr__(self): return str(self) def toDict(self): d = {} for k,v in self.items(): if type(v) is DotMap: # bizarre recursive assignment support if id(v) == id(self): v = d else: v = v.toDict() elif type(v) is list: l = [] for i in v: n = i if type(i) is DotMap: n = i.toDict() l.append(n) v = l d[k] = v return d def pprint(self): pprint(self.toDict()) # =================================== if LORETO: # MY_DICT_TYPES = [dict, DotMap] def Ptr(self, listOfQualifiers, create=False): ptr = self for item in listOfQualifiers: if item in ptr: ptr = ptr[item] else: if create: ptr[item] = DotMap() ptr = ptr[item] else: return None return ptr def KeyTree(self, fPRINT=False): return DictToList.KeyTree(self, myDictTYPES=MY_DICT_TYPES, fPRINT=fPRINT) def KeyList(self): return DictToList.KeyList(self, myDictTYPES=MY_DICT_TYPES) def PrintTree(self, fEXIT=False, MaxLevel=10, header=None, printTYPE='LTKV', stackLevel=1): PrintDictionaryTree.PrintDictionary(self, myDictTYPES=MY_DICT_TYPES, printTYPE=printTYPE, fEXIT=fEXIT, MaxLevel=MaxLevel, header=header, stackLevel=stackLevel+1) printDict = PrintTree printTree = PrintTree def GetValue(self, listOfQualifiers=[], fPRINT=False): return DictToList.getValue(self, listOfQualifiers=listOfQualifiers, myDictTYPES=MY_DICT_TYPES, fPRINT=fPRINT) # =================================== def empty(self): return (not any(self)) # proper dict subclassing def values(self): return self._map.values() # ipython support def __dir__(self): return self.keys() @classmethod def parseOther(self, other): if type(other) is DotMap: return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return DotMap(self.toDict()) def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d)
class DebugVariableViewer(object): """ Class that implements a generic viewer that display a list of variable values This class has to be inherited to effectively display variable values """ def __init__(self, window, items=None): """ Constructor @param window: Reference to the Debug Variable Panel @param items: List of DebugVariableItem displayed by Viewer """ self.ParentWindow = window items = [] if items is None else items self.ItemsDict = OrderedDict([(item.GetVariable(), item) for item in items]) self.Items = self.ItemsDict.viewvalues() # Variable storing current highlight displayed in Viewer self.Highlight = HIGHLIGHT_NONE # List of buttons self.Buttons = [] self.InitHighlightPensBrushes() def __del__(self): """ Destructor """ # Remove reference to Debug Variable Panel self.ParentWindow = None def InitHighlightPensBrushes(self): """ Init global pens and brushes """ if not HIGHLIGHT: HIGHLIGHT['DROP_PEN'] = wx.Pen(wx.Colour(0, 128, 255)) HIGHLIGHT['DROP_BRUSH'] = wx.Brush(wx.Colour(0, 128, 255, 128)) HIGHLIGHT['RESIZE_PEN'] = wx.Pen(wx.Colour(200, 200, 200)) HIGHLIGHT['RESIZE_BRUSH'] = wx.Brush(wx.Colour(200, 200, 200)) def GetIndex(self): """ Return position of Viewer in Debug Variable Panel @return: Position of Viewer """ return self.ParentWindow.GetViewerIndex(self) def GetItem(self, variable): """ Return item storing values of a variable @param variable: Variable path @return: Item storing values of this variable """ return self.ItemsDict.get(variable, None) def GetItems(self): """ Return items displayed by Viewer @return: List of items displayed in Viewer """ return self.ItemsDict.values() def AddItem(self, item): """ Add an item to the list of items displayed by Viewer @param item: Item to add to the list """ self.ItemsDict[item.GetVariable()] = item def RemoveItem(self, item): """ Remove an item from the list of items displayed by Viewer @param item: Item to remove from the list """ self.ItemsDict.pop(item.GetVariable(), None) def ClearItems(self): """ Clear list of items displayed by Viewer """ # Unsubscribe every items of the list for item in self.Items: self.ParentWindow.RemoveDataConsumer(item) # Clear list self.ItemsDict.clear() def ItemsIsEmpty(self): """ Return if list of items displayed by Viewer is empty @return: True if list is empty """ return len(self.Items) == 0 def SubscribeAllDataConsumers(self): """ Function that unsubscribe and remove every item that store values of a variable that doesn't exist in PLC anymore """ for item in self.ItemsDict.values()[:]: iec_path = item.GetVariable() # Check that variablepath exist in PLC if self.ParentWindow.GetDataType(iec_path) is None: # If not, unsubscribe and remove it self.ParentWindow.RemoveDataConsumer(item) self.RemoveItem(item) else: # If it exist, resubscribe and refresh data type self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True) item.RefreshVariableType() def ResetItemsData(self): """ Reset data stored in every items displayed in Viewer """ for item in self.Items: item.ResetData() def GetItemsMinCommonTick(self): """ Return the minimum tick common to all iems displayed in Viewer @return: Minimum common tick between items """ return reduce(max, [ item.GetData()[0, 0] for item in self.Items if len(item.GetData()) > 0 ], 0) def RefreshViewer(self): """ Method that refresh the content displayed by Viewer Need to be overridden by inherited classes """ pass def SetHighlight(self, highlight): """ Set Highlight type displayed in Viewer @return: True if highlight has changed """ # Return immediately if highlight don't change if self.Highlight == highlight: return False self.Highlight = highlight return True def GetButtons(self): """ Return list of buttons defined in Viewer @return: List of buttons """ return self.Buttons def IsOverButton(self, x, y): """ Return if point is over one button of Viewer @param x: X coordinate of point @param y: Y coordinate of point @return: button where point is over """ for button in self.GetButtons(): if button.HitTest(x, y): return button return None def HandleButton(self, x, y): """ Search for the button under point and if found execute associated callback @param x: X coordinate of point @param y: Y coordinate of point @return: True if a button was found and callback executed """ button = self.IsOverButton(x, y) if button is None: return False button.ProcessCallback() return True def ShowButtons(self, show): """ Set display state of buttons in Viewer @param show: Display state (True if buttons must be displayed) """ # Change display of every buttons for button in self.Buttons: button.Show(show) # Refresh button positions self.RefreshButtonsPosition() self.RefreshViewer() def RefreshButtonsPosition(self): """ Function that refresh buttons position in Viewer """ # Get Viewer size width, _height = self.GetSize() # Buttons are align right so we calculate buttons positions in # reverse order buttons = self.Buttons[:] buttons.reverse() # Position offset on x coordinate x_offset = 0 for button in buttons: # Buttons are stacked right, removing those that are not active if button.IsEnabled(): # Update button position according to button width and offset # on x coordinate w, _h = button.GetSize() button.SetPosition(width - 5 - w - x_offset, 5) # Update offset on x coordinate x_offset += w + 2 def DrawCommonElements(self, dc, buttons=None): """ Function that draw common graphics for every Viewers @param dc: wx.DC object corresponding to Device context where drawing common graphics @param buttons: List of buttons to draw if different from default (default None) """ # Get Viewer size width, height = self.GetSize() # Set dc styling for drop before or drop after highlight dc.SetPen(HIGHLIGHT['DROP_PEN']) dc.SetBrush(HIGHLIGHT['DROP_BRUSH']) # Draw line at upper side of Viewer if highlight is drop before if self.Highlight == HIGHLIGHT_BEFORE: dc.DrawLine(0, 1, width - 1, 1) # Draw line at lower side of Viewer if highlight is drop before elif self.Highlight == HIGHLIGHT_AFTER: dc.DrawLine(0, height - 1, width - 1, height - 1) # If no specific buttons are defined, get default buttons if buttons is None: buttons = self.Buttons # Draw buttons for button in buttons: button.Draw(dc) # If graph dragging is processing if self.ParentWindow.IsDragging(): destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self) srcPos = self.ParentWindow.GetDraggingAxesPosition(self) if destBBox.width > 0 and destBBox.height > 0: srcPanel = self.ParentWindow.DraggingAxesPanel srcBBox = srcPanel.GetAxesBoundingBox() srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0) srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0) srcBmp = _convert_agg_to_wx_bitmap(srcPanel.get_renderer(), None) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) dc.Blit(destBBox.x, destBBox.y, int(destBBox.width), int(destBBox.height), srcDC, srcX, srcY) def OnEnter(self, event): """ Function called when entering Viewer @param event: wx.MouseEvent """ # Display buttons self.ShowButtons(True) event.Skip() def OnLeave(self, event): """ Function called when leaving Viewer @param event: wx.MouseEvent """ # Hide buttons self.ShowButtons(False) event.Skip() def OnCloseButton(self): """ Function called when Close button is pressed """ wx.CallAfter(self.ParentWindow.DeleteValue, self) def OnForceButton(self): """ Function called when Force button is pressed """ self.ForceValue(self.ItemsDict.values()[0]) def OnReleaseButton(self): """ Function called when Release button is pressed """ self.ReleaseValue(self.ItemsDict.values()[0]) def OnMouseDragging(self, x, y): """ Function called when mouse is dragged over Viewer @param x: X coordinate of mouse pointer @param y: Y coordinate of mouse pointer """ xw, yw = self.GetPosition() # Refresh highlight in Debug Variable Panel (highlight can be displayed # in another Viewer self.ParentWindow.RefreshHighlight(x + xw, y + yw) def RefreshHighlight(self, x, y): """ Function called by Debug Variable Panel asking Viewer to refresh highlight according to mouse position @param x: X coordinate of mouse pointer @param y: Y coordinate of mouse pointer """ # Get Viewer size _width, height = self.GetSize() # Mouse is in the first half of Viewer if y < height / 2: # If Viewer is the upper one, draw drop before highlight if self.ParentWindow.IsViewerFirst(self): self.SetHighlight(HIGHLIGHT_BEFORE) # Else draw drop after highlight in previous Viewer else: self.SetHighlight(HIGHLIGHT_NONE) self.ParentWindow.HighlightPreviousViewer(self) # Mouse is in the second half of Viewer, draw drop after highlight else: self.SetHighlight(HIGHLIGHT_AFTER) def OnEraseBackground(self, event): """ Function called when Viewer background is going to be erase @param event: wx.EraseEvent """ # Prevent flicker on Windows pass def OnResize(self, event): """ Function called when Viewer size changed @param event: wx.ResizeEvent """ # Refresh button positions self.RefreshButtonsPosition() self.ParentWindow.ForceRefresh() event.Skip() def ForceValue(self, item): """ Force value of item given @param item: Item to force value """ # Check variable data type iec_path = item.GetVariable() iec_type = self.ParentWindow.GetDataType(iec_path) # Return immediately if not found if iec_type is None: return # Open a dialog to enter varaible forced value dialog = ForceVariableDialog(self, iec_type, str(item.GetValue())) if dialog.ShowModal() == wx.ID_OK: self.ParentWindow.ForceDataValue(iec_path.upper(), dialog.GetValue()) def ReleaseValue(self, item): """ Release value of item given @param item: Item to release value """ self.ParentWindow.ReleaseDataValue(item.GetVariable().upper())
class BaseCache(object): """ BaseCache is a class that saves and operates on an OrderedDict. It has a certain capacity, stored in the attribute `maxsize`. Whether this capacity is reached, can be checked by using the boolean property `is_full`. To implement a custom cache, inherit from this class and override the methods ``__getitem__`` and ``__setitem__``. Call the method `sunpy.database.caching.BaseCache.callback` as soon as an item from the cache is removed. """ __metaclass__ = ABCMeta def __init__(self, maxsize=float('inf')): self.maxsize = maxsize self._dict = OrderedDict() def get(self, key, default=None): # pragma: no cover """Return the corresponding value to `key` if `key` is in the cache, `default` otherwise. This method has no side-effects, multiple calls with the same cache and the same passed key must always return the same value. """ try: return self._dict[key] except KeyError: return default @abstractmethod def __getitem__(self, key): """abstract method: this method must be overwritten by inheriting subclasses. It defines what happens if an item from the cache is attempted to be accessed. """ return # pragma: no cover @abstractmethod def __setitem__(self, key, value): """abstract method: this method must be overwritten by inheriting subclasses. It defines what happens if a new value should be assigned to the given key. If the given key does already exist in the cache or not must be checked by the person who implements this method. """ @abstractproperty def to_be_removed(self): """The item that will be removed on the next :meth:`sunpy.database.caching.BaseCache.remove` call. """ @abstractmethod def remove(self): """Call this method to manually remove one item from the cache. Which item is removed, depends on the implementation of the cache. After the item has been removed, the callback method is called. """ def callback(self, key, value): """This method should be called (by convention) if an item is removed from the cache because it is full. The passed key and value are the ones that are removed. By default this method does nothing, but it can be customized in a custom cache that inherits from this base class. """ @property def is_full(self): """True if the number of items in the cache equals :attr:`maxsize`, False otherwise. """ return len(self._dict) == self.maxsize def __delitem__(self, key): self._dict.__delitem__(key) def __contains__(self, key): return key in self._dict.keys() def __len__(self): return len(self._dict) def __iter__(self): for key in self._dict.__iter__(): yield key def __reversed__(self): # pragma: no cover for key in self._dict.__reversed__(): yield key def clear(self): # pragma: no cover return self._dict.clear() def keys(self): # pragma: no cover return self._dict.keys() def values(self): # pragma: no cover return self._dict.values() def items(self): # pragma: no cover return self._dict.items() def iterkeys(self): # pragma: no cover return self._dict.iterkeys() def itervalues(self): # pragma: no cover for value in self._dict.itervalues(): yield value def iteritems(self): # pragma: no cover for key, value in six.iteritems(self._dict): yield key, value def update(self, *args, **kwds): # pragma: no cover self._dict.update(*args, **kwds) def pop(self, key, default=MutableMapping._MutableMapping__marker): # pragma: no cover return self._dict.pop(key, default) def setdefault(self, key, default=None): # pragma: no cover return self._dict.setdefault(key, default) def popitem(self, last=True): # pragma: no cover return self._dict.popitem(last) def __reduce__(self): # pragma: no cover return self._dict.__reduce__() def copy(self): # pragma: no cover return self._dict.copy() def __eq__(self, other): # pragma: no cover return self._dict.__eq__(other) def __ne__(self, other): # pragma: no cover return self._dict.__ne__(other) def viewkeys(self): # pragma: no cover return self._dict.viewkeys() def viewvalues(self): # pragma: no cover return self._dict.viewvalues() def viewitems(self): # pragma: no cover return self._dict.viewitems() @classmethod def fromkeys(cls, iterable, value=None): # pragma: no cover return OrderedDict.fromkeys(iterable, value) def __repr__(self): # pragma: no cover return '{0}({1!r})'.format(self.__class__.__name__, dict(self._dict))
class DotMap(MutableMapping, OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() self._dynamic = True if kwargs: if '_dynamic' in kwargs: self._dynamic = kwargs['_dynamic'] if args: d = args[0] if isinstance(d, dict): for k, v in self.__call_items(d): if isinstance(v, dict): v = DotMap(v, _dynamic=self._dynamic) if type(v) is list: l = [] for i in v: n = i if type(i) is dict: n = DotMap(i, _dynamic=self._dynamic) l.append(n) v = l self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): if k is not '_dynamic': self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map and self._dynamic and k != '_ipython_canary_method_should_not_exist_': # automatically extend to new DotMap self[k] = DotMap() return self._map[k] def __setattr__(self, k, v): if k in { '_map', '_dynamic', '_ipython_canary_method_should_not_exist_' }: super(DotMap, self).__setattr__(k, v) else: self[k] = v def __getattr__(self, k): if k in { '_map', '_dynamic', '_ipython_canary_method_should_not_exist_' }: super(DotMap, self).__getattr__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k, v in self.__call_items(self._map): # bizarre recursive assignment situation (why someone would do this is beyond me) if id(v) == id(self): items.append('{0}=DotMap(...)'.format(k)) else: items.append('{0}={1}'.format(k, repr(v))) joined = ', '.join(items) out = '{0}({1})'.format(self.__class__.__name__, joined) return out def __repr__(self): return str(self) def toDict(self): d = {} for k, v in self.items(): if type(v) is DotMap: # bizarre recursive assignment support if id(v) == id(self): v = d else: v = v.toDict() elif type(v) in (list, tuple): l = [] for i in v: n = i if type(i) is DotMap: n = i.toDict() l.append(n) if type(v) is tuple: v = tuple(l) else: v = l d[k] = v return d def pprint(self, pformat='dict'): if pformat == 'json': print(dumps(self.toDict(), indent=4, sort_keys=True)) else: pprint(self.toDict()) def empty(self): return (not any(self)) # proper dict subclassing def values(self): return self._map.values() # ipython support def __dir__(self): return self.keys() @classmethod def parseOther(self, other): if type(other) is DotMap: return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return DotMap(self) def __copy__(self): return self.copy() def __deepcopy__(self, memo=None): return self.copy() def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d) # bannerStr def _getListStr(self, items): out = '[' mid = '' for i in items: mid += ' {}\n'.format(i) if mid != '': mid = '\n' + mid out += mid out += ']' return out def _getValueStr(self, k, v): outV = v multiLine = len(str(v).split('\n')) > 1 if multiLine: # push to next line outV = '\n' + v if type(v) is list: outV = self._getListStr(v) out = '{} {}'.format(k, outV) return out def _getSubMapDotList(self, pre, name, subMap): outList = [] if pre == '': pre = name else: pre = '{}.{}'.format(pre, name) def stamp(pre, k, v): valStr = self._getValueStr(k, v) return '{}.{}'.format(pre, valStr) for k, v in subMap.items(): if isinstance(v, DotMap) and v != DotMap(): subList = self._getSubMapDotList(pre, k, v) outList.extend(subList) else: outList.append(stamp(pre, k, v)) return outList def _getSubMapStr(self, name, subMap): outList = ['== {} =='.format(name)] for k, v in subMap.items(): if isinstance(v, DotMap) and v != DotMap(): # break down to dots subList = self._getSubMapDotList('', k, v) # add the divit # subList = ['> {}'.format(i) for i in subList] outList.extend(subList) else: out = self._getValueStr(k, v) # out = '> {}'.format(out) out = '{}'.format(out) outList.append(out) finalOut = '\n'.join(outList) return finalOut def bannerStr(self): lines = [] previous = None for k, v in self.items(): if previous == 'DotMap': lines.append('-') out = '' if isinstance(v, DotMap): name = k subMap = v out = self._getSubMapStr(name, subMap) lines.append(out) previous = 'DotMap' else: out = self._getValueStr(k, v) lines.append(out) previous = 'other' lines.append('--') s = '\n'.join(lines) return s
class DotMap(OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() if args: d = args[0] if type(d) is dict: for k,v in self.__call_items(d): if type(v) is dict: v = DotMap(v) self._map[k] = v if kwargs: for k,v in self.__call_items(kwargs): self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map: # automatically extend to new DotMap self[k] = DotMap() return self._map[k] def __setattr__(self, k, v): if k == '_map': super(DotMap, self).__setattr__(k,v) else: self[k] = v def __getattr__(self, k): if k == '_map': super(DotMap, self).__getattr__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k,v in self.__call_items(self._map): items.append('{0}={1}'.format(k, repr(v))) out = 'DotMap({0})'.format(', '.join(items)) return out def __repr__(self): return str(self) def toDict(self): d = {} for k,v in self.items(): if type(v) is DotMap: v = v.toDict() d[k] = v return d def pprint(self): pprint(self.toDict()) # proper dict subclassing def values(self): return self._map.values() @classmethod def parseOther(self, other): if type(other) is DotMap: return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return self def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = OrderedDict.fromkeys(seq, value) return d
class ConfigMap(MutableMapping, OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() # todo: simplify self._dynamic = True if kwargs: if '_dynamic' in kwargs: self._dynamic = kwargs['_dynamic'] del kwargs['_dynamic'] self._evaluate = True if kwargs: if '_evaluate' in kwargs: self._evaluate = kwargs['_evaluate'] del kwargs['_evaluate'] self._evaluated = False if kwargs: if '_evaluated' in kwargs: self._evaluated = kwargs['_evaluated'] del kwargs['_evaluated'] if args: d = args[0] if isinstance(d, dict): for k, v in self.__call_items(d): if isinstance(v, dict): v = ConfigMap(v, _dynamic=self._dynamic, _evaluate=self._evaluate, _evaluated=self._evaluated) if type(v) is list: l = [] for i in v: n = i if type(i) is dict: n = ConfigMap(i, _dynamic=self._dynamic, _evaluate=self._evaluate, _evaluated=self._evaluated) l.append(n) v = l self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k, evaluate=None): if evaluate is None: evaluate = self._evaluate if k not in self._map: if k == '_ipython_canary_method_should_not_exist_': raise KeyError if self._dynamic: # automatically extend to new ConfigMap self[k] = ConfigMap() else: # todo: display full recursive path? raise KeyError("'%s' does not exist" % k) var = self._map[k] if evaluate: if isinstance(var, ConfigMethod): var = var.evaluate() # todo: return instead to avoid second config map eval? if isinstance(var, ConfigMap): var = var.evaluate() return var def __setattr__(self, k, v): if k in ['_map', '_dynamic', '_ipython_canary_method_should_not_exist_', '_evaluate', '_evaluated']: super(ConfigMap, self).__setattr__(k, v) else: self[k] = v def __getattr__(self, k): if k in ['_map', '_dynamic', '_ipython_canary_method_should_not_exist_', '_evaluate', '_evaluated']: return self.__getattribute__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k, v in self.__call_items(self._map): # bizarre recursive assignment situation (why someone would do this is beyond me) if id(v) == id(self): items.append('{0}=ConfigMap(...)'.format(k)) else: items.append('{0}={1}'.format(k, repr(v))) joined = ', '.join(items) out = '{0}({1})'.format(self.__class__.__name__, joined) return out def __repr__(self): return str(self) def toDict(self, evaluate=None, with_hidden=True): if evaluate is None: evaluate = bool(self._evaluate) d = {} for k, v in self.items(): if evaluate and isinstance(v, ConfigMethod): v = v.evaluate() if isinstance(v, ConfigMap): v = v.toDict(evaluate=evaluate, with_hidden=with_hidden) if id(v) != id(self) else d elif isinstance(v, list): v = [i.toDict(evaluate=evaluate, with_hidden=with_hidden) if isinstance(i, ConfigMap) else i for i in v] elif isinstance(v, tuple): v = (i.toDict(evaluate=evaluate, with_hidden=with_hidden) if isinstance(i, ConfigMap) else i for i in v) if with_hidden is False \ and (isinstance(k, str) and ((k.startswith('_') and not k.endswith('_')) or k.startswith('~'))): continue d[k] = v return d def evaluate(self): if self._evaluated: return self # TODO: case where config method access a key of the config that is just being evaluated. # shouldn't give an endless loop # todo: make more efficient return ConfigMap(self.toDict(evaluate=True), _dynamic=False, _evaluated=True) def pprint(self, pformat='json'): if pformat == 'json': print(dumps(self.toDict(), indent=4, sort_keys=True, default=str)) else: pprint(self.toDict()) def empty(self): return not any(self) # proper dict subclassing def values(self): return self._map.values() # ipython support def __dir__(self): return self.keys() @classmethod def parseOther(self, other): if type(other) is ConfigMap: return other._map else: return other def __cmp__(self, other): other = ConfigMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = ConfigMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = ConfigMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = ConfigMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = ConfigMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = ConfigMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = ConfigMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return ConfigMap(self, _dynamic=self._dynamic, _evaluate=self._evaluate, _evaluated=self._evaluated) def __copy__(self): return self.copy() def __deepcopy__(self, memo=None): return self.copy() def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = ConfigMap(_dynamic=False) d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d) # bannerStr def _getListStr(self, items): out = '[' mid = '' for i in items: mid += ' {}\n'.format(i) if mid != '': mid = '\n' + mid out += mid out += ']' return out def _getValueStr(self, k, v): outV = v multiLine = len(str(v).split('\n')) > 1 if multiLine: # push to next line outV = '\n' + v if type(v) is list: outV = self._getListStr(v) out = '{} {}'.format(k, outV) return out def _getSubMapDotList(self, pre, name, subMap): outList = [] if pre == '': pre = name else: pre = '{}.{}'.format(pre, name) def stamp(pre, k, v): valStr = self._getValueStr(k, v) return '{}.{}'.format(pre, valStr) for k, v in subMap.items(): if isinstance(v, ConfigMap) and v != ConfigMap(): subList = self._getSubMapDotList(pre, k, v) outList.extend(subList) else: outList.append(stamp(pre, k, v)) return outList def _getSubMapStr(self, name, subMap): outList = ['== {} =='.format(name)] for k, v in subMap.items(): if isinstance(v, ConfigMap) and v != ConfigMap(): # break down to dots subList = self._getSubMapDotList('', k, v) # add the divit # subList = ['> {}'.format(i) for i in subList] outList.extend(subList) else: out = self._getValueStr(k, v) # out = '> {}'.format(out) out = '{}'.format(out) outList.append(out) finalOut = '\n'.join(outList) return finalOut def bannerStr(self): lines = [] previous = None for k, v in self.items(): if previous == 'ConfigMap': lines.append('-') out = '' if isinstance(v, ConfigMap): name = k subMap = v out = self._getSubMapStr(name, subMap) lines.append(out) previous = 'ConfigMap' else: out = self._getValueStr(k, v) lines.append(out) previous = 'other' lines.append('--') s = '\n'.join(lines) return s
class BaseCache(object): """ BaseCache is a class that saves and operates on an OrderedDict. It has a certain capacity, stored in the attribute `maxsize`. Whether this capacity is reached, can be checked by using the boolean property `is_full`. To implement a custom cache, inherit from this class and override the methods ``__getitem__`` and ``__setitem__``. Call the method `sunpy.database.caching.BaseCache.callback` as soon as an item from the cache is removed. """ __metaclass__ = ABCMeta def __init__(self, maxsize=float('inf')): self.maxsize = maxsize self._dict = OrderedDict() def get(self, key, default=None): # pragma: no cover """Return the corresponding value to `key` if `key` is in the cache, `default` otherwise. This method has no side-effects, multiple calls with the same cache and the same passed key must always return the same value. """ try: return self._dict[key] except KeyError: return default @abstractmethod def __getitem__(self, key): """abstract method: this method must be overwritten by inheriting subclasses. It defines what happens if an item from the cache is attempted to be accessed. """ return # pragma: no cover @abstractmethod def __setitem__(self, key, value): """abstract method: this method must be overwritten by inheriting subclasses. It defines what happens if a new value should be assigned to the given key. If the given key does already exist in the cache or not must be checked by the person who implements this method. """ @abstractproperty def to_be_removed(self): """The item that will be removed on the next :meth:`sunpy.database.caching.BaseCache.remove` call. """ @abstractmethod def remove(self): """Call this method to manually remove one item from the cache. Which item is removed, depends on the implementation of the cache. After the item has been removed, the callback method is called. """ def callback(self, key, value): """This method should be called (by convention) if an item is removed from the cache because it is full. The passed key and value are the ones that are removed. By default this method does nothing, but it can be customized in a custom cache that inherits from this base class. """ @property def is_full(self): """True if the number of items in the cache equals :attr:`maxsize`, False otherwise. """ return len(self._dict) == self.maxsize def __delitem__(self, key): self._dict.__delitem__(key) def __contains__(self, key): return key in self._dict.keys() def __len__(self): return len(self._dict) def __iter__(self): for key in self._dict.__iter__(): yield key def __reversed__(self): # pragma: no cover for key in self._dict.__reversed__(): yield key def clear(self): # pragma: no cover return self._dict.clear() def keys(self): # pragma: no cover return self._dict.keys() def values(self): # pragma: no cover return self._dict.values() def items(self): # pragma: no cover return self._dict.items() def iterkeys(self): # pragma: no cover return self._dict.iterkeys() def itervalues(self): # pragma: no cover for value in self._dict.itervalues(): yield value def iteritems(self): # pragma: no cover for key, value in self._dict.iteritems(): yield key, value def update(self, *args, **kwds): # pragma: no cover self._dict.update(*args, **kwds) def pop(self, key, default=MutableMapping._MutableMapping__marker): # pragma: no cover return self._dict.pop(key, default) def setdefault(self, key, default=None): # pragma: no cover return self._dict.setdefault(key, default) def popitem(self, last=True): # pragma: no cover return self._dict.popitem(last) def __reduce__(self): # pragma: no cover return self._dict.__reduce__() def copy(self): # pragma: no cover return self._dict.copy() def __eq__(self, other): # pragma: no cover return self._dict.__eq__(other) def __ne__(self, other): # pragma: no cover return self._dict.__ne__(other) def viewkeys(self): # pragma: no cover return self._dict.viewkeys() def viewvalues(self): # pragma: no cover return self._dict.viewvalues() def viewitems(self): # pragma: no cover return self._dict.viewitems() @classmethod def fromkeys(cls, iterable, value=None): # pragma: no cover return OrderedDict.fromkeys(iterable, value) def __repr__(self): # pragma: no cover return '{0}({1!r})'.format(self.__class__.__name__, dict(self._dict))
class DotMap(OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() self._dynamic = True # mettendo False non funzionano più i test di default. E' normale in quanto si aspettano la creazione dinamica dei figli # =================================== if LORETO: global MY_DICT_TYPES # global var per la classe self._dynamic = False # mettendo False non funzionano più i test di default. E' normale in quanto si aspettano la creazione dinamica dei figli MY_DICT_TYPES = [dict, DotMap, OrderedDict] # by Loreto (DEFAULT dictionary) # =================================== if kwargs: if '_dynamic' in kwargs: self._dynamic = kwargs['_dynamic'] if args: d = args[0] if isinstance(d, dict): for k, v in self.__call_items(d): if type(v) is dict: v = DotMap(v, _dynamic=self._dynamic) if type(v) is list: l = [] for i in v: n = i if type(i) is dict: n = DotMap(i, _dynamic=self._dynamic) l.append(n) v = l self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): if k is not '_dynamic': self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map and self._dynamic and k != '_ipython_canary_method_should_not_exist_': # automatically extend to new DotMap self[k] = DotMap() return self._map[k] def __setattr__(self, k, v): if k in { '_map', '_dynamic', '_ipython_canary_method_should_not_exist_' }: super(DotMap, self).__setattr__(k, v) else: self[k] = v def __getattr__(self, k): if k == { '_map', '_dynamic', '_ipython_canary_method_should_not_exist_' }: super(DotMap, self).__getattr__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k, v in self.__call_items(self._map): # bizarre recursive assignment situation (why someone would do this is beyond me) if id(v) == id(self): items.append('{0}=DotMap(...)'.format(k)) else: items.append('{0}={1}'.format(k, repr(v))) out = 'DotMap({0})'.format(', '.join(items)) return out def __repr__(self): return str(self) def toDict(self): d = {} for k, v in self.items(): if type(v) is DotMap: # bizarre recursive assignment support if id(v) == id(self): v = d else: v = v.toDict() elif type(v) is list: l = [] for i in v: n = i if type(i) is DotMap: n = i.toDict() l.append(n) v = l d[k] = v return d def pprint(self): pprint(self.toDict()) # =================================== if LORETO: # MY_DICT_TYPES = [dict, DotMap] def Ptr(self, listOfQualifiers, create=False): ptr = self for item in listOfQualifiers: if item in ptr: ptr = ptr[item] else: if create: ptr[item] = DotMap() ptr = ptr[item] else: return None return ptr def KeyTree(self, fPRINT=False): return DictToList.KeyTree(self, myDictTYPES=MY_DICT_TYPES, fPRINT=fPRINT) def KeyList(self): return DictToList.KeyList(self, myDictTYPES=MY_DICT_TYPES) def PrintTree(self, fEXIT=False, maxDepth=10, header=None, whatPrint='LTKV', stackLevel=1): PrintDictionaryTree.PrintDictionary(self, myDictTYPES=MY_DICT_TYPES, whatPrint=whatPrint, fEXIT=fEXIT, maxDepth=maxDepth, header=header, stackLevel=stackLevel + 1) printDict = PrintTree printTree = PrintTree def GetValue(self, listOfQualifiers=[], fPRINT=False): return DictToList.getValue(self, listOfQualifiers=listOfQualifiers, myDictTYPES=MY_DICT_TYPES, fPRINT=fPRINT) # =================================== def empty(self): return (not any(self)) # proper dict subclassing def values(self): return self._map.values() # ipython support def __dir__(self): return self.keys() @classmethod def parseOther(self, other): if type(other) is DotMap: return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return DotMap(self.toDict()) def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d)
class DebugVariableViewer: def __init__(self, window, items=[]): """ Constructor @param window: Reference to the Debug Variable Panel @param items: List of DebugVariableItem displayed by Viewer """ self.ParentWindow = window self.ItemsDict = OrderedDict([(item.GetVariable(), item) for item in items]) self.Items = self.ItemsDict.viewvalues() # Variable storing current highlight displayed in Viewer self.Highlight = HIGHLIGHT_NONE # List of buttons self.Buttons = [] def __del__(self): """ Destructor """ # Remove reference to Debug Variable Panel self.ParentWindow = None def GetIndex(self): """ Return position of Viewer in Debug Variable Panel @return: Position of Viewer """ return self.ParentWindow.GetViewerIndex(self) def GetItem(self, variable): """ Return item storing values of a variable @param variable: Variable path @return: Item storing values of this variable """ return self.ItemsDict.get(variable, None) def GetItems(self): """ Return items displayed by Viewer @return: List of items displayed in Viewer """ return self.ItemsDict.values() def AddItem(self, item): """ Add an item to the list of items displayed by Viewer @param item: Item to add to the list """ self.ItemsDict[item.GetVariable()] = item def RemoveItem(self, item): """ Remove an item from the list of items displayed by Viewer @param item: Item to remove from the list """ self.ItemsDict.pop(item.GetVariable(), None) def ClearItems(self): """ Clear list of items displayed by Viewer """ # Unsubscribe every items of the list for item in self.Items: self.ParentWindow.RemoveDataConsumer(item) # Clear list self.ItemsDict.clear() def ItemsIsEmpty(self): """ Return if list of items displayed by Viewer is empty @return: True if list is empty """ return len(self.Items) == 0 def SubscribeAllDataConsumers(self): """ Function that unsubscribe and remove every item that store values of a variable that doesn't exist in PLC anymore """ for item in self.ItemsDict.values()[:]: iec_path = item.GetVariable() # Check that variablepath exist in PLC if self.ParentWindow.GetDataType(iec_path) is None: # If not, unsubscribe and remove it self.ParentWindow.RemoveDataConsumer(item) self.RemoveItem(item) else: # If it exist, resubscribe and refresh data type self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True) item.RefreshVariableType() def ResetItemsData(self): """ Reset data stored in every items displayed in Viewer """ for item in self.Items: item.ResetData() def GetItemsMinCommonTick(self): """ Return the minimum tick common to all iems displayed in Viewer @return: Minimum common tick between items """ return reduce(max, [item.GetData()[0, 0] for item in self.Items if len(item.GetData()) > 0], 0) def RefreshViewer(self): """ Method that refresh the content displayed by Viewer Need to be overridden by inherited classes """ pass def SetHighlight(self, highlight): """ Set Highlight type displayed in Viewer @return: True if highlight has changed """ # Return immediately if highlight don't change if self.Highlight == highlight: return False self.Highlight = highlight return True def GetButtons(self): """ Return list of buttons defined in Viewer @return: List of buttons """ return self.Buttons def IsOverButton(self, x, y): """ Return if point is over one button of Viewer @param x: X coordinate of point @param y: Y coordinate of point @return: button where point is over """ for button in self.GetButtons(): if button.HitTest(x, y): return button return None def HandleButton(self, x, y): """ Search for the button under point and if found execute associated callback @param x: X coordinate of point @param y: Y coordinate of point @return: True if a button was found and callback executed """ button = self.IsOverButton(x, y) if button is None: return False button.ProcessCallback() return True def ShowButtons(self, show): """ Set display state of buttons in Viewer @param show: Display state (True if buttons must be displayed) """ # Change display of every buttons for button in self.Buttons: button.Show(show) # Refresh button positions self.RefreshButtonsPosition() self.RefreshViewer() def RefreshButtonsPosition(self): """ Function that refresh buttons position in Viewer """ # Get Viewer size width, height = self.GetSize() # Buttons are align right so we calculate buttons positions in # reverse order buttons = self.Buttons[:] buttons.reverse() # Position offset on x coordinate x_offset = 0 for button in buttons: # Buttons are stacked right, removing those that are not active if button.IsEnabled(): # Update button position according to button width and offset # on x coordinate w, h = button.GetSize() button.SetPosition(width - 5 - w - x_offset, 5) # Update offset on x coordinate x_offset += w + 2 def DrawCommonElements(self, dc, buttons=None): """ Function that draw common graphics for every Viewers @param dc: wx.DC object corresponding to Device context where drawing common graphics @param buttons: List of buttons to draw if different from default (default None) """ # Get Viewer size width, height = self.GetSize() # Set dc styling for drop before or drop after highlight dc.SetPen(HIGHLIGHT_DROP_PEN) dc.SetBrush(HIGHLIGHT_DROP_BRUSH) # Draw line at upper side of Viewer if highlight is drop before if self.Highlight == HIGHLIGHT_BEFORE: dc.DrawLine(0, 1, width - 1, 1) # Draw line at lower side of Viewer if highlight is drop before elif self.Highlight == HIGHLIGHT_AFTER: dc.DrawLine(0, height - 1, width - 1, height - 1) # If no specific buttons are defined, get default buttons if buttons is None: buttons = self.Buttons # Draw buttons for button in buttons: button.Draw(dc) # If graph dragging is processing if self.ParentWindow.IsDragging(): destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self) srcPos = self.ParentWindow.GetDraggingAxesPosition(self) if destBBox.width > 0 and destBBox.height > 0: srcPanel = self.ParentWindow.DraggingAxesPanel srcBBox = srcPanel.GetAxesBoundingBox() srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0) srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0) srcBmp = _convert_agg_to_wx_bitmap( srcPanel.get_renderer(), None) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) dc.Blit(destBBox.x, destBBox.y, int(destBBox.width), int(destBBox.height), srcDC, srcX, srcY) def OnEnter(self, event): """ Function called when entering Viewer @param event: wx.MouseEvent """ # Display buttons self.ShowButtons(True) event.Skip() def OnLeave(self, event): """ Function called when leaving Viewer @param event: wx.MouseEvent """ # Hide buttons self.ShowButtons(False) event.Skip() def OnCloseButton(self): """ Function called when Close button is pressed """ wx.CallAfter(self.ParentWindow.DeleteValue, self) def OnForceButton(self): """ Function called when Force button is pressed """ self.ForceValue(self.ItemsDict.values()[0]) def OnReleaseButton(self): """ Function called when Release button is pressed """ self.ReleaseValue(self.ItemsDict.values()[0]) def OnMouseDragging(self, x, y): """ Function called when mouse is dragged over Viewer @param x: X coordinate of mouse pointer @param y: Y coordinate of mouse pointer """ xw, yw = self.GetPosition() # Refresh highlight in Debug Variable Panel (highlight can be displayed # in another Viewer self.ParentWindow.RefreshHighlight(x + xw, y + yw) def RefreshHighlight(self, x, y): """ Function called by Debug Variable Panel asking Viewer to refresh highlight according to mouse position @param x: X coordinate of mouse pointer @param y: Y coordinate of mouse pointer """ # Get Viewer size width, height = self.GetSize() # Mouse is in the first half of Viewer if y < height / 2: # If Viewer is the upper one, draw drop before highlight if self.ParentWindow.IsViewerFirst(self): self.SetHighlight(HIGHLIGHT_BEFORE) # Else draw drop after highlight in previous Viewer else: self.SetHighlight(HIGHLIGHT_NONE) self.ParentWindow.HighlightPreviousViewer(self) # Mouse is in the second half of Viewer, draw drop after highlight else: self.SetHighlight(HIGHLIGHT_AFTER) def OnEraseBackground(self, event): """ Function called when Viewer background is going to be erase @param event: wx.EraseEvent """ # Prevent flicker on Windows pass def OnResize(self, event): """ Function called when Viewer size changed @param event: wx.ResizeEvent """ # Refresh button positions self.RefreshButtonsPosition() self.ParentWindow.ForceRefresh() event.Skip() def ForceValue(self, item): """ Force value of item given @param item: Item to force value """ # Check variable data type iec_path = item.GetVariable() iec_type = self.ParentWindow.GetDataType(iec_path) # Return immediately if not found if iec_type is None: return # Open a dialog to enter varaible forced value dialog = ForceVariableDialog(self, iec_type, str(item.GetValue())) if dialog.ShowModal() == wx.ID_OK: self.ParentWindow.ForceDataValue(iec_path.upper(), dialog.GetValue()) def ReleaseValue(self, item): """ Release value of item given @param item: Item to release value """ self.ParentWindow.ReleaseDataValue( item.GetVariable().upper())
class NotificationDisplay(object): '''Interface to display notification stack. Should have "display(note, cb_dismiss=None) -> nid(UInt32, >0)", "close(nid)" methods and NoWindowError(nid) exception, raised on erroneous nid's in close(). Current implementation based on notipy: git://github.com/the-isz/notipy.git''' window = namedtuple('Window', 'gobj event_boxes') base_css = b''' #notification { background: transparent; } #notification #frame { background-color: #d4ded8; padding: 3px; } #notification #hs { background-color: black; } #notification #critical { background-color: #ffaeae; } #notification #normal { background-color: #f0ffec; } #notification #low { background-color: #bee3c6; } #notification #summary { padding-left: 5px; font-size: 1.2em; text-shadow: 1px 1px 0px gray; } #notification #body { font-size: 1em; } #notification #body * { background-color: #d4ded8; } ''' base_css_min = b'#notification * { font-size: 8; }' # simpliest fallback def __init__(self, layout_margin, layout_anchor, layout_direction, icon_scale=dict(), markup_default=False, markup_warn=False, markup_strip=False): self.margins = dict( it.chain.from_iterable( map( lambda ax: ((2**ax, layout_margin), (-2**ax, layout_margin)), xrange(2)))) self.layout_anchor = layout_anchor self.layout_direction = layout_direction self.icon_scale = icon_scale self.markup_default = markup_default self.markup_warn, self.markup_strip = markup_warn, markup_strip self._windows = OrderedDict() self._default_style = self._get_default_css() Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), self._default_style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def _pango_markup_parse(self, text, _err_mark='[TN82u8] '): success = True try: _, attr_list, text, _ = Pango.parse_markup(text, -1, '\0') except GLib.GError as err: if self.markup_warn: msg_start = '{}Pango formatting failed'.format(_err_mark) if msg_start not in text: # detect and avoid possible feedback loops log.warn('%s (%s) for text, stripping markup: %r', msg_start, err, text) else: text = xml_escape( text) # escape message so it'd render bugged part if self.markup_strip: text = strip_markup(text) try: _, attr_list, text, _ = Pango.parse_markup(text, -1, '\0') except GLib.GError: attr_list = None success = False return success, text, attr_list def _pango_markup_to_gtk( self, text, attr_list=None, _pango_classes={ 'SIZE': Pango.AttrInt, 'WEIGHT': Pango.AttrInt, 'UNDERLINE': Pango.AttrInt, 'STRETCH': Pango.AttrInt, 'VARIANT': Pango.AttrInt, 'STYLE': Pango.AttrInt, 'SCALE': Pango.AttrFloat, 'FAMILY': Pango.AttrString, 'FONT_DESC': Pango.AttrFontDesc, 'STRIKETHROUGH': Pango.AttrInt, 'BACKGROUND': Pango.AttrColor, 'FOREGROUND': Pango.AttrColor, 'RISE': Pango.AttrInt }, _pango_to_gtk={'font_desc': 'font'}): # See https://bugzilla.gnome.org/show_bug.cgi?id=59390 for why it is necessary # And doesn't work with GI anyway because of # https://bugzilla.gnome.org/show_bug.cgi?id=646788 # "Behdad Esfahbod [pango developer]: I personally have no clue how to fix this" # Workaround from https://github.com/matasbbb/pitivit/commit/da815339e # TODO: fix when AttrList.get_iterator will be accessible via GI or textbuffer gets set_markup() if attr_list is None: _, text, attr_list = self._pango_markup_parse(text) if attr_list is None: yield (text, None) raise StopIteration gtk_tags = defaultdict(dict) def parse_attr(attr, _data): gtk_attr = attr.klass.type.value_nick if gtk_attr in _pango_to_gtk: gtk_attr = _pango_to_gtk[gtk_attr] pango_attr, span = attr.klass.type.value_name, (attr.start_index, attr.end_index) assert pango_attr.startswith('PANGO_ATTR_'), pango_attr attr.__class__ = _pango_classes[ pango_attr[11:]] # allows to access attr.value for k in 'value', 'ink_rect', 'logical_rect', 'desc', 'color': if not hasattr(attr, k): continue val = getattr(attr, k) if k == 'color': val = '#' + ''.join( '{:02x}'.format(v / 256) for v in [val.red, val.green, val.blue]) gtk_tags[span][gtk_attr] = val break else: raise KeyError( 'Failed to extract value for pango attribute: {}'.format( pango_attr)) return False attr_list.filter(parse_attr, None) pos = 0 for (a, b), props in sorted(gtk_tags.viewitems()): if a > pos: yield (text[pos:a], None) yield (text[a:b], props) pos = b if text[pos:]: yield (text[pos:], None) def _get_default_css(self): css, base_css = Gtk.CssProvider(), self.base_css for attempt in xrange(6): try: css.load_from_data(base_css) except GLib.GError as err: log.warn('Failed to load default CSS style (try %s): %s', attempt + 1, err) # print(base_css) else: break # Try to work around https://bugzilla.gnome.org/show_bug.cgi?id=678876 and similar issues if attempt == 0: base_css = re.sub(br'\b(background-color:)\s*rgba\([^;]+;', br'\1 white;', base_css) elif attempt == 1: base_css = re.sub(br'\b(font-size:)\s*(\d+)px\s*;', br'\1 \2;', base_css) elif attempt == 2: base_css = re.sub(br'\b(text-shadow:)[^;]+;', br'\1 1 1 0 gray;', base_css) elif attempt == 3: base_css = re.sub(br'\btext-shadow:[^;]+;', b'', base_css) elif attempt == 4: base_css = self.base_css_min # last resort before no-css-at-all else: break # don't load any css return css def _update_layout(self): # Get the coordinates of the "anchor" corner (screen corner +/- margins) base = tuple(map( lambda ax, gdk_dim=('width', 'height'):\ (getattr(Gdk.Screen, gdk_dim[ax])() - self.margins[2**ax])\ if 2**ax & self.layout_anchor else self.margins[-2**ax], xrange(2) )) # Iterate over windows in order, placing each one starting from a "base" corner for win in map(op.attrgetter('gobj'), self._windows.viewvalues()): win.move(*map( lambda ax: base[ax] - (win.get_size()[ax] if 2**ax & self. layout_anchor else 0), xrange(2))) margin = self.margins[(2 * ( (2**self.layout_direction) & self.layout_anchor) / 2**self.layout_direction - 1) * 2**self.layout_direction] base = tuple(map( lambda ax: base[ax] if self.layout_direction != ax else\ base[ax] + (margin + win.get_size()[ax])\ * (2 * (2**ax ^ (2**ax & self.layout_anchor)) / 2**ax - 1), xrange(2) )) def _get_icon(self, icon, remote=False): widget_icon = None if icon is not None: if isinstance(icon, types.StringTypes): icon_path = os.path.expanduser(urllib.url2pathname(icon)) if icon_path.startswith('file://'): icon_path = icon_path[7:] if os.path.isfile(icon_path): widget_icon = GdkPixbuf.Pixbuf.new_from_file(icon_path) else: # Available names: Gtk.IconTheme.get_default().list_icons(None) theme = Gtk.IconTheme.get_default() icon_size = any(self.icon_scale.get('fixed', list())) or 32 widget_icon = theme.lookup_icon( icon, icon_size, Gtk.IconLookupFlags.USE_BUILTIN) if widget_icon: widget_icon = widget_icon.load_icon() else: # Msgs from remote hosts natually can have non-local icon paths in them ( log.warn if not remote else log.debug )('Provided icon info seem to be neither valid icon file nor' ' a name in a freedesktop.org-compliant icon theme (or current theme' ' does not have that one), ignoring it: %r', core.format_trunc(icon)) else: w, h, rowstride, has_alpha, bits_per_sample, channels, data = icon data = bytes(bytearray(data)) widget_icon = GdkPixbuf.Pixbuf.new_from_data( data, GdkPixbuf.Colorspace.RGB, bool(has_alpha), int(bits_per_sample), int(w), int(h), int(rowstride)) widget_icon._data = data # must be preserved from gc if widget_icon: if any(it.chain.from_iterable( self.icon_scale.values())): # scale icon w, h = widget_icon.get_width(), widget_icon.get_height() for k in 'fixed', 'min', 'max': box_w, box_h = self.icon_scale.get(k, (0, 0)) if not any([box_w, box_h]): continue if k == 'min' and not ((box_w and w < box_w) or (box_h and h < box_h)): continue if k == 'max' and not ((box_w and w > box_w) or (box_h and h > box_h)): continue scale_down = (box_w and w > box_w) or (box_h and h > box_h) if scale_down: scale = min # factor<1, unspec=1, must fit on both dimensions elif box_w and box_h: scale = min # factor>1, but still pick min to fit on both else: scale = max # ignore unspec=1 and scale to max possible factor scale = scale(float(box_w or w) / w, float(box_h or h) / h) box_w, box_h = w * scale, h * scale log.debug( 'Scaling image (%s, criteria: %s) by a factor of' ' %.3f: %dx%d -> %dx%d', ['up', 'down'][scale_down], k, scale, w, h, box_w, box_h) widget_icon = widget_icon.scale_simple( box_w, box_h, GdkPixbuf.InterpType.BILINEAR) if k == 'fixed': break # no need to apply min/max after that widget_icon, pixbuf = Gtk.Image(), widget_icon widget_icon.set_from_pixbuf(pixbuf) return widget_icon def _set_visual(self, win, ev=None): visual = win.get_screen().get_rgba_visual() if visual: win.set_visual(visual) def _create_win(self, summary, body, icon=None, urgency_label=None, markup=False, remote=None): log.debug( 'Creating window with parameters: %s', core.repr_trunc_rec( dict(summary=summary, body=body, icon=icon, urgency=urgency_label, markup=markup))) win = Gtk.Window(name='notification', type=Gtk.WindowType.POPUP) win.set_default_size(400, 20) win.connect('screen-changed', self._set_visual) self._set_visual(win) ev_boxes = [win] frame = Gtk.Box(name='frame') win.add(frame) try: widget_icon = self._get_icon(icon, remote=remote) except Exception: # Gdk may raise errors for some images/formats log.exception('Failed to set notification icon') widget_icon = None box_margin = 3 v_box = Gtk.VBox(spacing=box_margin, expand=False) if widget_icon is not None: h_box = Gtk.HBox(spacing=box_margin * 2) frame.pack_start(h_box, True, True, 0) h_box.pack_start(widget_icon, False, False, 0) h_box.pack_start(v_box, True, True, 0) ev_boxes.append(h_box) else: frame.pack_start(v_box, True, True, 0) widget_summary = Gtk.Label(name='summary') # Sanitize tags through pango first, so set_markup won't produce empty label summary_markup, summary_text, summary\ = self.get_display_summary(summary, markup) if summary_markup: widget_summary.set_markup(summary) else: widget_summary.set_text(summary) widget_summary.set_alignment(0, 0) if urgency_label: summary_box = Gtk.EventBox(name=urgency_label) summary_box.add(widget_summary) else: summary_box = widget_summary v_box.pack_start(summary_box, False, False, 0) ev_boxes.append(summary_box) v_box.pack_start(Gtk.HSeparator(name='hs'), False, False, 0) widget_body = Gtk.TextView(name='body', wrap_mode=Gtk.WrapMode.WORD_CHAR, cursor_visible=False, editable=False) widget_body_buffer = widget_body.get_buffer() body_markup, body_text, body_attrs = self.get_display_body( body, markup) if not body_markup: widget_body_buffer.set_text(body_text) else: # This buffer uses pango markup, even though GtkTextView does not support it # Most magic is in pango_markup_to_gtk(), there doesn't seem to be any cleaner way def get_tag(props, _tag_id=iter(xrange(2**31 - 1)), _tag_table=dict()): k = tuple(sorted(props.viewitems())) if k not in _tag_table: _tag_table[k] = widget_body_buffer\ .create_tag('x{}'.format(next(_tag_id)), **props) return _tag_table[k] pos = widget_body_buffer.get_end_iter() for text, props in body_attrs: if props: widget_body_buffer.insert_with_tags( pos, text, get_tag(props)) else: widget_body_buffer.insert(pos, text) v_box.pack_start(widget_body, True, True, 0) ev_boxes.append(widget_body) # Make sure the window is initially drawn off-screen, because it can't be # placed properly until it's size is known, and it's size is unknown until it's # actually handled by window manager and then drawn by X # Proper placement is done on update_layout() call win.move(-2000, -2000) win.show_all() return self.window(win, ev_boxes) def get_display_summary(self, summary, markup): if markup: success, text, _ = self._pango_markup_parse(summary) if not success: markup, summary = False, text else: text = summary return markup, text, summary def get_display_body(self, body, markup): if markup: _, text, attr_list = self._pango_markup_parse(body) if attr_list is None: markup, body_attrs = False, [(text, None)] else: body_attrs = self._pango_markup_to_gtk(text, attr_list) else: text, body_attrs = body, [(body, None)] return markup, text, body_attrs def get_note_markup(self, note): return note.hints.get('x-nt-markup', self.markup_default) def get_note_text(self, note): 'Returns note text, stripped of all markup, if any (and if enabled).' markup = self.get_note_markup(note) _, summary_text, _ = self.get_display_summary(note.summary, markup) _, body_text, _ = self.get_display_body(note.body, markup) return summary_text, body_text def display(self, note, cb_dismiss=None, cb_hover=None, cb_leave=None): try: # Priorities for icon sources: # image{-,_}data: hint. raw image data structure of signature (iiibiiay) # image{-,_}path: hint. either an URI (file://...) or a name in a f.o-compliant icon theme # app_icon: parameter. same as image-path # icon_data: hint. same as image-data # image_* is a deprecated hints from 1.1 spec, 1.2 is preferred # (don't seem to be even mentioned in 1.2 spec icon priorities section) hints = note.hints.copy() k = '__app_icon' # to avoid clobbering anything hints[k] = note.icon for k in 'image-data', 'image_data',\ 'image-path', 'image_path', k, 'icon_data': image = hints.get(k) if image: log.debug('Got icon image from hint: %s', k) break urgency = note.hints.get('urgency') if urgency is not None: urgency = core.urgency_levels.by_id(int(urgency)) markup = self.get_note_markup(note) win = self._create_win(note.summary, note.body, image, urgency, markup=markup, remote=note.hints.get('x-nt-from-remote')) for eb in win.event_boxes: eb.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK) for ev, cb in [('button-press-event', cb_dismiss), ('motion-notify-event', cb_hover), ('leave-notify-event', cb_leave)]: if cb: eb.connect(ev, lambda w, ev, cb, nid: cb(nid), cb, note.id) if cb_dismiss and win.event_boxes: # Connect only to window object (or first eventbox in the list) win.event_boxes[0].connect('destroy', lambda w, cb, nid: cb(nid), cb_dismiss, note.id) # update_layout() *must* be delayed until window "configure-event", because # actual window size is unknown until it's resized by window manager and drawn by X # See the list of caveats here: # http://developer.gnome.org/gtk3/unstable/GtkWindow.html#gtk-window-get-size win.gobj.connect('configure-event', lambda w, void: self._update_layout()) self._windows[note.id] = win except: log.exception('Failed to create notification window') class NoWindowError(Exception): pass def _close(self, nid): try: win = self._windows.pop(nid).gobj except KeyError: raise self.NoWindowError(nid) win.hide(), win.destroy() def close(self, nid): self._close(nid) self._update_layout()
class DotMap(OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() if args: d = args[0] if type(d) is dict: for k, v in self.__call_items(d): if type(v) is dict: v = DotMap(v) self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map: # DON'T automatically extend to new DotMap # self[k] = DotMap() raise AttributeError('%s is not defined in DotMap' % k) return self._map[k] def __setattr__(self, k, v): if k == '_map': super(DotMap, self).__setattr__(k, v) else: self[k] = v def __getattr__(self, k): if k == '_map': super(DotMap, self).__getattr__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k, v in self.__call_items(self._map): items.append('{0}={1}'.format(k, repr(v))) out = 'DotMap({0})'.format(', '.join(items)) return out def __repr__(self): return str(self) def toDict(self): d = {} for k, v in self.items(): if type(v) is DotMap: v = v.toDict() d[k] = v return d def pprint(self): pprint(self.toDict()) # proper dict subclassing def values(self): return self._map.values() @classmethod def parseOther(self, other): if type(other) is DotMap: return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return self def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = OrderedDict.fromkeys(seq, value) return d
class NotificationDisplay(object): '''Interface to display notification stack. Should have "display(note, cb_dismiss=None) -> nid(UInt32, >0)", "close(nid)" methods and NoWindowError(nid) exception, raised on erroneous nid's in close(). Current implementation based on notipy: git://github.com/the-isz/notipy.git''' window = namedtuple('Window', 'gobj event_boxes') base_css = b''' #notification { background: transparent; } #notification #frame { padding: 3px; background-color: #d4ded8; } #notification #hs { background-color: black; } #notification #critical { background-color: #ffaeae; } #notification #normal { background-color: #f0ffec; } #notification #low { background-color: #bee3c6; } #notification #summary { padding-left: 5px; font-size: 10px; text-shadow: 1px 1px 0px gray; } #notification #body { font-size: 8px; } #notification #body * { background-color: #d4ded8; } ''' base_css_min = b'#notification * { font-size: 8; }' # simpliest fallback def __init__( self, layout_margin, layout_anchor, layout_direction, icon_width, icon_height, markup_default=False, markup_warn=False, markup_strip=False ): self.margins = dict(it.chain.from_iterable(map( lambda ax: ( (2**ax, layout_margin), (-2**ax, layout_margin) ), xrange(2) ))) self.layout_margin = layout_margin self.layout_anchor = layout_anchor self.layout_direction = layout_direction self.icon_width = icon_width self.icon_height = icon_height self.markup_default = markup_default self.markup_warn, self.markup_strip = markup_warn, markup_strip self._windows = OrderedDict() self._default_style = self._get_default_css() Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), self._default_style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION ) def _pango_markup_parse(self, text, _err_mark='[TN82u8] '): success = True try: _, attr_list, text, _ = Pango.parse_markup(text, -1, '\0') except GLib.GError as err: if self.markup_warn: msg_start = '{}Pango formatting failed'.format(_err_mark) if msg_start not in text: # detect and avoid possible feedback loops log.warn('%s (%s) for text, stripping markup: %r', msg_start, err, text) else: text = xml_escape(text) # escape message so it'd render bugged part if self.markup_strip: text = strip_markup(text) try: _, attr_list, text, _ = Pango.parse_markup(text, -1, '\0') except GLib.GError: attr_list = None success = False return success, text, attr_list def _pango_markup_to_gtk( self, text, attr_list=None, _pango_classes={ 'SIZE': Pango.AttrInt, 'WEIGHT': Pango.AttrInt, 'UNDERLINE': Pango.AttrInt, 'STRETCH': Pango.AttrInt, 'VARIANT': Pango.AttrInt, 'STYLE': Pango.AttrInt, 'SCALE': Pango.AttrFloat, 'FAMILY': Pango.AttrString, 'FONT_DESC': Pango.AttrFontDesc, 'STRIKETHROUGH': Pango.AttrInt, 'BACKGROUND': Pango.AttrColor, 'FOREGROUND': Pango.AttrColor, 'RISE': Pango.AttrInt }, _pango_to_gtk={'font_desc': 'font'} ): # See https://bugzilla.gnome.org/show_bug.cgi?id=59390 for why it is necessary # And doesn't work with GI anyway because of # https://bugzilla.gnome.org/show_bug.cgi?id=646788 # "Behdad Esfahbod [pango developer]: I personally have no clue how to fix this" # Workaround from https://github.com/matasbbb/pitivit/commit/da815339e # TODO: fix when AttrList.get_iterator will be accessible via GI or textbuffer gets set_markup() if attr_list is None: _, text, attr_list = self._pango_markup_parse(text) if attr_list is None: yield (text, None) raise StopIteration gtk_tags = defaultdict(dict) def parse_attr(attr, _data): gtk_attr = attr.klass.type.value_nick if gtk_attr in _pango_to_gtk: gtk_attr = _pango_to_gtk[gtk_attr] pango_attr, span = attr.klass.type.value_name, (attr.start_index, attr.end_index) assert pango_attr.startswith('PANGO_ATTR_'), pango_attr attr.__class__ = _pango_classes[pango_attr[11:]] # allows to access attr.value for k in 'value', 'ink_rect', 'logical_rect', 'desc', 'color': if not hasattr(attr, k): continue val = getattr(attr, k) if k == 'color': val = '#' + ''.join('{:02x}'.format(v/256) for v in [val.red, val.green, val.blue]) gtk_tags[span][gtk_attr] = val break else: raise KeyError('Failed to extract value for pango attribute: {}'.format(pango_attr)) return False attr_list.filter(parse_attr, None) pos = 0 for (a, b), props in sorted(gtk_tags.viewitems()): if a > pos: yield (text[pos:a], None) yield (text[a:b], props) pos = b if text[pos:]: yield (text[pos:], None) def _get_default_css(self): css, base_css = Gtk.CssProvider(), self.base_css for attempt in xrange(6): try: css.load_from_data(base_css) except GLib.GError as err: log.warn('Failed to load default CSS style (try %s): %s', attempt+1, err) # print(base_css) else: break # Try to work around https://bugzilla.gnome.org/show_bug.cgi?id=678876 and similar issues if attempt == 0: base_css = re.sub(br'\b(background-color:)\s*rgba\([^;]+;', br'\1 white;', base_css) elif attempt == 1: base_css = re.sub(br'\b(font-size:)\s*(\d+)px\s*;', br'\1 \2;', base_css) elif attempt == 2: base_css = re.sub(br'\b(text-shadow:)[^;]+;', br'\1 1 1 0 gray;', base_css) elif attempt == 3: base_css = re.sub(br'\btext-shadow:[^;]+;', b'', base_css) elif attempt == 4: base_css = self.base_css_min # last resort before no-css-at-all else: break # don't load any css return css def _update_layout(self): # Get the coordinates of the "anchor" corner (screen corner +/- margins) base = tuple(map( lambda ax, gdk_dim=('width', 'height'):\ (getattr(Gdk.Screen, gdk_dim[ax])() - self.margins[2**ax])\ if 2**ax & self.layout_anchor else self.margins[-2**ax], xrange(2) )) # Iterate over windows in order, placing each one starting from a "base" corner for win in map(op.attrgetter('gobj'), self._windows.viewvalues()): win.move(*map(lambda ax: base[ax] - ( win.get_size()[ax] if 2**ax & self.layout_anchor else 0 ), xrange(2))) margin = self.margins[(2 * ( (2**self.layout_direction) & self.layout_anchor ) / 2**self.layout_direction - 1) * 2**self.layout_direction] base = tuple(map( lambda ax: base[ax] if self.layout_direction != ax else\ base[ax] + (margin + win.get_size()[ax])\ * (2 * (2**ax ^ (2**ax & self.layout_anchor)) / 2**ax - 1), xrange(2) )) def _get_icon(self, icon): widget_icon = None if icon is not None: if isinstance(icon, types.StringTypes): icon_path = os.path.expanduser(urllib.url2pathname(icon)) if icon_path.startswith('file://'): icon_path = icon_path[7:] if os.path.isfile(icon_path): widget_icon = GdkPixbuf.Pixbuf.new_from_file(icon_path) else: # Available names: Gtk.IconTheme.get_default().list_icons(None) theme = Gtk.IconTheme.get_default() icon_size = self.icon_width or self.icon_height or 32 widget_icon = theme.lookup_icon( icon, icon_size, Gtk.IconLookupFlags.USE_BUILTIN ) if widget_icon: widget_icon = widget_icon.load_icon() else: log.warn( '"%s" seems to be neither a valid icon file nor' ' a name in a freedesktop.org-compliant icon theme (or your theme' ' doesnt have that name). Ignoring.', icon ) else: widget_icon = GdkPixbuf.Pixbuf.new_from_data( bytearray(icon[6]), GdkPixbuf.Colorspace.RGB, icon[3], icon[4], icon[0], icon[1], icon[2], lambda x, y: None, None ) if widget_icon: if self.icon_width or self.icon_height: # scale icon w, h = widget_icon.get_width(), widget_icon.get_height() # Use max (among w/h) factor on scale-up and min on scale-down, # so resulting icon will always fit in a specified box, # and will match it by (at least) w or h (ideally - both) scale = (self.icon_width and w > self.icon_width)\ or (self.icon_height and h > self.icon_height) # True if it's a scale-up scale = (min if bool(scale) ^ bool( self.icon_width and self.icon_height ) else max)\ (float(self.icon_width or w) / w, float(self.icon_height or h) / h) widget_icon = widget_icon.scale_simple( w * scale, h * scale, GdkPixbuf.InterpType.BILINEAR ) widget_icon, pixbuf = Gtk.Image(), widget_icon widget_icon.set_from_pixbuf(pixbuf) return widget_icon def _set_visual(self, win, ev=None): visual = win.get_screen().get_rgba_visual() if visual: win.set_visual(visual) def _create_win(self, summary, body, icon=None, urgency_label=None, markup=False): log.debug( 'Creating window with parameters: %s', dict(summary=summary, body=body, icon=icon, urgency=urgency_label, markup=markup) ) win = Gtk.Window(name='notification', type=Gtk.WindowType.POPUP) win.set_default_size(400, 20) win.connect('screen-changed', self._set_visual) self._set_visual(win) ev_boxes = [win] frame = Gtk.Frame(name='frame', shadow_type=Gtk.ShadowType.ETCHED_OUT) win.add(frame) try: widget_icon = self._get_icon(icon) except Exception: # Gdk may raise errors for some images/formats log.exception('Failed to set notification icon') widget_icon = None v_box = Gtk.VBox(spacing=self.layout_margin, expand=False) if widget_icon is not None: h_box = Gtk.HBox(spacing=self.layout_margin * 2) frame.add(h_box) h_box.pack_start(widget_icon, False, False, 0) h_box.pack_start(v_box, True, True, 0) ev_boxes.append(h_box) else: frame.add(v_box) widget_summary = Gtk.Label(name='summary') # Sanitize tags through pango first, so set_markup won't produce empty label summary_markup, summary_text, summary\ = self.get_display_summary(summary, markup) if summary_markup: widget_summary.set_markup(summary) else: widget_summary.set_text(summary) widget_summary.set_alignment(0, 0) if urgency_label: summary_box = Gtk.EventBox(name=urgency_label) summary_box.add(widget_summary) else: summary_box = widget_summary v_box.pack_start(summary_box, False, False, 0) ev_boxes.append(summary_box) v_box.pack_start(Gtk.HSeparator(name='hs'), False, False, 0) widget_body = Gtk.TextView( name='body', wrap_mode=Gtk.WrapMode.WORD_CHAR, cursor_visible=False, editable=False ) widget_body_buffer = widget_body.get_buffer() body_markup, body_text, body_attrs = self.get_display_body(body, markup) if not body_markup: widget_body_buffer.set_text(body_text) else: # This buffer uses pango markup, even though GtkTextView does not support it # Most magic is in pango_markup_to_gtk(), there doesn't seem to be any cleaner way def get_tag(props, _tag_id=iter(xrange(2**31-1)), _tag_table=dict()): k = tuple(sorted(props.viewitems())) if k not in _tag_table: _tag_table[k] = widget_body_buffer\ .create_tag('x{}'.format(next(_tag_id)), **props) return _tag_table[k] pos = widget_body_buffer.get_end_iter() for text, props in body_attrs: if props: widget_body_buffer.insert_with_tags(pos, text, get_tag(props)) else: widget_body_buffer.insert(pos, text) v_box.pack_start(widget_body, False, False, 0) ev_boxes.append(widget_body) # Make sure the window is initially drawn off-screen, because it can't be # placed properly until it's size is known, and it's size is unknown until it's # actually handled by window manager and then drawn by X # Proper placement is done on update_layout() call win.move(-2000, -2000) win.show_all() return self.window(win, ev_boxes) def get_display_summary(self, summary, markup): if markup: success, text, _ = self._pango_markup_parse(summary) if not success: markup, summary = False, text else: text = summary return markup, text, summary def get_display_body(self, body, markup): if markup: _, text, attr_list = self._pango_markup_parse(body) if attr_list is None: markup, body_attrs = False, [(text, None)] else: body_attrs = self._pango_markup_to_gtk(text, attr_list) else: text = body return markup, text, body_attrs def get_note_markup(self, note): return note.hints.get('x-nt-markup', self.markup_default) def get_note_text(self, note): 'Returns note text, stripped of all markup, if any (and if enabled).' markup = self.get_note_markup(note) _, summary_text, _ = self.get_display_summary(note.summary, markup) _, body_text, _ = self.get_display_body(note.body, markup) return summary_text, body_text def display(self, note, cb_dismiss=None, cb_hover=None, cb_leave=None): try: # Priorities for icon sources: # image{-,_}data: hint. raw image data structure of signature (iiibiiay) # image{-,_}path: hint. either an URI (file://...) or a name in a f.o-compliant icon theme # app_icon: parameter. same as image-path # icon_data: hint. same as image-data # image_* is a deprecated hints from 1.1 spec, 1.2 is preferred # (don't seem to be even mentioned in 1.2 spec icon priorities section) hints = note.hints.copy() k = '__app_icon' # to avoid clobbering anything hints[k] = note.icon for k in 'image-data', 'image_data',\ 'image-path', 'image_path', k, 'icon_data': image = hints.get(k) if image: log.debug('Got icon image from hint: %s', k) break urgency = note.hints.get('urgency') if urgency is not None: urgency = urgency_levels.by_id(int(urgency)) markup = self.get_note_markup(note) win = self._create_win(note.summary, note.body, image, urgency, markup=markup) for eb in win.event_boxes: eb.add_events( Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK ) for ev,cb in [ ('button-press-event', cb_dismiss), ('motion-notify-event', cb_hover), ('leave-notify-event', cb_leave) ]: if cb: eb.connect(ev, lambda w,ev,cb,nid: cb(nid), cb, note.id) if cb_dismiss and win.event_boxes: # Connect only to window object (or first eventbox in the list) win.event_boxes[0].connect( 'destroy', lambda w,cb,nid: cb(nid), cb_dismiss, note.id ) # update_layout() *must* be delayed until window "configure-event", because # actual window size is unknown until it's resized by window manager and drawn by X # See the list of caveats here: # http://developer.gnome.org/gtk3/unstable/GtkWindow.html#gtk-window-get-size win.gobj.connect('configure-event', lambda w,void: self._update_layout()) self._windows[note.id] = win except: log.exception('Failed to create notification window') class NoWindowError(Exception): pass def _close(self, nid): try: win = self._windows.pop(nid).gobj except KeyError: raise self.NoWindowError(nid) win.hide(), win.destroy() def close(self, nid): self._close(nid) self._update_layout()
class NotificationDisplay(object): '''Interface to display notification stack. Should have "display(note, cb_dismiss=None) -> nid(UInt32, >0)", "close(nid)" methods and NoWindowError(nid) exception, raised on erroneous nid's in close(). Current implementation based on notipy: git://github.com/the-isz/notipy.git''' window = namedtuple('Window', 'gobj event_boxes') def __init__(self, layout_margin, layout_anchor, layout_direction, img_w, img_h): self.margins = dict( it.chain.from_iterable( map( lambda ax: ((2**ax, layout_margin), (-2**ax, layout_margin)), xrange(2)))) self.layout_margin = layout_margin self.layout_anchor = layout_anchor self.layout_direction = layout_direction self.img_w = img_w self.img_h = img_h self._windows = OrderedDict() self._default_style = Gtk.CssProvider() self._default_style.load_from_data(b''' #notification { background-color: white; } #notification #hs { background-color: black; } #notification #critical { background-color: #ffaeae; } #notification #normal { background-color: #f0ffec; } #notification #low { background-color: #bee3c6; } #notification #summary { font-size: 10; text-shadow: 1 1 0 gray; } #notification #body { font-size: 8; }''') Gtk.StyleContext.add_provider_for_screen( Gdk.Screen.get_default(), self._default_style, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) def _update_layout(self): # Yep, I was SMOKING CRACK here, and it all made sense at the time base = tuple(map( lambda ax, gdk_dim=('width', 'height'):\ (getattr(Gdk.Screen, gdk_dim[ax])() - self.margins[2**ax])\ if 2**ax & self.layout_anchor else self.margins[-2**ax], xrange(2) )) for win in map(op.attrgetter('gobj'), self._windows.viewvalues()): win.move(*map( lambda ax: base[ax] - (win.get_size()[ax] if 2**ax & self. layout_anchor else 0), xrange(2))) margin = self.margins[(2 * ( (2**self.layout_direction) & self.layout_anchor) / 2**self.layout_direction - 1) * 2**self.layout_direction] base = tuple(map( lambda ax: base[ax] if self.layout_direction != ax else\ base[ax] + (margin + win.get_size()[ax])\ * (2 * (2**ax ^ (2**ax & self.layout_anchor)) / 2**ax - 1), xrange(2) )) def _create_win(self, summary, body, icon=None, urgency_label=None): log.debug( 'Creating window with parameters: {}'\ .format(', '.join(map(unicode, [summary, body, icon, urgency_label]))) ) win = Gtk.Window(name='notification', type=Gtk.WindowType.POPUP) win.set_default_size(400, 20) ev_boxes = [win] frame = Gtk.Frame(shadow_type=Gtk.ShadowType.ETCHED_OUT) win.add(frame) widget_icon = None if icon is not None: if isinstance(icon, unicode): icon_path = os.path.expanduser(urllib.url2pathname(icon)) if icon_path.startswith('file://'): icon_path = icon_path[7:] if os.path.isfile(icon_path): widget_icon = Gtk.Image() if self.img_w != -1 or self.img_h != -1: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( icon_path, self.img_w, self.img_h) #scaled_buf = pixbuf.scale_simple(self.img_w, self.img_h, gtk.gdk.INTERP_BILINEAR) widget_icon.set_from_pixbuf(pixbuf) else: widget_icon.set_from_file(icon_path) else: # available names: Gtk.IconTheme.get_default().list_icons(None) theme = Gtk.IconTheme.get_default() if theme.has_icon(icon): widget_icon = Gtk.Image() widget_icon.set_from_icon_name( icon, Gtk.IconSize.DND) # XXX: why this IconSize? else: log.warn(( '"{}" seems to be neither a valid icon file nor' ' a name in a freedesktop.org-compliant icon theme (or your theme' ' doesnt have that name). Ignoring.').format(icon)) else: # For image-data and icon_data, image should look like this: # dbus.Struct( # (dbus.Int32, # width # dbus.Int32, # height # dbus.Int32, # rowstride # dbus.Boolean, # has alpha # dbus.Int32, # bits per sample # dbus.Int32, # channels # dbus.Array([dbus.Byte, ...])) # image data # ) # data, colorspace, has_alpha, bits_per_sample, # width, height, rowstride, destroy_fn, destroy_fn_data # XXX: Do I need to free the image via a function callback? pixbuf = GdkPixbuf.Pixbuf.new_from_data( bytearray(icon[6]), GdkPixbuf.Colorspace.RGB, icon[3], icon[4], icon[0], icon[1], icon[2], lambda x, y: None, None) widget_icon = Gtk.Image() widget_icon.set_from_pixbuf(pixbuf) v_box = Gtk.VBox(spacing=self.layout_margin, expand=False) if widget_icon is not None: h_box = Gtk.HBox(spacing=self.layout_margin * 2) frame.add(h_box) h_box.pack_start(widget_icon, False, False, 0) h_box.pack_start(v_box, True, True, 0) else: frame.add(v_box) widget_summary = Gtk.Label(name='summary', label=summary) widget_summary.set_alignment(0, 0) if urgency_label: summary_box = Gtk.EventBox(name=urgency_label) summary_box.add(widget_summary) else: summary_box = widget_summary v_box.pack_start(summary_box, False, False, 0) v_box.pack_start(Gtk.HSeparator(name='hs'), False, False, 0) widget_body = Gtk.TextView(name='body', wrap_mode=Gtk.WrapMode.WORD_CHAR) widget_body_buffer = widget_body.get_buffer() widget_body_buffer.set_text(body) v_box.pack_start(widget_body, False, False, 0) ev_boxes.append(widget_body) win.show_all() return self.window(win, ev_boxes) def display(self, note, cb_dismiss=None, cb_hover=None, cb_leave=None): try: # Priorities for icon sources: # image{-,_}data: hint. raw image data structure of signature (iiibiiay) # image{-,_}path: hint. either an URI (file://...) or a name in a f.o-compliant icon theme # app_icon: parameter. same as image-path # icon_data: hint. same as image-data # image_* is a deprecated hints from 1.1 spec, 1.2 is preferred # (don't seem to be even mentioned in 1.2 spec icon priorities section) hints = note.hints.copy() k = '__app_icon' # to avoid clobbering anything hints[k] = note.icon for k in 'image-data', 'image_data',\ 'image-path', 'image_path', k, 'icon_data': image = hints.get(k) if image: log.debug('Got icon image from hint: {}'.format(k)) break urgency = note.hints.get('urgency') if urgency is not None: urgency = urgency_levels.by_id(int(urgency)) win = self._create_win(note.summary, note.body, image, urgency) for eb in win.event_boxes: eb.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK) for ev, cb in [('button-press-event', cb_dismiss), ('motion-notify-event', cb_hover), ('leave-notify-event', cb_leave)]: if cb: eb.connect(ev, lambda w, ev, cb, nid: cb(nid), cb, note.id) if cb_dismiss and win.event_boxes: # Connect only to window object (or first eventbox in the list) win.event_boxes[0].connect('destroy', lambda w, cb, nid: cb(nid), cb_dismiss, note.id) self._windows[note.id] = win self._update_layout() except: log.exception('Failed to create notification window') class NoWindowError(Exception): pass def _close(self, nid): try: win = self._windows.pop(nid).gobj except KeyError: raise self.NoWindowError(nid) win.hide(), win.destroy() def close(self, nid): self._close(nid) self._update_layout()
class DotMap(OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() self._dynamic = True if kwargs: if '_dynamic' in kwargs: self._dynamic = kwargs['_dynamic'] if args: d = args[0] if isinstance(d, dict): for k,v in self.__call_items(d): if type(v) is dict: v = DotMap(v, _dynamic=self._dynamic) if type(v) is list: l = [] for i in v: n = i if type(i) is dict: n = DotMap(i, _dynamic=self._dynamic) l.append(n) v = l self._map[k] = v if kwargs: for k,v in self.__call_items(kwargs): if k is not '_dynamic': self._map[k] = v def __call_items(self, obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def next(self): return self._map.next() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map and self._dynamic and k != '_ipython_canary_method_should_not_exist_': # automatically extend to new DotMap self[k] = DotMap() return self._map[k] def __setattr__(self, k, v): if k in {'_map','_dynamic', '_ipython_canary_method_should_not_exist_'}: super(DotMap, self).__setattr__(k,v) else: self[k] = v def __getattr__(self, k): if k == {'_map','_dynamic','_ipython_canary_method_should_not_exist_'}: super(DotMap, self).__getattr__(k) else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k,v in self.__call_items(self._map): # bizarre recursive assignment situation (why someone would do this is beyond me) if id(v) == id(self): items.append('{0}=DotMap(...)'.format(k)) else: items.append('{0}={1}'.format(k, repr(v))) out = 'DotMap({0})'.format(', '.join(items)) return out def __repr__(self): return str(self) def toDict(self): d = {} for k,v in self.items(): if type(v) is DotMap: # bizarre recursive assignment support if id(v) == id(self): v = d else: v = v.toDict() elif type(v) is list: l = [] for i in v: n = i if type(i) is DotMap: n = i.toDict() l.append(n) v = l d[k] = v return d def pprint(self): pprint(self.toDict()) def empty(self): return (not any(self)) # proper dict subclassing def values(self): return self._map.values() # ipython support def __dir__(self): return self.keys() @classmethod def parseOther(self, other): if type(other) is DotMap: return other._map else: return other def __cmp__(self, other): other = DotMap.parseOther(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parseOther(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parseOther(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parseOther(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parseOther(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parseOther(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def clear(self): self._map.clear() def copy(self): return DotMap(self.toDict()) def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def popitem(self): return self._map.popitem() def setdefault(self, key, default=None): self._map.setdefault(key, default) def update(self, *args, **kwargs): if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def viewitems(self): return self._map.viewitems() def viewkeys(self): return self._map.viewkeys() def viewvalues(self): return self._map.viewvalues() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = OrderedDict.fromkeys(seq, value) return d def __getstate__(self): return self.__dict__ def __setstate__(self, d): self.__dict__.update(d)
class Maps(MutableMapping): """ Converts a dictionary of key:value pairs into a dotted representation of those keys. Normal string representation of keys is still accessible via normal dictionary indexing. Note: If a key contains non-alphanumeric characters (!@#$%, etc, including spaces), they will be replaced with an underscore (_). Examples: >>> # Normal usage >>> test = {"hello": "world"} >>> print(Maps(test)) Output: Maps(hello="world") >>> test = {"hello": "world"} >>> maps = Maps(test) >>> print(maps.hello) Output: "world" >>> test = {"hello": "world"} >>> maps = Maps(test) >>> print(maps["hello"]) Output: "world" >>> # If a dictionary key has non-alphanumeric characters >>> # Notice how a series of special characters is replaced >>> # by only a single underscore >>> test = {"hello joh*&^n": "hi computer"} >>> maps = Maps(test) >>> print(maps) Output: Maps(hello_joh_n="hi computer") Raises: ValueError: An argument is of a legal type but is, or contains, an illegal value. """ # Class-level variables _dynamic: bool _map: OrderedDict def __init__(self, *args, **kwargs) -> None: super().__init__() self._dynamic = True self._map = OrderedDict() if kwargs: for key, value in self._get_items(kwargs): key = re.sub('[^0-9a-zA-Z]+', '_', key) if key != '_dynamic': self._map[key] = value else: self._dynamic = value if args: dictionary = args[0] if not isinstance(dictionary, dict): raise ValueError( "object passed to constructor must be of type 'dict': " f"'{type(dictionary).__name__}'") # Recursive handling tracked_ids = {id(dictionary): self} for key, value in self._get_items(dictionary): if isinstance(key, str): key = re.sub('[^0-9a-zA-Z]+', '_', key) value_id = id(value) if isinstance(value, dict): if value_id in tracked_ids: value = tracked_ids[value_id] else: value = self.__class__(value, _dynamic=self._dynamic) tracked_ids[value_id] = value if isinstance(value, list): listed_items = [] for item in value: temp_item = item if isinstance(item, dict): temp_item = self.__class__(item, _dynamic=self._dynamic) listed_items.append(temp_item) value = listed_items try: self._map[key] = ast.literal_eval(value) except NameError: if value.lower() == "false": self._map[key] = False elif value.lower() == "true": self._map[key] = True else: self._map[key] = value except (SyntaxError, ValueError): # Cannot eval this value self._map[key] = value # Dunder methods def __add__(self, value: object) -> Union[Any, NoReturn]: if self.empty(): return value else: self_type = type(self).__name__ value_type = type(value).__name__ raise TypeError( f"unsupported operand type(s) for +: '{self_type}' and '{value_type}'" ) def __cmp__(self, value: object) -> Any: value = Maps.parse_value(value) return self._map.__cmp__(value) def __contains__(self, name: str) -> bool: return self._map.__contains__(name) def __copy__(self) -> Maps: return self.__class__(self) def __deepcopy__(self) -> Maps: return self.copy() def __delitem__( self, key: str, dict_delitem: Optional[Callable[..., Any]] = dict.__delitem__) -> Any: return self._map.__delitem__(key, dict_delitem=dict_delitem) def __dir__(self) -> Iterable: return self.keys() def __eq__(self, value: Any) -> bool: value = Maps.parse_value(value) if not isinstance(value, dict): return False return self._map.__eq__(value) def __ge__(self, value: Any) -> bool: value = Maps.parse_value(value) return self._map.__ge__(value) def __gt__(self, value: Any) -> bool: value = Maps.parse_value(value) return self._map.__gt__(value) def __iter__(self) -> Iterable: return self._map.__iter__() def __le__(self, value: Any) -> bool: value = Maps.parse_value(value) return self._map.__le__(value) def __len__(self) -> int: return self._map.__len__() def __lt__(self, value: Any) -> bool: value = Maps.parse_value(value) return self._map.__lt__(value) def __ne__(self, value: Any) -> bool: value = Maps.parse_value(value) return self._map.__ne__(value) def __repr__(self) -> str: return str(self) def __str__(self) -> str: items = [] for key, value in self._get_items(self._map): # Recursive assignment case if id(value) == id(self): items.append("{0}={1}(...)".format(key, self.__class__.__name__)) else: items.append("{0}={1}".format(key, repr(value))) joined = ", ".join(items) return "{0}({1})".format(self.__class__.__name__, joined) def __delattr__(self, name: str) -> None: self._map.__delitem__(name) def __getattr__(self, name: str) -> Any: if name in ('_map', '_dynamic', "_ipython_canary_method_should_not_exist_"): return super().__getattr__(name) try: return super(self.__class__, self).__getattribute__(name) except AttributeError: pass return self[name] def __setattr__(self, name: str, value: Any) -> None: if name in ('_map', '_dynamic', "_ipython_canary_method_should_not_exist_"): super().__setattr__(name, value) else: self[name] = value def __getitem__(self, name: str) -> Union[Any, Maps]: if (name not in self._map and self._dynamic and name != "_ipython_canary_method_should_not_exist_"): self[name] = self.__class__() return self._map[name] def __setitem__(self, name: str, value: Any) -> None: self._map[name] = value def __getstate__(self) -> dict: return self.__dict__ def __setstate__(self, value: dict) -> None: self.__dict__.update(value) # Internal methods def _get_items(self, item: Any) -> Iterable: if hasattr(item, 'iteritems') and ismethod(getattr(item, 'iteritems')): return item.iteritems() else: return item.items() # Public methods def clear(self) -> None: """Remove all items from the Maps object.""" self._map.clear() def copy(self) -> Maps: """Makes a copy of the Maps object in memory.""" return self.__copy__() def empty(self) -> bool: """Returns whether the Maps object is empty.""" return (not any(self)) @classmethod def fromkeys(cls, iterable: Iterable, value: Optional[Any] = None) -> Iterable: """Returns a new :obj:`Maps` object with keys supplied from an iterable setting each key in the object with :term:`value`. Args: iterable (:obj:`Iterable`): Any iterable. value (:obj:`obj`, optional): The value to set for the keys. Default is :obj:`None`. Returns: Maps: The :obj:`Maps` object. """ maps = cls() maps.map = OrderedDict.fromkeys(iterable, value) return maps def get(self, key: str, default: Optional[Any] = None) -> Any: """ Returns the value of 'key'. If :term:`key` does not exist, :term:default` is returned instead. Args: key (:obj:`str`): The key to get the value needed from the dict. default (:obj:`obj`, optional): The value to return if :term:`key` does not exist. Returns: Any: The value at :term:`key` or :term:default`. """ return self._map.get(key, default) def has_key(self, key: str) -> bool: return key in self._map def items(self) -> Generator[Tuple[str, Any]]: """Returns a generator yielding a (key, value) pair.""" return self._get_items(self._map) def iteritems(self) -> Iterator: """ Returns an iterator over the Maps oject's (key, value) pairs. """ return self.items() def iterkeys(self) -> Iterator: """Returns an iterator over the Maps object's keys.""" return self._map.iterkeys() def itervalues(self) -> Iterator: """Returns an iterator over the Maps object's values.""" return self._map.itervalues() def keys(self) -> Iterable: """Returns the keys of the Maps object.""" return self._map.keys() def next(self) -> str: """Returns the next key in the dictionary.""" return self._map.next() @classmethod def parse_ini(cls, ini_dict: ConfigParser, to_maps=False) -> Union[dict, Maps]: """ Converts the values from an INI file from all strings to their actual Python base-types (i.e. int, float, bool, etc). If the value cannot be converted, it is kept as a string. If a value of the key:value pairs is not a string, its type is maintained. Note: Any meant-to-be-bool values in the key:value pairs that are not exactly 'False' or 'True', but are similar like 'false' or 'tRue' for example, will be converted to bools. Args: ini_dict (:obj:`ConfigParser`): The dictionary returned by configparser when an INI file is loaded. to_maps (:obj:`bool`): Return a :obj:`Maps` object instead of a :obj:`dict`. Returns: dict or Maps: A dictionary maintaining the same key:value pairs as the input dictionary; however, the values are their Python base-types. If :obj:`to_maps` is :obj:`True`, return a :obj:`Maps` object. Raises: TypeError: An argument is of an illegal type. """ # Check for dict because of recursion; ini_dict is only meant # to be a dict when the function recursively converts the values # from a ConfigParser if not isinstance(ini_dict, (dict, ConfigParser)): raise TypeError( "argument 'ini_dict' must be of type 'ConfigParser': " f"{type(ini_dict).__name__}") if isinstance(ini_dict, ConfigParser): ini_dict_ = {} for section in ini_dict.sections(): ini_dict_[section] = {} for option in ini_dict.options(section): # Parse using configparser option_value = ini_dict.get(section, option) # Parse using os environ matches = [(m.start(0), m.end(0)) for m in re.finditer("&", option_value)] if len(matches) > 0 and len(matches) % 2 == 0: i = 0 while True: try: index_end = matches.pop(i + 1)[1] index_start = matches.pop(i)[0] sub = option_value[index_start:index_end] sub_replace = os.environ[sub[1:-1]] option_value = option_value.replace( sub, sub_replace) except IndexError: break except KeyError: pass ini_dict_[section][option] = option_value ini_dict = ini_dict_ for key, value in ini_dict.items(): if isinstance(value, dict): # Recursively parse dict ini_dict[key] = Maps.parse_ini(value, to_maps=to_maps) else: if not isinstance(value, str): continue try: ini_dict[key] = ast.literal_eval(value) except NameError: if value.lower() == "false": ini_dict[key] = False elif value.lower() == "true": ini_dict[key] = True else: ini_dict[key] = value except (SyntaxError, ValueError): # Cannot eval this value ini_dict[key] = value return Maps(ini_dict) if to_maps else ini_dict @classmethod def parse_value(cls, value: Any) -> Any: """ Checks if :term:`value` subclasses :obj:`Maps`. If so, it returns the :obj:`Maps` object; otherwise the :term:`value` itself. Args: value (:obj:`Any`): The value to parse. Returns: Any: :obj:`OrderedDict` if :term:`value` subclasses :obj:`Maps`, otherwise :term:`value`. """ if issubclass(type(value), Maps): return value.map else: return value def pop(self, key: str, default: Optional[Any] = None) -> Union[Any, NoReturn]: """ Removes and returns the value in the Maps object at 'key'. If 'key' does not exist, then 'default' is returned. Args: key (:obj:`str`): The key to use to remove a value from the Maps object. default (:obj:`obj`, optional): The value to return if :term:`key` does not exist in the :obj:`Maps` object. Returns: Any: The value at :term:`key`, otherwise :term:`default`. """ return self._map.pop() def popitem(self) -> Any: """Removes and returns an arbitrary (key, value) pair from the :obj:`Maps` object. Returns: Any: The arbitrary (key, value) pair. Raises: KeyError: The :obj:`Maps` object is empty. """ return self._map.popitem() def setdefault(self, key: str, default=None) -> Any: """ Returns a value of the 'key' in the Maps object. If 'key' is not found, then 'default' is inserted at 'key' into the Maps object and then returns that value. Args: key: The key to return the value of. default (:obj:`obj`, optional): The value to insert if 'key' does not exist. Defaults to none. Returns: object: The object at 'key' in the Maps object, default' otherwise. """ return self._map.setdefault(key, default) def to_dict(self) -> Union[dict, NoReturn]: """Converts the :obj:`Maps` object to a stdlib dictionary. Returns: dict: The converted :obj:`Maps` object as a dictionary. """ new_dict = {} for key, value in self.items(): if issubclass(type(value), Maps): if id(value) == id(self): value = new_dict else: value = value.to_dict() elif isinstance(value, (tuple, list)): new_list = [] for item in value: temp_item = item if issubclass(type(item), Maps): temp_item = item.to_dict() new_list.append(temp_item) if isinstance(value, tuple): value = tuple(new_list) else: value = new_list new_dict[key] = value return new_dict def update(self, *args, **kwargs) -> None: """Adds or changes existing values using a dictionary or iterator of key:value pairs.""" if len(args) != 0: self._map.update(*args) self._map.update(kwargs) def values(self) -> Any: """Returns the values of the :obj:`Maps` object.""" return self._map.values() def viewitems(self) -> Any: """Returns a new view of the :obj:`Maps` object's items (key:value pairs).""" return self._map.viewitems() def viewkeys(self) -> Any: """Returns a new view of the :obj:`Maps` object's keys.""" return self._map.viewkeys() def viewvalues(self) -> Any: """Returns a new view of the :obj:`Maps` object's values.""" return self._map.viewvalues()
class SortedDotDict(object): def __init__(self, *args, **kwargs): super(SortedDotDict, self).__init__(*args, **kwargs) self._dict = SortedDict() def __contains__(self, *args, **kwargs): return self._dict.__contains__(*args, **kwargs) def __eq__(self, *args, **kwargs): return self._dict.__eq__(*args, **kwargs) def __format__(self, *args, **kwargs): return self._dict.__format__(*args, **kwargs) def __ge__(self, *args, **kwargs): return self._dict.__ge__(*args, **kwargs) def __getattr__(self, key): try: return self._dict[key] except: raise AttributeError(key) def __iter__(self): vals = list(self.values()) for k in vals: yield k def __getitem__(self, key): return self._dict[key] def __setitem__(self, key, value): self._dict[key] = value def __delitem__(self, key): del self._dict[key] def keys(self): return list(self._dict.keys()) def values(self): vals = list(self._dict.values()) vals = [v for v in vals if isinstance(v, (ConfigurationGroup, Value))] vals.sort() return vals def items(self): return list(self._dict.items()) def iterkeys(self): return iter(self._dict.keys()) def itervalues(self): return iter(self._dict.values()) def iteritems(self): return iter(self._dict.items()) def get(self, *args, **kwargs): return self._dict.get(*args, **kwargs) def clear(self): return self._dict.clear() def copy(self): s = SortedDotDict() s._dict = self._dict.copy() return s def fromkeys(self): return self._dict.fromkeys() def has_key(self, key): return key in self._dict def pop(self, *args, **kwargs): return self._dict.pop(*args, **kwargs) def popitem(self, *args, **kwargs): return self._dict.popitem(*args, **kwargs) def setdefault(self, key, default): return self._dict.setdefault(key, default) def update(self, d): return self._dict.update(d) def viewitems(self, *args, **kwargs): return self._dict.viewitems(*args, **kwargs) def viewvalues(self, *args, **kwargs): return self._dict.viewvalues(*args, **kwargs)
def __init__(self, vmid, timestamp): self.vm_id = vmid self.timestamp = timestamp replicas_dict = OrderedDict() ordered_replicas_dict = OrderedDict() for vm_id in range(21, 30): if vm_id % 3 == 0: replicas_dict[vm_id] = VM('a' + str(vm_id), -vm_id**vm_id) else: replicas_dict[vm_id] = VM('a' + str(vm_id), vm_id + vm_id) replicas_dict.popitem() print "Normal" for vm in replicas_dict.viewvalues(): print vm.timestamp ordered_replicas_dict = OrderedDict() for vm in (sorted(replicas_dict.values(), key=operator.attrgetter('timestamp'), reverse=True)): ordered_replicas_dict[vm.vm_id] = vm print "In order:" for vm in ordered_replicas_dict.viewvalues(): print vm.timestamp x = ordered_replicas_dict.popitem() print "pop:", x[1].timestamp
class PartiePT(Partie): __tablename__ = "partie_pestuse" __mapper_args__ = {'polymorphic_identity': 'pestuse'} partie_id = Column(Integer, ForeignKey('parties.id'), primary_key=True) repetitions = relationship('RepetitionsPT') def __init__(self, le2mserv, joueur): super(PartiePT, self).__init__("pestuse", "PT") self._le2mserv = le2mserv self.joueur = joueur self._texte_recapitulatif = u"" self._texte_final = u"" # self.PT_gain_ecus = 0 # self.PT_gain_euros = 0 self._histo_build = OrderedDict() self._histo_build[le2mtrans(u"Period")] = "PT_period" self._histo_build[le2mtrans(u"DecisionY")] = "PT_decisionY" self._histo_build[le2mtrans(u"DecisionZ")] = "PT_decisionZ" self._histo_build[le2mtrans(u"NbAteliersY")] = "PT_nbAteliersY" self._histo_build[le2mtrans(u"NbAteliersZ")] = "PT_nbAteliersZ" self._histo_build[le2mtrans(u"RendementY")] = "PT_rendementY" self._histo_build[le2mtrans(u"RendementZ")] = "PT_rendementZ" self._histo_build[le2mtrans(u"ProfitY")] = "PT_profitY" self._histo_build[le2mtrans(u"ProfitZ")] = "PT_profitZ" self._histo_build[le2mtrans(u"Tirage_de")] = "PT_tirage_de" self._histo_build[le2mtrans(u"GainY")] = "PT_gainY" self._histo_build[le2mtrans(u"GainZ")] = "PT_gainZ" self._histo_build[le2mtrans(u"Period\npayoff")] = "PT_periodpayoff" self._histo_build[le2mtrans(u"Cumulative\npayoff")] = "PT_cumulativepayoff" self._histo_content = [list(self._histo_build.viewkeys())] # self.periods = {} self._currentperiod = None @defer.inlineCallbacks def display_QC(self, type_partie): """ Display the comprehension questionnaire screen on the remote Get back the decision :return: """ if type_partie == "BEN": QC = list(pms.QCBEN) elif type_partie == "WEX": QC = list(pms.QCWEX) elif type_partie == "WEA": QC = list(pms.QCWEA) elif type_partie == "WIN": QC = list(pms.QCWIN) elif type_partie == "WEI": QC = list(pms.QCWEI) elif type_partie == "WIE": QC = list(pms.QCWIE) QC_NbQuest = len(QC) reponses_fausse = [] for i_QC_NbQuest in range(0, QC_NbQuest): logger.debug(u"{} Decision".format(self.joueur)) debut_QC = datetime.now() self.PT_decision_QC = yield (self.remote.callRemote( "display_QC", i_QC_NbQuest, type_partie)) self.PT_decisiontime_QC = (datetime.now() - debut_QC).seconds indice_bonne_reponse = QC[i_QC_NbQuest][1].index(QC[i_QC_NbQuest][2][0]) if self.PT_decision_QC != indice_bonne_reponse: reponses_fausse.append(i_QC_NbQuest) # self.joueur.info(u"{}".format(self.PT_decision_QC)) self.joueur.remove_waitmode() self.joueur.info(u"Faute(s) {}".format(reponses_fausse)) @property def currentperiod(self): return self._currentperiod @defer.inlineCallbacks def configure(self): logger.debug(u"{} Configure".format(self.joueur)) yield (self.remote.callRemote("configure", get_module_attributes(pms))) self.joueur.info(u"Ok") @defer.inlineCallbacks def newperiod(self, period): """ Create a new period and inform the remote If this is the first period then empty the historic :param periode: :return: """ logger.debug(u"{} New Period".format(self.joueur)) if period == 1: del self._histo_content[1:] self._currentperiod = RepetitionsPT(period) self._le2mserv.gestionnaire_base.ajouter(self.currentperiod) self.repetitions.append(self.currentperiod) yield (self.remote.callRemote("newperiod", period)) logger.info(u"{} Ready for period {}".format(self.joueur, period)) @defer.inlineCallbacks def display_decision(self, type_partie): """ Display the decision screen on the remote Get back the decision :return: """ logger.debug(u"{} Decision".format(self.joueur)) debut = datetime.now() les_decisions = yield (self.remote.callRemote( "display_decision", self._histo_content, type_partie)) self.currentperiod.PT_decisionY = les_decisions[0] self.currentperiod.PT_decisionZ = les_decisions[1] self.currentperiod.PT_nbAteliersY = les_decisions[2] self.currentperiod.PT_nbAteliersZ = 10 - les_decisions[2] self.currentperiod.PT_type_partie = type_partie self.currentperiod.PT_decisiontime = (datetime.now() - debut).seconds self.joueur.info(u"{} {} {} {}".format(self.currentperiod.PT_nbAteliersY, self.currentperiod.PT_nbAteliersZ, self.currentperiod.PT_decisionY, self.currentperiod.PT_decisionZ)) self.joueur.remove_waitmode() @defer.inlineCallbacks def lance_de(self, type_partie): """ Lancement du de :return: """ logger.debug(u"{} tirage du dé".format(self.joueur)) debut_de = datetime.now() self.currentperiod.PT_tirage_de = yield (self.remote.callRemote( "tirage_de", type_partie)) self.currentperiod.PT_decisiontime_de = (datetime.now() - debut_de).seconds self.joueur.info(u"{}".format(self.currentperiod.PT_tirage_de)) self.joueur.remove_waitmode() @defer.inlineCallbacks def affichage_result(self, type_partie, indice_part_pestuse, tirage_part_pestuse_gain, tirage_periode_pestuse_gain): """ Affichage des resultat de la periode :return: """ decision_pour_Y = self.currentperiod.PT_decisionY decision_pour_Z = self.currentperiod.PT_decisionZ nbAtelierY = self.currentperiod.PT_nbAteliersY nbAtelierZ = self.currentperiod.PT_nbAteliersZ tirage_du_de = self.currentperiod.PT_tirage_de logger.debug(u"{} Affichage des résultats de la période".format(self.joueur)) debut_ar = datetime.now() les_retours = yield (self.remote.callRemote( "affichage_result", decision_pour_Y, decision_pour_Z, tirage_du_de, type_partie, nbAtelierY, nbAtelierZ)) self.currentperiod.PT_rendementY = les_retours[0] self.currentperiod.PT_rendementZ = les_retours[1] self.currentperiod.PT_profitY = les_retours[2] self.currentperiod.PT_profitZ = les_retours[3] self.currentperiod.PT_tirage_de = les_retours[4] self.currentperiod.PT_gainY = les_retours[5] self.currentperiod.PT_gainZ = les_retours[6] # On met la valeur des gains si on est sur la partie pestuse tiree au sort et la bonne periode self.currentperiod.PT_periodpayoff = 0 # print "indice_part_pestuse = ", indice_part_pestuse # print "tirage_part_pestuse_gain = ", tirage_part_pestuse_gain # print "\n" # print "self.currentperiod.PT_period = ", self.currentperiod.PT_period # print "tirage_periode_pestuse_gain", tirage_periode_pestuse_gain if indice_part_pestuse == tirage_part_pestuse_gain and self.currentperiod.PT_period == tirage_periode_pestuse_gain: self.currentperiod.PT_periodpayoff = les_retours[5] + les_retours[6] self.PT_gain_ecus = les_retours[5] + les_retours[6] self.PT_gain_euros = \ float(self.PT_gain_ecus) * float(pms.TAUX_CONVERSION) # print "JE PASSE et self.currentperiod.PT_periodpayoff = ", self.currentperiod.PT_periodpayoff # print " Et self.PT_gain_ecus = ", self.PT_gain_ecus resu_payoff = self.currentperiod.PT_periodpayoff self.currentperiod.PT_indice_part_pestuse = indice_part_pestuse self.currentperiod.PT_tirage_part_pestuse_gain = tirage_part_pestuse_gain self.currentperiod.PT_tirage_periode_pestuse_gain = tirage_periode_pestuse_gain # On remplit l historique self._histo_content.append( [getattr(self.currentperiod, e) for e in self._histo_build.viewvalues()]) self.currentperiod.PT_decisiontime_ar = (datetime.now() - debut_ar).seconds self.joueur.info(u"{}".format(self.currentperiod.PT_tirage_ar)) self.joueur.remove_waitmode() yield resu_payoff def compute_periodpayoff(self): """ Compute the payoff for the period :return: """ logger.debug(u"{} Period Payoff".format(self.joueur)) # self.currentperiod.PT_periodpayoff = 0 # cumulative payoff since the first period if self.currentperiod.PT_period < 2: self.currentperiod.PT_cumulativepayoff = \ self.currentperiod.PT_periodpayoff else: previousperiod = self.periods[self.currentperiod.PT_period - 1] self.currentperiod.PT_cumulativepayoff = \ previousperiod.PT_cumulativepayoff + \ self.currentperiod.PT_periodpayoff # we store the period in the self.periodes dictionnary self.periods[self.currentperiod.PT_period] = self.currentperiod logger.debug(u"{} Period Payoff {}".format( self.joueur, self.currentperiod.PT_periodpayoff)) @defer.inlineCallbacks def display_summary(self, *args): """ Create the summary (txt and historic) and then display it on the remote :param args: :return: """ logger.debug(u"{} Summary".format(self.joueur)) self._texte_recapitulatif = texts.get_recapitulatif(self.currentperiod) self._histo_content.append( [getattr(self.currentperiod, e) for e in self._histo_build.viewvalues()]) yield (self.remote.callRemote( "display_summary", self._texte_recapitulatif, self._histo_content)) self.joueur.info("Ok") self.joueur.remove_waitmode() def compute_partpayoff(self, tirage_part_pestuse_gain, tirage_periode_pestuse_gain): """ Compute the payoff of the part :return: """ logger.debug(u"{} Part Payoff".format(self.joueur)) # gain partie self.PT_gain_ecus = self.currentperiod.PT_cumulativepayoff self.PT_gain_euros = \ float(self.PT_gain_ecus) * float(pms.TAUX_CONVERSION) # texte final self._texte_final = texts.get_texte_final( self.PT_gain_ecus, self.PT_gain_euros, tirage_part_pestuse_gain, tirage_periode_pestuse_gain ) logger.debug(u"{} Final text {}".format(self.joueur, self._texte_final)) logger.info(u'{} Payoff ecus {} Payoff euros {:.2f}'.format( self.joueur, self.PT_gain_ecus, self.PT_gain_euros))