def debug(ctx: click.Context, hash: str, output: Optional[str]) -> None: """Extract all data related to a given hash value.""" logger.info(f"extracting {hash}") if output is None: output = hash output2 = "" else: output2 = output + "_" with funsies._context.Fun(ctx.obj): things = funsies.get(hash) if len(things) == 0: logger.error(f"{hash} does not correspond to anything!") elif len(things) == 1: logger.info(f"got {type(things[0])}") funsies.debug.anything(things[0], output) logger.success(f"saved to {output}") else: logger.warning(f"got {len(things)} objects") funsies.debug.anything(things[0], output) logger.success(f"{type(things[0])} -> {output}") for el in things[1:]: funsies.debug.anything(el, output2 + el.hash) logger.success(f"{type(el)} -> {output2 + el.hash}")
def wait( # noqa:C901 ctx: click.Context, hashes: tuple[str, ...], timeout: Optional[float]) -> None: """Wait until redis database or certain hashes are ready.""" if timeout is not None: tmax = time.time() + timeout while True: try: db, _ = ctx.obj.new_connection(try_fail=True) db.ping() break except Exception: time.sleep(1.0) if timeout is not None: t1 = time.time() if t1 > tmax: logger.error("timeout exceeded") raise SystemExit(2) with funsies._context.Fun(ctx.obj): db = funsies._context.get_redis() h = [] for hash in hashes: things = funsies.get(hash) if len(things) == 0: logger.warning(f"no object with hash {hash}") for t in things: if isinstance(t, types.Artefact): h += [t.hash] logger.info(f"waiting on artefact at {hash}") elif isinstance(t, types.Operation): h += [next(iter(t.out.values()))] logger.info(f"waiting on operation at {hash}") else: logger.warning(f"ignoring {type(t)} at {t.hash}") while len(h) > 0: stat = get_status(db, h[0], resolve_links=True) if stat > 0: h.pop(0) logger.success(f"{len(h)} things left to wait for.") time.sleep(0.5) if timeout is not None: t1 = time.time() if t1 > tmax: logger.error("timeout exceeded") raise SystemExit(2)
def execute(ctx: click.Context, hashes: tuple[str, ...]) -> None: """Enqueue execution of hashes.""" with funsies._context.Fun(ctx.obj): exec_list = [] for hash in hashes: things = funsies.get(hash) if len(things) == 0: logger.warning(f"no object with hash {hash}") for t in things: if isinstance(t, types.Operation) or isinstance( t, types.Artefact): exec_list += [t] else: logger.warning( f"object with hash {hash} of type {type(t)} skipped") funsies.execute(*exec_list)
def cat(ctx: click.Context, hashes: tuple[str, ...]) -> None: """Print artefacts to stdout.""" with funsies._context.Fun(ctx.obj): for hash in hashes: logger.info(f"extracting {hash}") things = funsies.get(hash) if len(things) == 0: logger.error("hash does not correspond to anything!") raise SystemExit(2) if len(things) > 1: logger.error(f"hash resolves to {len(things)} things.") art = things[0] if isinstance(art, types.Artefact): res = funsies.take(art, strict=False) if isinstance(res, types.Error): logger.warning(f"error at {hash}: {res.kind}") if res.details is not None: sys.stderr.buffer.write((res.details + "\n").encode()) logger.warning(f"error source: {res.source}") elif isinstance(res, bytes): sys.stdout.buffer.write(res) logger.success(f"{hash} output to stdout") elif isinstance(res, str): sys.stdout.buffer.write(res.encode()) logger.success(f"{hash} output to stdout") else: sys.stdout.buffer.write(json.dumps(res).encode()) logger.success(f"{hash} output to stdout") elif isinstance(art, types.Operation): logger.error("not an artefact") logger.info("did you mean...") sys.stderr.write(" INPUTS:\n") for key, val in art.inp.items(): sys.stderr.write(f" {key:<30} -> {val[:8]}\n") sys.stderr.write(" OUTPUTS:\n") for key, val in art.out.items(): sys.stderr.write(f" {key:<30} -> {val[:8]}\n") else: logger.error("not an artefact:") logger.error(f"{art}")
def graph(ctx: click.Context, hashes: tuple[str, ...], inputs: bool) -> None: """Print to stdout a DOT-formatted graph to visualize DAGs.""" # funsies import funsies._graphviz with funsies._context.Fun(ctx.obj): db = funsies._context.get_redis() if len(hashes) == 0: # If no hashes are passed, we graph all the DAGs on index hashes = tuple([ dag.decode() for dag in db.smembers(funsies._constants.DAG_INDEX) ]) all_data: list[hash_t] = [] for hash in hashes: things = funsies.get(hash.split("/")[-1]) if len(things) == 0: logger.warning(f"no object with hash {hash}") for t in things: if isinstance(t, types.Operation) or isinstance( t, types.Artefact): all_data += [hash_t(t.hash)] if len(all_data): logger.info(f"writing graph for {len(all_data)} objects") out = funsies._graphviz.format_dot( *funsies._graphviz.export(db, all_data), targets=all_data, show_inputs=inputs, ) sys.stdout.write(out) logger.success("done") else: logger.error("No data points") raise SystemExit(2)
def reset(ctx: click.Context, hashes: tuple[str, ...]) -> None: """Reset operations and their dependents.""" with funsies._context.Fun(ctx.obj): for hash in hashes: things = funsies.get(hash) if len(things) == 0: logger.warning(f"no object with hash {hash}") if len(things) > 1: logger.error( f"more than one object with hash starting with {hash}") logger.info("which one do you mean of :") for t in things: logger.info(f"\t{t.hash}") logger.info("none were reset") else: if isinstance(things[0], types.Artefact) or isinstance( things[0], types.Operation): funsies.ui.reset(things[0]) logger.success(f"{hash} was reset") else: logger.error( f"object {hash} is neither an operation or an artefact" ) logger.info(f"{hash} was not reset")