Example #1
0
def cli(prefix: str, paths: List[PurePath], commands: List[str],
        flags: List[str], logger: UI, descriptions: List[str],
        transaction: Transaction, *args, **kwargs):
    flags = list(map(parse_flag, flags))
    n = len(commands)
    if not len(paths) in [1, n]:
        logger.exit('There must either be 1 or n paths '
                    'where n is the number of commands.')

    if not len(descriptions) in [0, 1, n]:
        logger.exit('There must either be 1 or n descriptions '
                    'where n is the number of commands.')

    iterator = enumerate(itertools.zip_longest(paths, commands, descriptions))
    for i, (path, command, description) in iterator:
        if path is None:
            if n == 1:
                path = PurePath(paths[0])
            else:
                path = PurePath(paths[0], str(i))
        if len(descriptions) == 0:
            description = 'Description not given.'
        if len(descriptions) == 1:
            description = descriptions[0]

        new(path=path,
            prefix=prefix,
            command=command,
            description=description,
            flags=flags,
            transaction=transaction)
Example #2
0
def cli(prefix: str, paths: List[PurePath], commands: List[str],
        args: List[str], logger: UI, descriptions: List[str],
        transaction: Transaction, *_, **__):
    n = len(commands)
    if not len(paths) in [1, n]:
        logger.exit("There must either be 1 or n paths "
                    "where n is the number of subcommands.")

    if not (descriptions is None or len(descriptions) in [0, 1, n]):
        logger.exit("There must either be 1 or n descriptions "
                    "where n is the number of subcommands.")
    descriptions = descriptions or []
    iterator = enumerate(itertools.zip_longest(paths, commands, descriptions))
    for i, (path, command, description) in iterator:
        if path is None:
            if n == 1:
                path = PurePath(paths[0])
            else:
                path = PurePath(paths[0], str(i))
        if description is None:
            if descriptions:
                description = descriptions[0]

        new(
            command=Command(prefix, command, *args, path=path),
            description=description,
            path=path,
            transaction=transaction,
        )
Example #3
0
def build_tree(paths, depth: int = None):
    aggregator = defaultdict(list)
    for path in paths:
        try:
            head, *tail = PurePath(path).parts
        except ValueError:
            return dict()
        if tail:
            head += '/'
        aggregator[head].append(PurePath(*tail))

    return {k: build_tree(v, depth=depth) for k, v in aggregator.items()}
Example #4
0
    def get(
        self,
        patterns: Iterable[PurePath],
        unless: Iterable[PurePath] = None,
        order: bool = None,
        descendants: bool = False,
        active: bool = False,
        since: datetime = None,
        last: timedelta = None,
    ) -> List[RunEntry]:

        if descendants:
            patterns = list(map(str, patterns))
            patterns += [f'{str(pattern).rstrip("/%")}/%' for pattern in patterns]
        condition = DataBase.pattern_match(*patterns)
        if since or last:
            if since:
                time = since
            if last:
                time = datetime.now() - last
            if since and last:
                time = max(datetime.now() - last, since)
            condition = condition & GreaterThan("datetime", time)
        if active:
            condition = condition & In("path", *TMUXSession.active_runs(self.logger))
        if unless:
            unless = DataBase.pattern_match(*unless)

        return [
            RunEntry(PurePath(p), *e)
            for (p, *e) in self.select(
                condition=condition, unless=unless, order=order
            ).fetchall()
        ]
Example #5
0
def paths(runs: List[RunEntry],
          pprint: bool = True,
          depth: int = None) -> List[str]:
    _paths = [PurePath(*e.path.parts[:depth]) for e in runs]
    if depth is not None:
        _paths = sorted(set(_paths), key=lambda p: natural_order(str(p)))
    return tree_strings(build_tree(_paths)) if pprint else _paths
Example #6
0
def cli(prefix: str, path: PurePath, spec: Path, flags: List[str], logger: UI,
        description: str, transaction: Transaction, *args, **kwargs):
    # spec: Path
    with spec.open() as f:
        obj = json.load(f, object_pairs_hook=lambda pairs: pairs)
    try:
        try:
            array = [SpecObj(**dict(obj))]
        except TypeError:
            array = [SpecObj(**dict(o)) for o in obj]
    except TypeError:
        logger.exit(f'Each object in {spec} must have a '
                    f'"command" field and a "flags" field.')

    for i, obj in enumerate(array):
        new_path = path if len(array) == 1 else PurePath(path, str(i))

        def parse_flag(flag: Flag):
            values = flag.values if isinstance(flag.values,
                                               list) else [flag.values]
            null_keys = ['null', '', 'none', 'None']
            return [
                f'--{v}' if flag.key in null_keys else f'--{flag.key}="{v}"'
                for v in values
            ]

        flags = [[f] for f in flags]
        flags += list(map(parse_flag, obj.flags))
        new(path=new_path,
            prefix=prefix,
            command=obj.command,
            description=description,
            flags=flags,
            transaction=transaction)
Example #7
0
def move(
    query_args: QueryArgs,
    dest_path: str,
    kill_tmux: bool,
    transaction: Transaction,
    db: DataBase,
):
    dest_path_is_dir = any(
        [dest_path == ".", f"{dest_path}/%" in db,
         dest_path.endswith("/")])

    for src_pattern in query_args.patterns:
        dest_to_src = defaultdict(list)
        src_entries = db.get(**query_args._replace(
            patterns=[src_pattern])._asdict())

        for entry in src_entries:

            # parent, grandparent, great-grandparent, etc.
            parents = [str(entry.path)
                       ] + [str(p) + "/" for p in entry.path.parents]
            matches = [
                p for p in reversed(parents) if like(str(p),
                                                     str(src_pattern) + "%")
            ]

            head = next(iter(matches))  # a/b/% -> a/b
            tail = PurePath(*[PurePath(m).name
                              for m in matches])  # a/b/% -> c/d

            if dest_path_is_dir:
                dest = PurePath(dest_path, tail)
            else:
                dest = str(entry.path).replace(head.rstrip("/"), dest_path, 1)
            dest_to_src[dest] += [entry.path]

        for dest, srcs in dest_to_src.items():
            for i, src in enumerate(srcs):
                if len(srcs) > 1:
                    dest = PurePath(dest, str(i))
                else:
                    dest = PurePath(dest)
                transaction.move(src=src, dest=dest, kill_tmux=kill_tmux)
                if dest in db:
                    transaction.remove(dest)
Example #8
0
 def get_value(path: PurePath) -> Optional[float]:
     path = Path(
         str(value_path).replace("<path>", str(path)).replace("\\", ""))
     try:
         with path.open() as f:
             return float(f.read())
     except (ValueError, FileNotFoundError):
         print(f"{path} not found")
         return
Example #9
0
def tree_strings(tree, prefix='', root_prefix='', root='.'):
    yield prefix + root_prefix + root
    if root_prefix == '├── ':
        prefix += '│   '
    if root_prefix == '└── ':
        prefix += '    '
    if tree:
        items = _, *tail = tree.items()
        for (root, tree), _next in zip_longest(items, tail):
            for s in tree_strings(tree=tree,
                                  prefix=prefix,
                                  root_prefix='├── ' if _next else '└── ',
                                  root=root):
                yield PurePath(s)
Example #10
0
def strings(runs: List[RunEntry], flags: List[str], prefix: str, db: DataBase,
            path: Optional[PurePath], description: Optional[str]):
    entry_dict = defaultdict(list)
    return_strings = [highlight('To reproduce:')]
    for entry in runs:
        entry_dict[entry.commit].append(entry)
    for commit, entries in entry_dict.items():
        return_strings.append(f'git checkout {commit}')
        command_string = 'runs new'
        for i, entry in enumerate(entries):
            if path is None:
                new_path = entry.path
            elif len(entries) > 1:
                new_path = PurePath(path, str(i))
            else:
                new_path = path

            subcommand = get_command_string(
                path=PurePath(new_path),
                prefix=prefix,
                command=entry.command,
                flags=flags)
            new_path, subcommand, _description = map(json.dumps, [
                str(new_path), subcommand, description
                or entry.description.strip('"').strip("'")
            ])
            if len(entries) == 1:
                command_string += f' {new_path} {subcommand} --description={_description}'
            else:
                command_string = ' \\\n  '.join([
                    command_string,
                    f'--path={new_path}',
                    f'--command={subcommand}',
                    f'--description={_description}',
                ])
        return_strings.append(command_string)
    return return_strings
Example #11
0
def tree_strings(tree, prefix="", root_prefix="", root="."):
    yield prefix + root_prefix + root
    if root_prefix == "├── ":
        prefix += "│   "
    if root_prefix == "└── ":
        prefix += "    "
    if tree:
        items = _, *tail = tree.items()
        for (root, tree), _next in zip_longest(items, tail):
            for s in tree_strings(
                    tree=tree,
                    prefix=prefix,
                    root_prefix="├── " if _next else "└── ",
                    root=root,
            ):
                yield PurePath(s)
Example #12
0
def strings(
    runs: List[RunEntry],
    args: List[str],
    prefix: str,
    db: DataBase,
    description: Optional[str],
    path: Optional[PurePath],
    porcelain: bool,
):
    entry_dict = defaultdict(list)
    return_strings = [] if porcelain else [highlight("To reproduce:")]
    for entry in runs:
        entry_dict[entry.commit].append(entry)
    for commit, entries in entry_dict.items():
        return_strings.append(f"git checkout {commit}")
        string = "runs new"
        for i, entry in enumerate(entries):
            if path is None:
                new_path = entry.path
            elif len(entries) > 1:
                new_path = PurePath(path, str(i))
            else:
                new_path = path

            command = Command(entry.command, path=entry.path)
            command = command.exclude(prefix, *args)
            new_path, command, _description = map(
                json.dumps,
                [
                    str(new_path),
                    str(command),
                    description or entry.description.strip('"').strip("'"),
                ],
            )
            join_string = " " if len(entries) == 1 else " \\\n"
            string = join_string.join(
                [
                    string,
                    f"--path={new_path}",
                    f"--command={command}",
                    f"--description={_description}",
                ]
            )
        return_strings.append(string)
    return return_strings
Example #13
0
def new(path: PurePath, prefix: str, command: str, description: str,
        flags: List[List[str]], transaction: Transaction):
    bash = transaction.bash
    if description is None:
        description = ''
    if description == 'commit-message':
        description = bash.cmd('git log -1 --pretty=%B'.split())

    flag_sets = list(itertools.product(*flags))
    for i, flag_set in enumerate(flag_sets):
        new_path = path if len(flag_sets) == 1 else PurePath(path, str(i))
        if new_path in transaction.db:
            transaction.remove(new_path)

        full_command = build_command(command=command,
                                     path=new_path,
                                     prefix=prefix,
                                     flags=flag_set)
        transaction.add_run(path=new_path,
                            command=full_command,
                            commit=bash.last_commit(),
                            datetime=datetime.now().isoformat(),
                            description=description)
Example #14
0
def cli(
    prefix: str,
    path: PurePath,
    spec: Path,
    args: List[str],
    logger: UI,
    description: str,
    transaction: Transaction,
    max_runs: int,
    *_,
    **__,
):
    # spec: Path
    if not spec.exists():
        logger.exit(f"{spec.absolute()} does not exist.")
    with spec.open() as f:
        obj = json.load(f, object_pairs_hook=lambda pairs: pairs)
    try:
        try:
            spec_objs = [SpecObj(**dict(obj))]
        except ValueError:
            spec_objs = [SpecObj(**dict(o)) for o in obj]
    except TypeError:
        logger.exit(f"Each object in {spec} must have a "
                    '"command" field and a "args" field.')

    def listify(x):
        if isinstance(x, list):
            return x
        return [x]

    def prepend(arg: str):
        if not arg or arg.startswith("-"):
            return arg
        return f"--{arg}"

    def arg_alternatives(key, values):
        for v in listify(values):
            if isinstance(v, list):
                value = " ".join([f'"{_v}"' for _v in v])
                yield [prepend(f"{key} {value}")]
            else:
                yield [prepend(f'{key}="{value}"') for value in listify(v)]

    def flag_alternatives(values):
        if values:
            for v in values:
                yield list(map(prepend, v))
        else:
            yield [None]

    def group_args(spec):
        for k, v in spec.args or []:
            yield list(arg_alternatives(k, v))
        yield list(flag_alternatives(spec.flags))

    def arg_assignments():
        for spec in spec_objs:
            for arg_set in itertools.product(*group_args(spec)):
                yield spec.command, [a for s in arg_set for a in s if a]

    assignments = list(arg_assignments())
    if max_runs is not None and len(assignments) > max_runs:
        random.shuffle(assignments)
        assignments = assignments[:max_runs]
    for i, (command, arg_set) in enumerate(assignments):
        new_path = path if len(assignments) == 1 else PurePath(path, str(i))
        command = Command(prefix, command, *arg_set, *args, path=new_path)
        new(
            path=new_path,
            command=command,
            description=description,
            transaction=transaction,
        )