def test_sanity(self): unicode_ = 0x1F352 # instance instance_ = Unicode.parse(unicode_) # expected test_ = CjTest(instance_) test_.add_check(test_.name == "CHERRIES", test_.value == '\U0001F352', test_.decimal == 127826, test_.bin == "0b11111001101010010", test_.hex == "0x1f352", test_.oct == '0o371522') test_.check_all()
class _StateTime(State): __clock = Unicode("\U0001F55C") __max_sequence = 12 size = 11 def display(self, current_value: Number, max_value: Number, current_percent: Number, time_it: Number, n_times: int) -> str: time_estimate = estimate(current_value, max_value, time_it) idx = int(proportional(int(current_percent), self.__max_sequence)) t_format = f"{time_format(time_estimate)}" value = f"{self.__clock + idx} {t_format}" return f"{value}{self.blanks(len(value))} estimated" def done(self, current_value: Number, max_value: Number, current_percent: Number, time_it: Number, n_times: float) -> str: return f"{self.__clock} {time_format(time_it)} total"
class ProgressBase: __awaiting_state = _StateAwaiting() __done_unicode = Unicode("\U00002705") __err_unicode = Unicode("\U0000274C") __key_map = { "loading": _StateLoading, "time": _StateTime, "percent": _StatePercent, "bar": _StateBar } def __init__(self, max_value: int = 100, states=None): self._builded = False self.n_times = 0 self.__name = self.set_name("Cereja Progress Tool") self.__task_count = 0 self.started = False self._awaiting_update = True self._show = False self.console = console self.started_time = None self._states = () self.add_state(states) self.max_value = max_value self._current_value = 0 self.th_root = self._create_progress_service() self._with_context = False self._was_done = False self._err = False self._builded = True @property def name(self): return self.__name def set_name(self, value: str): if not isinstance(value, str): raise TypeError("Please send string.") self.__name = value def _create_progress_service(self): return threading.Thread(name="awaiting", target=self._progress_service) def __repr__(self): state, _ = self._states_view(self.max_value) progress_example_view = f"{state}" state_conf = f"{self.__class__.__name__}{self._parse_states()}" return f"{state_conf}\n{self.console.parse(progress_example_view, title='Example States View')}" def hook_error(self, *args, **kwargs): self._err = True if not self._with_context: self.stop() def _parse_states(self): return tuple(map(lambda stt: stt.__class__.__name__, self._states)) def _get_done_state(self, **kwargs): result = list(map(lambda state: state.done(**kwargs), self._states)) done_msg = f"Done! {self.__done_unicode}" done_msg = self.console.format(done_msg, 'green') result.append(done_msg) return result def _get_error_state(self): result, _ = self._states_view(self._current_value) error_msg = f"Error! {self.__err_unicode}" error_msg = self.console.format(error_msg, 'red') return f'{result} {error_msg}' def _get_state(self, **kwargs): return list(map(lambda state: state.display(**kwargs), self._states)) @property def time_it(self): return time.time() - (self.started_time or time.time()) def _states_view(self, for_value: Number) -> Tuple[str, bool]: """ Return current state and bool bool: :param for_value: :return: current state and bool if is done else False """ self.n_times += 1 kwargs = { "current_value": for_value, "max_value": self.max_value, "current_percent": self.percent_(for_value), "time_it": self.time_it, "n_times": self.n_times } if for_value >= self.max_value: return ' - '.join(self._get_done_state(**kwargs)), True return ' - '.join(self._get_state(**kwargs)), False def add_state(self, state: Union[State, Sequence[State]], idx=-1): self._filter_and_add_state(state, idx) def remove_state(self, idx): states = list(self._states) states.pop(idx) self._states = tuple(states) def _valid_states(self, states: Union[State, Sequence[State]]): if states is not None: if not is_iterable(states): states = (states, ) return tuple( map(lambda st: st if isinstance(st, State) else st(), states)) return None, def _filter_and_add_state(self, state: Union[State, Sequence[State]], index_=-1): state = self._valid_states(state) filtered = tuple( filter(lambda stt: stt not in self._states, tuple(state))) if any(filtered): if index_ == -1: self._states += filtered else: states = list(self._states) for idx, new_state in enumerate(filtered): states.insert(index_ + idx, new_state) self._states = tuple(states) if self._builded: self.console.log(f"Added new states! {filtered}") @property def states(self): return self._parse_states() def percent_(self, for_value: Number) -> Number: return percent(for_value, self.max_value) def update_max_value(self, max_value: int): """ You can modify the progress to another value. It is not a percentage, although it is purposely set to 100 by default. :param max_value: This number represents the maximum amount you want to achieve. """ if not isinstance(max_value, (int, float, complex)): raise Exception(f"Current value {max_value} isn't valid.") if max_value != self.max_value: self.max_value = max_value def _progress_service(self): last_value = self._current_value n_times = 0 while self.started: if (self._awaiting_update and self._current_value != last_value) or not self._show: n_times += 1 self.console.replace_last_msg( self.__awaiting_state.display(0, 0, 0, 0, n_times=n_times)) time.sleep(0.5) if not self._awaiting_update or self._show: self._show_progress(self._current_value) last_value = self._current_value time.sleep(0.01) if not self._was_done: self._show_progress(self.max_value) def _show_progress(self, for_value=None): self._awaiting_update = False build_progress, is_done = self._states_view(for_value) end = '\n' if is_done else None if self._err: self.console.replace_last_msg((self._get_error_state())) if not self._was_done and not self._err: self.console.replace_last_msg(build_progress, end=end) if is_done: self._show = False self._awaiting_update = True self._was_done = True else: self._was_done = False def _update_value(self, value): self._awaiting_update = False self._show = True self._current_value = value def show_progress(self, for_value, max_value=None): """ Build progress by a value. :param for_value: Fraction of the "max_value" you want to achieve. Remember that this value is not necessarily the percentage. It is totally dependent on the "max_value" :param max_value: This number represents the maximum amount you want to achieve. It is not a percentage, although it is purposely set to 100 by default. """ if max_value is not None: self.update_max_value(max_value) self._update_value(for_value) def _reset(self): self._current_value = 0 self.n_times = 0 self.started_time = None def start(self): if self.started: return self.started_time = time.time() self.started = True try: self.th_root.start() except: self.th_root.join() self.th_root = self._create_progress_service() self.th_root.start() self.console.persist_on_runtime() self.n_times = 0 def stop(self): if self.started: self._awaiting_update = False self.started = False self.th_root.join() self.console.disable() def restart(self): self._reset() def __len__(self): return len(self._states) def __getitem__(self, slice_): if isinstance(slice_, tuple): if max(slice_) > len(self): raise IndexError(f"{max(slice_)} isn't in progress") return tuple(self._states[idx] for idx in slice_ if idx < len(self)) if isinstance(slice_, str): key = self.__key_map[slice_] if key in self._states: slice_ = self._states.index(key) else: raise KeyError(f"Not exists {key}") return self._states[slice_] def __call__(self, sequence: Sequence, task_name=None) -> "ProgressBase": if not is_iterable(sequence): raise ValueError("Send a sequence.") self.update_max_value(len(sequence)) self.sequence = sequence if task_name is not None: self.console.set_prefix(f"{self.name}({task_name})") if self._with_context and task_name is None: self.console.set_prefix(f"{self.name}(iter-{self.__task_count})") self.started_time = time.time() self.__task_count += 1 return self def __next__(self): if not self._with_context: self.start() for n, obj in enumerate(self.sequence): self._update_value(n + 1) yield obj if not self._with_context: self.stop() self.console.set_prefix(self.name) self.sequence = () def __iter__(self): return self.__next__() def __setitem__(self, key, value): value = self._valid_states(value)[0] if isinstance(value, State): value = value if isinstance(key, int): states_ = list(self._states) states_[key] = value self._states = tuple(states_) else: raise ValueError("Please send State object") def __enter__(self, *args, **kwargs): if hasattr(self, 'sequence'): raise ChildProcessError( "Dont use progress instance on with statement.") self._with_context = True self.start() return self def __exit__(self, exc_type, exc_val, exc_tb): if isinstance(exc_val, Exception) and not isinstance( exc_val, DeprecationWarning): self.console.error( f'{os.path.basename(exc_tb.tb_frame.f_code.co_filename)}:{exc_tb.tb_lineno}: {exc_val}' ) self.stop() self.__task_count = 0 self._with_context = False