Exemple #1
0
def main():
    global tmp_file, current_process, child_processes

    # https://stackoverflow.com/questions/25099895/
    signal.signal(signal.SIGTTOU, signal.SIG_IGN)

    # Ensure that child processes are cleaned up
    signal.signal(signal.SIGINT, kill_signal_handler)
    signal.signal(signal.SIGTERM, kill_signal_handler)
    atexit.register(cleanup_resources)

    tag_to_paths = load_tags()
    parser = argparse.ArgumentParser(prog='run',
                                     description=cmd_description,
                                     usage='run [options] [targets] command',
                                     formatter_class=HelpFormatter)
    parser.add_argument('-p',
                        '--parallel',
                        help='run the command in parallel',
                        action='store_true')
    parser.add_argument('-e',
                        '--exit-codes',
                        help='display the exit codes',
                        action='store_true')
    parser.add_argument('arguments',
                        type=str,
                        nargs=argparse.REMAINDER,
                        metavar='[targets]',
                        help='tags or paths to run the command for'
                        ).completer = ChoicesCompleter(tag_to_paths.keys())
    autocomplete(parser)
    parsed = parser.parse_args()

    # Separate the targets from the command
    index = 0
    targets = OrderedDict()
    while index < len(parsed.arguments):
        target = parsed.arguments[index]
        if target in tag_to_paths:
            for path in sorted(tag_to_paths[target].keys()):
                targets[path] = target
            index += 1
        elif target.startswith('@'):
            parser.error('unknown tag {}'.format(target))
        elif os.path.isdir(target):
            path = expand_path(target)
            if path not in targets:
                targets[path] = None
            index += 1
        else:
            break  # beginning of the command

    # Join the command arguments into a string
    command_arguments = parsed.arguments[index:]
    if len(command_arguments) == 0:
        command = None
    elif len(command_arguments) == 1:
        command = ' '.join(command_arguments)
    else:
        command = ' '.join("'{}'".format(arg) if ' ' in arg else arg
                           for arg in command_arguments)
    if not (targets and command):
        parser.error('too few arguments')

    if parsed.parallel:
        # Run the command in parallel
        for path, tag in targets.items():
            tmp_file = tempfile.TemporaryFile(mode='w+t')
            current_process = subprocess.Popen(
                '{} -i -c "{}"'.format(shell, command),
                cwd=path,
                shell=True,
                stdout=tmp_file,
                stderr=tmp_file,
                preexec_fn=os.setsid,
            )
            child_processes.append((path, tag, current_process, tmp_file))
        for path, tag, current_process, tmp_file in child_processes:
            exit_code = current_process.wait()
            tail = ' ({}{}{})'.format(PINK, tag, CLEAR) if tag else ':'
            print('{}>>> {}{}{}{}'.format(YELLOW, CYAN, path, tail, CLEAR))
            tmp_file.seek(0)
            lines = tmp_file.readlines()
            offset = 0
            if len(lines) > 0 and contains_ctrl_error_msg(lines[0]):
                offset += 1
            if len(lines) > 1 and contains_ctrl_error_msg(lines[1]):
                offset += 1
            sys.stdout.write(''.join(lines[offset:]))
            tmp_file.close()
            if parsed.exit_codes:
                print('{}>>> {}exit code: {}{}'.format(YELLOW, RED, exit_code,
                                                       CLEAR))
            sys.stdout.write('\n')
    else:
        # Run the command sequentially
        full_command = []
        for path, tag in targets.items():
            tag_info = ' ({}{}{})'.format(PINK, tag, CLEAR) if tag else ':'
            if parsed.exit_codes:
                tail = 'printf "{}>>> {}exit code: $?{}\n\n"'.format(
                    YELLOW, RED, CLEAR)
            else:
                tail = 'printf "\n"'
            full_command.append(
                '(printf "{header}"; cd {path} && {cmd};{tail})'.format(
                    header='{}>>> {}{}{}{}\n'.format(YELLOW, CYAN, path, CLEAR,
                                                     tag_info),
                    path=path,
                    cmd=command,
                    tail=tail))
        subprocess.call(
            [shell, '-i', '-c', '{}'.format(';'.join(full_command))],
            stderr=sys.stdout.fileno())
        # https://stackoverflow.com/questions/25099895/
        os.tcsetpgrp(0, os.getpgrp())
    sys.exit(0)
Exemple #2
0
def main():
    tag_to_paths = load_tags()
    path_to_tags = defaultdict(set)
    for tag, paths in tag_to_paths.items():
        for path in paths.keys():
            path_to_tags[path].add(tag)

    parser = ArgumentParser(prog='tags',
                            description=cmd_description,
                            usage='tags [options] [paths] [tags]',
                            formatter_class=HelpFormatter)
    parser.add_argument('-e',
                        '--edit',
                        action='store_true',
                        help='Edit the tags directly using an editor')
    parser.add_argument('-x',
                        '--expand',
                        action='store_true',
                        help='Expand the directory paths')
    parser.add_argument('-r',
                        '--reverse',
                        help='display the reverse mapping',
                        action='store_true')
    parser.add_argument('-j',
                        '--json',
                        help='display the raw JSON',
                        action='store_true')
    parser.add_argument('search_terms',
                        type=str,
                        nargs='*',
                        metavar='[paths] [tags]',
                        help='tag and directory paths to filter by'
                        ).completer = ChoicesCompleter(tag_to_paths.keys())
    autocomplete(parser)
    parsed = parser.parse_args()

    if parsed.edit:
        if parsed.search_terms:
            raise parser.error('no arguments allowed with option -e/--edit')
        edit_file_path = TAGS_FILE_PATH + '.edit'
        shutil.copy2(TAGS_FILE_PATH, edit_file_path)
        subprocess.call([os.environ.get('EDITOR', 'vi'), edit_file_path])
        new_tag_to_paths = save_error = None
        with open(edit_file_path, 'r') as edit_file:
            try:
                new_tag_to_paths = json.load(edit_file)
            except ValueError as err:
                save_error = 'Failed to save tags: {}'.format(msgify(err))
            else:
                try:
                    check_tags(new_tag_to_paths)
                except ValueError as err:
                    save_error = 'Failed to save tags: {}'.format(err)
        safe_remove(edit_file_path)
        if save_error is not None:
            halt(save_error)
        save_tags({
            tag: {expand_path(p): shrink_path(p)
                  for p in paths}
            for tag, paths in new_tag_to_paths.items()
        })
        print('New tags saved successfully')
        sys.exit(0)

    if len(tag_to_paths) == 0:
        print('No tags defined')
        sys.exit(0)

    # Filter by any given tags and paths
    # TODO optimize here if possible
    if not parsed.search_terms:
        if parsed.expand:
            filtered = {t: ps.keys() for t, ps in tag_to_paths.items()}
        else:
            filtered = {t: ps.values() for t, ps in tag_to_paths.items()}
    else:
        filtered = {}
        for term in parsed.search_terms:
            if term in tag_to_paths:
                if parsed.expand:
                    filtered[term] = tag_to_paths[term].keys()
                else:
                    filtered[term] = tag_to_paths[term].values()
            elif os.path.isdir(term):
                term = expand_path(term)
                if term in path_to_tags:
                    for tag in path_to_tags[term]:
                        if parsed.expand:
                            filtered[tag] = tag_to_paths[tag].keys()
                        else:
                            filtered[tag] = tag_to_paths[tag].values()

    if parsed.json:
        formatted = {tag: sorted(paths) for tag, paths in filtered.items()}
        print(json.dumps(formatted, sort_keys=True, indent=4))
    elif parsed.reverse:
        reverse = defaultdict(set)
        for tag, paths in filtered.items():
            for path in paths:
                reverse[path].add(tag)
        for path, tags in reverse.items():
            print('{}{}{}'.format(CYAN, path, CLEAR))
            print('{}{}{}\n'.format(PINK, ' '.join(sorted(tags)), CLEAR))
    else:
        for tag, paths in sorted(filtered.items()):
            print('{}{}{}'.format(PINK, tag, CLEAR))
            print('{}{}{}\n'.format(CYAN, '\n'.join(sorted(paths)), CLEAR))
Exemple #3
0
def main():
    tag_to_paths = load_tags()
    path_to_tags = defaultdict(set)
    for tag, paths in tag_to_paths.items():
        for path in paths.keys():
            path_to_tags[path].add(tag)

    parser = ArgumentParser(
        prog='tags',
        description=cmd_description,
        usage='tags [options] [paths] [tags]',
        formatter_class=HelpFormatter
    )
    parser.add_argument(
        '-e', '--edit',
        action='store_true',
        help='Edit the tags directly using an editor'
    )
    parser.add_argument(
        '-x', '--expand',
        action='store_true',
        help='Expand the directory paths'
    )
    parser.add_argument(
        '-r', '--reverse',
        help='display the reverse mapping',
        action='store_true'
    )
    parser.add_argument(
        '-j', '--json',
        help='display the raw JSON',
        action='store_true'
    )
    parser.add_argument(
        'search_terms',
        type=str,
        nargs='*',
        metavar='[paths] [tags]',
        help='tag and directory paths to filter by'
    ).completer = ChoicesCompleter(tag_to_paths.keys())
    autocomplete(parser)
    parsed = parser.parse_args()

    if parsed.edit:
        if parsed.search_terms:
            raise parser.error('no arguments allowed with option -e/--edit')
        edit_file_path = TAGS_FILE_PATH + '.edit'
        shutil.copy2(TAGS_FILE_PATH, edit_file_path)
        subprocess.call([os.environ.get('EDITOR', 'vi'), edit_file_path])
        new_tag_to_paths = save_error = None
        with open(edit_file_path, 'r') as edit_file:
            try:
                new_tag_to_paths = json.load(edit_file)
            except ValueError as err:
                save_error = 'Failed to save tags: {}'.format(msgify(err))
            else:
                try:
                    check_tags(new_tag_to_paths)
                except ValueError as err:
                    save_error = 'Failed to save tags: {}'.format(err)
        safe_remove(edit_file_path)
        if save_error is not None:
            halt(save_error)
        save_tags({
            tag: {expand_path(p): shrink_path(p) for p in paths}
            for tag, paths in new_tag_to_paths.items()
        })
        print('New tags saved successfully')
        sys.exit(0)

    if len(tag_to_paths) == 0:
        print('No tags defined')
        sys.exit(0)

    # Filter by any given tags and paths
    # TODO optimize here if possible
    if not parsed.search_terms:
        if parsed.expand:
            filtered = {t: ps.keys() for t, ps in tag_to_paths.items()}
        else:
            filtered = {t: ps.values() for t, ps in tag_to_paths.items()}
    else:
        filtered = {}
        for term in parsed.search_terms:
            if term in tag_to_paths:
                if parsed.expand:
                    filtered[term] = tag_to_paths[term].keys()
                else:
                    filtered[term] = tag_to_paths[term].values()
            elif os.path.isdir(term):
                term = expand_path(term)
                if term in path_to_tags:
                    for tag in path_to_tags[term]:
                        if parsed.expand:
                            filtered[tag] = tag_to_paths[tag].keys()
                        else:
                            filtered[tag] = tag_to_paths[tag].values()

    if parsed.json:
        formatted = {tag: sorted(paths) for tag, paths in filtered.items()}
        print(json.dumps(formatted, sort_keys=True, indent=4))
    elif parsed.reverse:
        reverse = defaultdict(set)
        for tag, paths in filtered.items():
            for path in paths:
                reverse[path].add(tag)
        for path, tags in reverse.items():
            print('{}{}{}'.format(CYAN, path, CLEAR))
            print('{}{}{}\n'.format(PINK, ' '.join(sorted(tags)), CLEAR))
    else:
        for tag, paths in sorted(filtered.items()):
            print('{}{}{}'.format(PINK, tag, CLEAR))
            print('{}{}{}\n'.format(CYAN, '\n'.join(sorted(paths)), CLEAR))
Exemple #4
0
def main():
    global tmp_file, current_process, child_processes

    # https://stackoverflow.com/questions/25099895/
    signal.signal(signal.SIGTTOU, signal.SIG_IGN)

    # Ensure that child processes are cleaned up
    signal.signal(signal.SIGINT, kill_signal_handler)
    signal.signal(signal.SIGTERM, kill_signal_handler)
    atexit.register(cleanup_resources)

    tag_to_paths = load_tags()
    parser = argparse.ArgumentParser(
        prog='run',
        description=cmd_description,
        usage='run [options] [targets] command',
        formatter_class=HelpFormatter
    )
    parser.add_argument(
        '-p', '--parallel',
        help='run the command in parallel',
        action='store_true'
    )
    parser.add_argument(
        '-e', '--exit-codes',
        help='display the exit codes',
        action='store_true'
    )
    parser.add_argument(
        'arguments',
        type=str,
        nargs=argparse.REMAINDER,
        metavar='[targets]',
        help='tags or paths to run the command for'
    ).completer = ChoicesCompleter(tag_to_paths.keys())
    autocomplete(parser)
    parsed = parser.parse_args()

    # Separate the targets from the command
    index = 0
    targets = OrderedDict()
    while index < len(parsed.arguments):
        target = parsed.arguments[index]
        if target in tag_to_paths:
            for path in sorted(tag_to_paths[target].keys()):
                targets[path] = target
            index += 1
        elif target.startswith('@'):
            parser.error('unknown tag {}'.format(target))
        elif os.path.isdir(target):
            path = expand_path(target)
            if path not in targets:
                targets[path] = None
            index += 1
        else:
            break  # beginning of the command

    # Join the command arguments into a string
    command_arguments = parsed.arguments[index:]
    if len(command_arguments) == 0:
        command = None
    elif len(command_arguments) == 1:
        command = ' '.join(command_arguments)
    else:
        command = ' '.join(
            "'{}'".format(arg) if ' ' in arg else arg
            for arg in command_arguments
        )
    if not (targets and command):
        parser.error('too few arguments')

    if parsed.parallel:
        # Run the command in parallel
        for path, tag in targets.items():
            tmp_file = tempfile.TemporaryFile(mode='w+t')
            current_process = subprocess.Popen(
                '{} -i -c "{}"'.format(shell, command),
                cwd=path,
                shell=True,
                stdout=tmp_file,
                stderr=tmp_file,
                preexec_fn=os.setsid,
            )
            child_processes.append((path, tag, current_process, tmp_file))
        for path, tag, current_process, tmp_file in child_processes:
            exit_code = current_process.wait()
            tail = ' ({}{}{})'.format(PINK, tag, CLEAR) if tag else ':'
            print('{}>>> {}{}{}{}'.format(
                YELLOW, CYAN, path, tail, CLEAR
            ))
            tmp_file.seek(0)
            lines = tmp_file.readlines()
            offset = 0
            if len(lines) > 0 and contains_ctrl_error_msg(lines[0]):
                offset += 1
            if len(lines) > 1 and contains_ctrl_error_msg(lines[1]):
                offset += 1
            sys.stdout.write(''.join(lines[offset:]))
            tmp_file.close()
            if parsed.exit_codes:
                print('{}>>> {}exit code: {}{}'.format(
                    YELLOW, RED, exit_code, CLEAR
                ))
            sys.stdout.write('\n')
    else:
        # Run the command sequentially
        full_command = []
        for path, tag in targets.items():
            tag_info = ' ({}{}{})'.format(PINK, tag, CLEAR) if tag else ':'
            if parsed.exit_codes:
                tail = 'printf "{}>>> {}exit code: $?{}\n\n"'.format(
                    YELLOW, RED, CLEAR
                )
            else:
                tail = 'printf "\n"'
            full_command.append(
                '(printf "{header}"; cd {path} && {cmd};{tail})'.format(
                    header='{}>>> {}{}{}{}\n'.format(
                        YELLOW, CYAN, path, CLEAR, tag_info
                    ),
                    path=path,
                    cmd=command,
                    tail=tail
                )
            )
        subprocess.call(
            [shell, '-i', '-c', '{}'.format(';'.join(full_command))],
            stderr=sys.stdout.fileno()
        )
        # https://stackoverflow.com/questions/25099895/
        os.tcsetpgrp(0, os.getpgrp())
    sys.exit(0)