class CoreCommand: "Base command" _events = {} _entries = {} _native_pool = None _progress_counter = itertools.count(1) _progresses = LRUCache(1000 * 1000) def __new__(cls, *args, **kwargs): obj = super(CoreCommand, cls).__new__(cls) obj._get_commands() return obj def __init__(self, priority=constants.Priority.Normal): self._created_time = arrow.now() self.command_id = None self._started_time = None self._finished_time = None self._priority = priority self._futures = [] self._progress_max = None self._progress_current = None self._progress_text = None self._progress_count = None self._progress_type = None self._progress_tree = None self._progress_time = None self._progress_timestamp = 0 self._progress_title = self.__class__.__name__ def _run(self, *args, **kwargs): """ Run the command with *args and **kwargs. """ log.d("Running command:", self.__class__.__name__) r = self.main(*args, **kwargs) log.d("Finished running command:", self.__class__.__name__) return r @classmethod def get_all_progress(cls): ps = [] for c, t in cls._progresses.items(): x = t.get_node(t.root).data() if x: ps.insert(0, x.get_progress()) return ps def _add_progress(self, add=True): if not self._progress_count: self._progress_count = next(self._progress_counter) if self._progress_tree is None and self._progress_count not in self._progresses: self._progress_tree = Tree() if add: self._progresses[self._progress_count] = self._progress_tree self._progress_tree.create_node(self._progress_count, self._progress_count, data=weakref.ref(self)) self._progress_timestamp = arrow.now().timestamp self._progress_time = arrow.now() def merge(self, cmd): """ Merge this command into given command """ assert cmd is None or isinstance(cmd, CoreCommand) if cmd: self.merge_progress_into(cmd) return self def merge_progress_into(self, cmd): assert isinstance(cmd, CoreCommand) cmd._add_progress() self._add_progress(False) cmd._progress_tree.paste(cmd._progress_count, self._progress_tree) self._progress_tree = cmd._progress_tree if self._progress_count in self._progresses: del self._progresses[self._progress_count] def _str_progress_tree(self): self._tree_reader = "" def w(l): self._tree_reader = l.decode('utf-8') + '\n' try: self._progress_tree._Tree__print_backend(func=w) except tree_exceptions.NodeIDAbsentError: self._tree_reader = "Tree is empty" return self._tree_reader def get_progress(self): if self._progress_tree: log.d("Command", self, "progress tree:\n{}".format(self._str_progress_tree())) p = { 'title': self._progress_title, 'subtitle': '', 'subtype': None, 'text': '', 'value': .0, 'percent': .0, 'max': .0, 'type': self._progress_type, 'state': self.state.value if hasattr(self, "state") else None, 'timestamp': self._progress_timestamp } t = self._progress_tree.subtree(self._progress_count) prog_time = self._progress_time prog_text = self._progress_text if self._progress_text else '' prog_subtitle = '' prog_subtype = None for _, n in t.nodes.items(): cmd = n.data() if cmd: if cmd._progress_max: p['max'] += cmd._progress_max if cmd._progress_current: p['value'] += cmd._progress_current if not prog_time or (cmd._progress_time and cmd._progress_time > prog_time): prog_text = cmd._progress_text prog_subtitle = cmd._progress_title prog_subtype = cmd._progress_type if p['max']: p['percent'] = (100 / p['max']) * p['value'] else: p['percent'] = -1.0 p['text'] = prog_text p['subtitle'] = prog_subtitle p['subtype'] = prog_subtype return p return None def set_progress(self, value=None, text=None, title=None, type_=None): assert value is None or isinstance(value, (int, float)) assert text is None or isinstance(text, str) assert title is None or isinstance(text, str) self._add_progress() if title is not None: self._progress_title = title if value is not None: self._progress_current = value if text is not None: self._progress_text = text if type_ is not None: self._progress_type = type_ def set_max_progress(self, value, add=False): assert isinstance(value, (int, float)) self._add_progress() if add: if self._progress_max is None: self._progress_max = 0 self._progress_max += value else: self._progress_max = value def next_progress(self, add=1, text=None, _from=0): assert isinstance(add, (int, float)) if self._progress_current is None: self._progress_current = _from if text is not None: self._progress_text = text self._progress_current += add utils.switch(self._priority) @contextmanager def progress(self, max_progress=None, text=None): if max_progress is not None: self.set_max_progress(max_progress) yield if max_progress is not None: self.set_progress(max_progress, text) def run_native(self, f, *args, **kwargs): f = async_utils.AsyncFuture( self, self._native_pool.apply_async(_native_runner(f), args, kwargs)) self._futures.append(f) return f def push(self, msg, scope=None): if constants.notification: return constants.notification.push(msg, scope=scope) # TODO: raise error perhaps? def kill(self): [f.kill() for f in self._futures] def _log_stats(self, d=None): create_delta = self._finished_time - self._created_time run_delta = self._finished_time - self._started_time log_delta = (d - self._finished_time) if d else None log.i( "Command - '{}' -".format(self.__class__.__name__), "ID({})".format(self.command_id) if self.command_id else '', "running time:\n", "\t\tCreation delta: {} (time between creation and finish)\n". format(create_delta), "\t\tRunning delta: {} (time between start and finish)\n".format( run_delta), "\t\tLog delta: {} (time between finish and this log)\n".format( log_delta), ) def __del__(self): if hasattr(self, '_progress_count') and hasattr(self, '_progresses'): if self._progress_count and self._progress_count in self._progresses: del self._progresses[self._progress_count] @classmethod def _get_commands(cls, self=None): "" if self is not None: cls = self events = {} entries = {} for a in cls.__dict__.values(): if isinstance(a, CommandEvent): a.command_cls = cls events[a.name] = a a._init() if isinstance(a, CommandEntry): a.command_cls = cls entries[a.name] = a a._init() cls._entries = entries cls._events = events return entries, events