def prompt_user(question, options, default=""): options = options.lower() default = default.lower() assert len(default) < 2 and default in options if "?" not in options: options += "?" prompt_options = ",".join(o.upper() if o == default else o for o in options) prompt = "{} [{}]? ".format(question, prompt_options) result = "" while True: result = input(prompt).strip().lower() if result == "?": for option in PROMPT_HELP: click.secho("{} - {}".format(option, PROMPT_HELP[option]), fg="red", bold=True) elif len(result) == 1 and result in options: return result elif result: click.echo('invalid response "{}"'.format(result)) elif default: return default
def main(): parser = argparse.ArgumentParser() parser.add_argument("--log-level", dest="log_level", type=str, choices=["DEBUG", "INFO", "WARNING", "ERROR"], help="set log level, default is INFO") parser.add_argument("--no-log-file", dest="no_log_file", action='store_true', default=False, help="don't log to file") parser.add_argument("--log-filepath", dest="log_filepath", type=str, help='set log file path, default is "report.log"') parser.add_argument("--inpath", required=True, type=str, help='the file or directory path you want to upgrade.') parser.add_argument("--backup", type=str, nargs='?', default=None, const=None, help='backup directory, default is the "~/.paddle1to2/".') parser.add_argument("--write", action='store_true', default=False, help='modify files in-place.') parser.add_argument("--no-confirm", dest="no_confirm", action='store_true', default=False, help='write files in-place without confirm, ignored without --write.') parser.add_argument("--refactor", action='append', choices=refactor.__all__, help='this is a debug option. Specify refactor you want to run. If none, all refactors will be run.') parser.add_argument("--print-match", action='store_true', default=False, help='this is a debug option. Print matched code and node for each file.') args = parser.parse_args() if args.refactor: args.refactor = set(args.refactor) if args.backup is None: home = os.path.expanduser('~') args.backup = os.path.join(home, '.paddle1to2') else: args.backup = os.path.expanduser(args.backup) if args.log_level: logger.setLevel(args.log_level) if not args.no_log_file: log_to_file(args.log_filepath) if not should_convert(args.inpath): logger.error("convert abort!") sys.exit(1) # refactor code via "Query" step by step. q = Query(args.inpath) for fn in refactor.__all__: refactor_func = getattr(refactor, fn) if args.refactor and fn not in args.refactor: continue assert callable(refactor_func), "{} is not callable.".format(fn) logger.debug("run refactor: {}".format(fn)) if args.print_match: refactor_func(q, change_spec).filter(filters.print_match) else: refactor_func(q, change_spec) if args.write: # backup args.inpath backup = backup_inpath(args.inpath, args.backup) # print diff to stdout, and modify file in place. if utils.is_windows(): q.execute(write=True, silent=False, need_confirm=not args.no_confirm, backup=backup, in_process=True) else: q.execute(write=True, silent=False, need_confirm=not args.no_confirm, backup=backup) else: # print diff to stdout if utils.is_windows(): q.execute(write=False, silent=False, in_process=True) else: q.execute(write=False, silent=False) click.secho('Refactor finished without touching source files, add "--write" to modify source files in-place if everything is ok.', fg="red", bold=True)
def print_hunks(self, filename, hunks): auto_yes = False result = "" # print same filename header only once. hunks_header = set() for hunk in hunks: header = "{} {}".format(hunk[0], hunk[1]) if self.hunk_processor(filename, hunk) is False: continue if not self.silent: # print header, e.g. # --- ./model.py # +++ ./model.py if header not in hunks_header: for line in hunk[:2]: if line.startswith("---"): click.secho(line, fg="red", bold=True) elif line.startswith("+++"): click.secho(line, fg="green", bold=True) hunks_header.add(header) # print diff content for line in hunk[2:]: if line.startswith("-"): click.secho(line, fg="red") elif line.startswith("+"): click.secho(line, fg="green") else: click.echo(line)
def print_tree( node, results=None, filename=None, indent=0, recurse=-1, ): filename = filename or Filename("") tab = INDENT_STR * indent if filename and indent == 0: click.secho(filename, fg="red", bold=True) if isinstance(node, Leaf): click.echo( click.style(tab, fg="black", bold=True) + click.style( "[{}] {} {}".format(tok_name[node.type], repr(node.prefix), repr(node.value)), fg="yellow", )) else: click.echo( click.style(tab, fg="black", bold=True) + click.style( "[{}] {}".format(type_repr(node.type), repr(node.prefix)), fg="blue")) if node.children: if recurse: for child in node.children: # N.b. do not pass results here since we print them once # at the end. print_tree(child, indent=indent + 1, recurse=recurse - 1) else: click.echo(INDENT_STR * (indent + 1) + "...") if results is None: return for key in results: if key == "node": continue value = results[key] if isinstance(value, (Leaf, Node)): click.secho("results[{}] =".format(repr(key)), fg="red") print_tree(value, indent=1, recurse=1) else: # TODO: Improve display of multi-match here, see # test_print_tree_captures test. click.secho("results[{}] = {}".format(repr(key), value), fg="red")
def refactor(self, items, *a, **k): """Refactor a list of files and directories.""" for dir_or_file in sorted(items): if os.path.isdir(dir_or_file): self.refactor_dir(dir_or_file) else: self.queue_work(Filename(dir_or_file)) children = [] if self.in_process: self.queue.put(None) self.refactor_queue() else: child_count = max(1, min(self.NUM_PROCESSES, self.queue_count)) self.log_debug("starting {} processes".format(child_count)) for i in range(child_count): child = multiprocessing.Process(target=self.refactor_queue) child.start() children.append(child) self.queue.put(None) results_count = 0 while True: try: filename, hunks, exc, new_text = self.results.get_nowait() results_count += 1 if exc: self.log_error("{}: {}".format(type(exc).__name__, exc)) if exc.__cause__: self.log_error(" {}: {}".format( type(exc.__cause__).__name__, exc.__cause__)) if isinstance(exc, BowlerException) and exc.hunks: diff = "\n".join("\n".join(hunk) for hunk in exc.hunks) self.log_error("Generated transform:\n{}".format(diff)) self.exceptions.append(exc) else: self.log_debug("results: got {} hunks for {}".format( len(hunks), filename)) self.print_hunks(filename, hunks) if hunks and self.write: if self.need_confirm: if click.confirm( click.style( '"{}" will be modified in-place, and it has been backed up to "{}". Do you want to continue?' .format(filename, self.backup), fg='red', bold=True)): self.write_result(filename, new_text) if self.print_hint: click.secho( '"{}" refactor done! Recover your files from "{}" if anything is wrong.' .format(filename, self.backup)) else: if self.print_hint: click.secho( '"{}" refactor cancelled!'.format( filename), fg='red', bold=True) else: self.write_result(filename, new_text) if self.print_hint: click.secho( '"{}" refactor done! Recover your files from "{}" if anything is wrong.' .format(filename, self.backup)) if self.need_confirm: self.semaphore_confirm.release() except Empty: if self.queue.empty() and results_count == self.queue_count: break elif not self.in_process and not any(child.is_alive() for child in children): self.log_debug( "child processes stopped without consuming work") break else: time.sleep(0.05) except BowlerQuit: for child in children: child.terminate() break self.log_debug("all children stopped and all diff hunks processed")