def sed(cfg: Dict[str, str], sed_cmd: str) -> NoReturn: """ Runs an in-place "sed" edit against every notebook in the course, using "sed -E". Requires a version of "sed" that supports the "-i" (in-place edit) option. :param cfg: the loaded configuration :param sed_cmd: the "sed" command, which may or may not be quoted. :return: Nothing """ check_config(cfg, 'COURSE_NAME', 'COURSE_REPO') for nb in bdc.bdc_get_notebook_paths(build_file_path(cfg)): # Quote the argument. q = sed_cmd[0] if q in ('"', "'"): # Already quoted, hopefully. if sed_cmd[-1] != q: raise CourseError( f'Mismatched quotes in sed argument: {sed_cmd}') quoted = sed_cmd elif ('"' in sed_cmd) and ("'" in sed_cmd): raise CourseError( '"sed" argument cannot be quoted, since it contains ' + f'single AND double quotes: {sed_cmd}') elif "'" in sed_cmd: quoted = '"' + sed_cmd + '"' else: quoted = "'" + sed_cmd + "'" cmd(f'sed -E -i "" -e {quoted} "{nb}"')
def grep(cfg: Dict[str, str], pattern: str, case_blind: bool = False) -> NoReturn: """ Searches for the specified regular expression in every notebook within the current course, printing the colorized matches to standard output. If PAGER is set, the matches will be piped through the pager. Note that this function does NOT use grep(1). It implements the regular expression matching and colorization entirely within Python. :param cfg: The config. :param pattern: The regular expression (a string, not a compiled pattern) to find :param case_blind: Whether or not to use case-blind matching :return: Nothing """ def grep_one(path: str, r: Pattern, out: TextIO) -> NoReturn: home = os.environ['HOME'] if home: printable_path = os.path.join('~', path[len(home) + 1:]) else: printable_path = path matches = [] with open(path) as f: for line in f.readlines(): m = r.search(line) if not m: continue # If there's a pager, colorize the match. if cfg.get('PAGER'): s = m.start() e = m.end() matches.append(line[:s] + colored(line[s:e], 'red', attrs=['bold']) + line[e:]) else: matches.append(line) if matches: out.write(f'\n\n=== {printable_path}\n\n') out.write(''.join(matches)) r = None try: flags = 0 if not case_blind else re.IGNORECASE r = re.compile(pattern, flags=flags) except Exception as e: die(f'Cannot compile regular expression "{pattern}": {e}') check_config(cfg, 'COURSE_NAME', 'COURSE_REPO') with pager(cfg) as out: for nb in bdc.bdc_get_notebook_paths(build_file_path(cfg)): grep_one(nb, r, out)
def run_command_on_notebooks(cfg: Dict[str, str], command: str, args: Sequence[str]) -> NoReturn: """ Runs a command on every notebook in the current course. :param cfg: the loaded configuration. :param command: the command to run :param args: any command arguments, as a list :return: Nothing """ check_config(cfg, 'COURSE_NAME', 'COURSE_REPO') for nb in bdc.bdc_get_notebook_paths(build_file_path(cfg)): if args: quoted = ' '.join([quote_shell_arg(arg) for arg in args]) shell_command = f'{command} {quoted} {nb}' else: shell_command = f'{command} {nb}' try: cmd(shell_command) except CourseError as e: warn(str(e))