PK!8&&reprobench/__init__.pyname = "reprobench" VERSION = "0.1.0" PK! reprobench/console/main.py#!/usr/bin/env python import os import argparse import sys import strictyaml import click from loguru import logger from reprobench.core.schema import schema from reprobench.utils import import_class from reprobench.runners import LocalRunner, SlurmRunner @click.group() @click.version_option(version="0.1.0") @click.option("--verbose", "-v", "verbosity", count=True, default=0, help="Verbosity") def cli(verbosity): sys.path.append(os.getcwd()) logger.remove() if verbosity == 0: logger.add(sys.stderr, level="ERROR") elif verbosity == 1: logger.add(sys.stderr, level="WARNING") elif verbosity == 2: logger.add(sys.stderr, level="INFO") elif verbosity == 3: logger.add(sys.stderr, level="DEBUG") elif verbosity >= 4: logger.add(sys.stderr, level="TRACE") @cli.group() def run(): pass @run.command("local") @click.option( "-o", "--output-dir", type=click.Path(file_okay=False, writable=True, resolve_path=True), default="./output", required=True, show_default=True, ) @click.option("-r", "--resume", is_flag=True) @click.argument("config", type=click.File("r")) def local_runner(output_dir, resume, config): config_text = config.read() config = strictyaml.load(config_text, schema=schema).data runner = LocalRunner(config, output_dir, resume) runner.run() @run.command("slurm") @click.option( "-o", "--output-dir", type=click.Path(file_okay=False, writable=True, resolve_path=True), default="./output", required=True, show_default=True, ) @click.option("--run-template", type=click.Path(dir_okay=False, resolve_path=True)) @click.option("--compile-template", type=click.Path(dir_okay=False, resolve_path=True)) @click.option("-r", "--resume", is_flag=True) @click.option("-d", "--teardown", is_flag=True) @click.option("-p", "--python-path", required=True) @click.argument("config", type=click.File("r")) def slurm_runner( output_dir, run_template, compile_template, resume, teardown, python_path, config ): config_path = os.path.realpath(config.name) config_text = config.read() config = strictyaml.load(config_text, schema=schema).data runner = SlurmRunner( config=config, run_template_file=run_template, compile_template_file=compile_template, config_path=config_path, output_dir=output_dir, resume=resume, teardown=teardown, python_path=python_path, ) runner.run() if __name__ == "__main__": cli() PK!+(reprobench/core/bases.pyclass Runner: def __init__(self, config): pass def run(self): pass class Step: @classmethod def register(cls, config={}): pass @classmethod def execute(cls, context, config={}): pass class Tool: name = "Base Tool" REQUIRED_PATHS = [] @classmethod def setup(cls): pass @classmethod def version(cls): return "1.0.0" @classmethod def pre_run(cls, context): pass @classmethod def cmdline(cls, context): pass @classmethod def post_run(cls, context): pass @classmethod def teardown(cls): pass PK!D2 reprobench/core/bootstrap.pyimport itertools from pathlib import Path from loguru import logger from tqdm import tqdm from reprobench.core.db import ( MODELS, Limit, Parameter, ParameterCategory, Run, Task, TaskCategory, Tool, db, ) from reprobench.task_sources.doi import DOISource from reprobench.task_sources.local import LocalSource from reprobench.task_sources.url import UrlSource from reprobench.utils import import_class def _bootstrap_db(config): logger.info("Bootstrapping db...") db.connect() db.create_tables(MODELS) Limit.insert_many( [{"type": key, "value": value} for (key, value) in config["limits"].items()] ).execute() Tool.insert_many( [{"name": name, "module": module} for (name, module) in config["tools"].items()] ).execute() for (category, parameters) in config["parameters"].items(): parameter_category = ParameterCategory.create(title=category) for (key, value) in parameters.items(): Parameter.create(category=parameter_category, key=key, value=value) def _bootstrap_tasks(config): logger.info("Bootstrapping tasks...") for (category, task) in config["tasks"].items(): task_category = TaskCategory.create(title=category) source = None if task["type"] == "local": source = LocalSource(**task) elif task["type"] == "url": source = UrlSource(**task) elif task["type"] == "doi": source = DOISource(**task) else: raise NotImplementedError( f"No implementation for task source {task['type']}" ) files = source.setup() for file in files: Task.create(category=task_category, path=str(file)) def _setup_tools(config): logger.info("Running setups on tools...") for tool in config["tools"].values(): import_class(tool).setup() def _register_steps(config): logger.info("Registering steps...") for runstep in itertools.chain.from_iterable(config["steps"].values()): import_class(runstep["step"]).register(runstep.get("config", {})) def _bootstrap_runs(config, output_dir): tools = Tool.select(Tool.id, Tool.name).iterator() parameters = ParameterCategory.select( ParameterCategory.id, ParameterCategory.title ).iterator() tasks = ( Task.select(Task.id, Task.path, TaskCategory.title) .join(TaskCategory) .iterator() ) for (tool, parameter, task) in tqdm( itertools.product(tools, parameters, tasks), desc="Bootstrapping runs" ): directory = ( Path(output_dir) / tool.name / parameter.title / task.category.title / Path(task.path).name ) directory.mkdir(parents=True, exist_ok=True) Run.create( tool=tool, task=task, parameter_category=parameter, directory=directory, status=Run.SUBMITTED, ) def bootstrap(config, output_dir): _bootstrap_db(config) _bootstrap_tasks(config) _setup_tools(config) _register_steps(config) _bootstrap_runs(config, output_dir) PK!o/TTreprobench/core/db.pyfrom pathlib import Path from datetime import datetime from peewee import Proxy, Model from playhouse.apsw_ext import ( DateTimeField, CharField, ForeignKeyField, IntegerField, BooleanField, ) db = Proxy() class BaseModel(Model): created_at = DateTimeField(default=datetime.now) class Meta: database = db class Limit(BaseModel): type = CharField(max_length=32, unique=True) value = CharField() class TaskCategory(BaseModel): title = CharField() class Task(BaseModel): category = ForeignKeyField(TaskCategory, backref="tasks", on_delete="cascade") path = CharField() class Tool(BaseModel): module = CharField(unique=True) name = CharField() version = CharField(null=True) class ParameterCategory(BaseModel): title = CharField() class Parameter(BaseModel): category = ForeignKeyField( ParameterCategory, backref="parameters", on_delete="cascade" ) key = CharField() value = CharField() class Meta: indexes = ((("category", "key"), True),) class Run(BaseModel): FAILED = -2 CANCELED = -1 PENDING = 0 SUBMITTED = 1 RUNNING = 2 DONE = 3 STATUS_CHOICES = ( (FAILED, "Failed"), (CANCELED, "Canceled"), (PENDING, "Pending"), (SUBMITTED, "Submitted"), (RUNNING, "Running"), (DONE, "Done"), ) tool = ForeignKeyField(Tool, backref="runs", on_delete="cascade") parameter_category = ForeignKeyField( ParameterCategory, backref="runs", on_delete="cascade" ) task = ForeignKeyField(Task, backref="runs", on_delete="cascade") status = IntegerField(choices=STATUS_CHOICES, default=PENDING) directory = CharField(null=True) class Meta: only_save_dirty = True MODELS = [Limit, TaskCategory, Task, ParameterCategory, Parameter, Run, Tool] PK!"]+;77reprobench/core/exceptions.pyclass ExecutableNotFoundError(RuntimeError): pass PK!:Juureprobench/core/schema.pyfrom strictyaml import ( Map, Regex, Seq, Str, Int, Optional, Seq, MapPattern, Enum, Bool, Any, ) limits_schema = Map( { "time": Int(), Optional("memory", default=8192): Int(), Optional("output"): Int(), Optional("cores"): Str(), } ) module_schema = Regex(r"\.?\w+(\.\w+)*") step_schema = Seq( Map({"step": module_schema, Optional("config"): MapPattern(Str(), Any())}) ) task_sources = Enum(["local", "url"]) schema = Map( { "title": Str(), Optional("description"): Str(), "limits": limits_schema, "steps": Map({"run": step_schema, Optional("compile"): step_schema}), "tasks": MapPattern(Str(), MapPattern(Str(), Any())), "tools": MapPattern(Str(), module_schema), "parameters": MapPattern(Str(), MapPattern(Str(), Str())), } ) PK!#5LL reprobench/executors/__init__.py# from .runsolver import RunsolverExecutor from .psmon import PsmonExecutor PK!Lreprobench/executors/db.pyfrom reprobench.core.db import BaseModel, Run from playhouse.apsw_ext import ForeignKeyField, FloatField, CharField, IntegerField class RunStatistic(BaseModel): TIMEOUT = "TLE" MEMOUT = "MEM" RUNTIME_ERR = "RTE" OUTPUT_LIMIT = "OLE" SUCCESS = "OK" VERDICT_CHOICES = ( (TIMEOUT, "Time Limit Exceeded"), (MEMOUT, "Memory Limit Exceeded"), (RUNTIME_ERR, "Runtime Error"), (OUTPUT_LIMIT, "Output Limit Exceeded"), (SUCCESS, "Run Successfully"), ) run = ForeignKeyField(Run, backref="statistics", on_delete="cascade") cpu_time = FloatField(help_text="CPU Time (s)", null=True) wall_time = FloatField(help_text="Wall Clock Time (s)", null=True) max_memory = FloatField(help_text="Max Memory Usage (KiB)", null=True) return_code = IntegerField(help_text="Process Return Code", null=True) verdict = CharField(choices=VERDICT_CHOICES, max_length=3, null=True) PK!0Qreprobench/executors/psmon.pyimport subprocess from loguru import logger from pathlib import Path from psmon import ProcessMonitor from psmon.limiters import WallTimeLimiter, CpuTimeLimiter, MaxMemoryLimiter from reprobench.core.bases import Step from reprobench.core.db import db, Run from .db import RunStatistic class PsmonExecutor(Step): @classmethod def register(cls, config={}): RunStatistic.create_table() @classmethod def execute(cls, context, config={}): tool = context["tool"] limits = context["limits"] tool.pre_run(context) cwd = context["run"].directory out_file = (Path(cwd) / "run.out").open("wb") err_file = (Path(cwd) / "run.err").open("wb") with db.atomic("EXCLUSIVE"): context["run"].status = Run.RUNNING context["run"].save() cmd = tool.cmdline(context) logger.debug(f"Running {cwd}") logger.trace(cmd) monitor = ProcessMonitor( cmd, cwd=cwd, stdout=out_file, stderr=err_file, freq=15 ) monitor.subscribe("wall_time", WallTimeLimiter(limits["time"] + 15)) monitor.subscribe("cpu_time", CpuTimeLimiter(limits["time"])) monitor.subscribe("max_memory", MaxMemoryLimiter(limits["memory"])) stats = monitor.run() logger.debug(f"Finished {cwd}") logger.debug(stats) with db.atomic("EXCLUSIVE"): context["run"].status = Run.DONE context["run"].save() verdict = None if stats["error"] == TimeoutError: verdict = RunStatistic.TIMEOUT elif stats["error"] == MemoryError: verdict = RunStatistic.MEMOUT elif stats["error"] or stats["return_code"] != 0: verdict = RunStatistic.RUNTIME_ERR else: verdict = RunStatistic.SUCCESS RunStatistic.create( run=context["run"], cpu_time=stats["cpu_time"], wall_time=stats["wall_time"], max_memory=stats["max_memory"], return_code=stats["return_code"], verdict=verdict, ) tool.post_run(context) PK!3b b !reprobench/executors/runsolver.py""" TODO: Update to latest refactor """ import subprocess import functools import operator from pathlib import Path from reprobench.core.bases import Step from reprobench.core.db import Run, RunStatistic from reprobench.utils import find_executable, silent_run class RunsolverExecutor(Step): def __init__(self): self.executable = find_executable("runsolver") def run(self, context): tool = context["tool"] limits = context["limits"] tool.pre_run(context) cwd = context["working_directory"] out_file = (Path(cwd) / "run.out").open("wb") err_file = (Path(cwd) / "run.err").open("wb") context["run"].status = Run.RUNNING context["run"].save() process = subprocess.run( [ self.executable, "-w", "run.watcher", "-v", "run.stat", "--cores", limits["cores"], "-C", str(limits["time"]), "--vsize-limit", str(limits["memory"]), # "-O", "0,{}".format(limits["output"]), "--", ] + tool.cmdline(context), cwd=cwd, stdout=out_file, stderr=err_file, ) context["run"].status = Run.DONE context["run"].verdict = Run.SUCCESS context["run"].save() tool.post_run(context) out_file.close() err_file.close() stat_file = Path(cwd) / "run.stat" stat_map = { "WCTIME": RunStatistic.WALL_TIME, "CPUTIME": RunStatistic.CPU_TIME, "MAXVM": RunStatistic.MEM_USAGE, } with stat_file.open() as f: for line in f: if line.startswith("#"): continue key, value = line.split("=") if key in stat_map: RunStatistic.create( run=context["run"], key=stat_map[key], value=value ) elif key == "TIMEOUT" and value == "true": context["run"].verdict = Run.TIMEOUT context["run"].save() elif key == "MEMOUT" and value == "true": context["run"].verdict = Run.MEMOUT context["run"].save() PK!HK>>reprobench/runners/__init__.pyfrom .local import LocalRunner from .slurm import SlurmRunner PK!=m m reprobench/runners/local.pyimport atexit import itertools import os import shutil import signal import time from datetime import datetime from multiprocessing.pool import Pool from pathlib import Path from loguru import logger from playhouse.apsw_ext import APSWDatabase from tqdm import tqdm from reprobench.core.bases import Runner from reprobench.core.bootstrap import bootstrap from reprobench.core.db import ParameterCategory, Run, Task, Tool, db from reprobench.task_sources.local import LocalSource from reprobench.utils import import_class def execute_run(args): run_id, config, db_path = args run = Run.get_by_id(run_id) ToolClass = import_class(run.tool.module) tool_instance = ToolClass() db.initialize(APSWDatabase(str(db_path))) context = config.copy() context["tool"] = tool_instance context["run"] = run logger.info(f"Processing task: {run.directory}") @atexit.register def exit(): signal.signal(signal.SIGTERM, signal.SIG_IGN) os.killpg(os.getpgid(0), signal.SIGTERM) time.sleep(3) os.killpg(os.getpgid(0), signal.SIGKILL) for runstep in config["steps"]["run"]: Step = import_class(runstep["step"]) Step.execute(context, runstep.get("config", {})) class LocalRunner(Runner): def __init__(self, config, output_dir="./output", resume=False): self.config = config self.output_dir = output_dir self.resume = resume self.queue = [] def setup(self): atexit.register(self.exit) self.setup_finished = False self.db_path = Path(self.output_dir) / f"{self.config['title']}.benchmark.db" db_created = Path(self.db_path).is_file() if db_created and not self.resume: logger.error( "It seems that a previous runs already exist at the output directory.\ Please use --resume to resume unfinished runs." ) self.setup_finished = True exit(1) Path(self.output_dir).mkdir(parents=True, exist_ok=True) logger.debug(f"Creating Database: {self.db_path}") self.database = APSWDatabase(str(self.db_path)) db.initialize(self.database) # TODO: maybe use .bootstrapped file instead? if not db_created: bootstrap(self.config, self.output_dir) self.setup_finished = True def exit(self): if len(self.queue) > 0 and hasattr(self, "pool"): self.pool.terminate() self.pool.join() if not self.resume and not self.setup_finished: shutil.rmtree(self.output_dir) def populate_unfinished_runs(self): query = Run.select(Run.id).where(Run.status < Run.DONE) self.queue = [(run.id, self.config, self.db_path) for run in query] def run(self): self.setup() self.populate_unfinished_runs() if len(self.queue) == 0: logger.success("No tasks remaining to run") exit(0) logger.debug("Executing runs...") self.pool = Pool() it = self.pool.imap_unordered(execute_run, self.queue) num_in_queue = len(self.queue) for _ in tqdm(it, total=num_in_queue): num_in_queue -= 1 self.pool.close() self.pool.join() logger.debug("Running teardown on all tools...") for tool in self.config["tools"].values(): import_class(tool).teardown() PK!P$reprobench/runners/slurm/__init__.pyimport os import signal import itertools import time import subprocess from string import Template from loguru import logger from multiprocessing.pool import Pool from pathlib import Path from playhouse.apsw_ext import APSWDatabase from reprobench.core.bases import Runner from reprobench.core.bootstrap import bootstrap from reprobench.core.db import db, Run, Tool, ParameterCategory, Task from reprobench.utils import import_class from .utils import create_ranges DIR = os.path.dirname(os.path.realpath(__file__)) class SlurmRunner(Runner): def __init__( self, config, config_path, python_path, output_dir="./output", **kwargs ): self.config = config self.config_path = config_path self.output_dir = output_dir self.python_path = python_path self.resume = kwargs.get("resume", False) self.teardown = kwargs.get("teardown", False) self.run_template_file = kwargs.get("run_template_file") self.compile_template_file = kwargs.get("compile_template_file") self.queue = [] if self.run_template_file is None: self.run_template_file = os.path.join(DIR, "./slurm.run.job.tpl") if self.compile_template_file is None: self.compile_template_file = (os.path.join(DIR, "./slurm.compile.job.tpl"),) def setup(self): self.db_path = Path(self.output_dir) / f"{self.config['title']}.benchmark.db" db_created = Path(self.db_path).is_file() if db_created and not self.resume: logger.error( "It seems that a previous runs exist at the output directory. Please use --resume to resume runs." ) exit(1) Path(self.output_dir).mkdir(parents=True, exist_ok=True) logger.debug(f"Creating Database: {self.db_path}") self.database = APSWDatabase(str(self.db_path)) db.initialize(self.database) # TODO: maybe use .bootstrapped file instead? if not db_created: bootstrap(self.config, self.output_dir) def populate_unfinished_runs(self): query = Run.select(Run.id).where(Run.status < Run.DONE) self.queue = [(run.id, self.config, self.db_path) for run in query] def run(self): if not self.teardown: self.setup() if self.resume: logger.info("Resuming unfinished runs...") self.populate_unfinished_runs() logger.debug("Generating template") with open(self.run_template_file) as tpl: template = Template(tpl.read()) job_str = template.safe_substitute( mem=int(1 + self.config["limits"]["memory"] / 1024 / 1024), # mb time=int(1 + (self.config["limits"]["time"] + 15) / 60), # minutes run_ids=create_ranges(self.queue), python_path=self.python_path, config_path=self.config_path, db_path=self.db_path, ) slurm_job_path = Path(self.output_dir) / "slurm.job" with open(slurm_job_path, "w") as job: job.write(job_str) logger.info("Submitting job array to SLURM...") sbatch_cmd = ["sbatch", str(slurm_job_path.resolve())] logger.debug(" ".join(sbatch_cmd)) subprocess.run(sbatch_cmd) else: logger.debug("Running teardown on all tools...") for tool_module in self.config["tools"].values(): import_class(tool_module).teardown() PK!Z.reprobench/runners/slurm/slurm.compile.job.tpl#!/bin/bash #SBATCH --export=all srun -- $python_path -m reprobench.runners.slurm.slurm_worker compile -c $config_path -d $db_path PK!9>=*reprobench/runners/slurm/slurm.run.job.tpl#!/bin/bash #SBATCH --export=all #SBATCH --array=$run_ids #SBATCH --mem=$mem #SBATCH --time=$time srun -- $python_path -m reprobench.runners.slurm.slurm_worker -c $config_path -d $db_path $SLURM_ARRAY_TASK_ID PK! W22(reprobench/runners/slurm/slurm_worker.pyimport os import signal import click import atexit import strictyaml import time from loguru import logger from playhouse.apsw_ext import APSWDatabase from reprobench.core.schema import schema from reprobench.core.db import db, Run from reprobench.utils import import_class @click.command() @click.option("-c", "--config", required=True, type=click.File()) @click.option( "-d", "--database", required=True, type=click.Path(dir_okay=False, resolve_path=True), ) @click.argument("run_id", type=int) def run(config, database, run_id): config = config.read() config = strictyaml.load(config, schema=schema).data db.initialize(APSWDatabase(str(database))) run = Run.get_by_id(run_id) ToolClass = import_class(run.tool.module) tool_instance = ToolClass() context = config.copy() context["tool"] = tool_instance context["run"] = run logger.info(f"Processing task: {run.directory}") @atexit.register def exit(): signal.signal(signal.SIGTERM, signal.SIG_IGN) os.killpg(os.getpgid(0), signal.SIGTERM) time.sleep(3) os.killpg(os.getpgid(0), signal.SIGKILL) for runstep in config["steps"]["run"]: Step = import_class(runstep["step"]) Step.execute(context, runstep.get("config", {})) if __name__ == "__main__": run() PK!&ı!reprobench/runners/slurm/utils.pyfrom itertools import tee, zip_longest # https://stackoverflow.com/a/3430312/9314778 def pairwise_longest(iterable): "variation of pairwise in http://docs.python.org/library/itertools.html#recipes" a, b = tee(iterable) next(b, None) return zip_longest(a, b) def takeuntil(predicate, iterable): """returns all elements before and including the one for which the predicate is true variation of http://docs.python.org/library/itertools.html#itertools.takewhile""" for x in iterable: yield x if predicate(x): break def get_range(it): "gets a range from a pairwise iterator" rng = list(takeuntil(lambda args: (args[1] is None) or (args[1] - args[0] > 1), it)) if rng: b, e = rng[0][0], rng[-1][0] return "%d-%d" % (b, e) if b != e else str(b) def create_ranges(zones): it = pairwise_longest(zones) return ",".join(iter(lambda: get_range(it), None)) PK!#reprobench/task_sources/__init__.pyPK!ϗ?reprobench/task_sources/base.pyclass BaseTaskSource(object): def __init__(self, path=None, **kwargs): self.path = path def setup(self): return [] PK!11'reprobench/task_sources/doi/__init__.pyfrom reprobench.task_sources.url import UrlSource from reprobench.task_sources.doi.zenodo import ZenodoHandler, ZenodoSandboxHandler class DOISource(UrlSource): handlers = [ZenodoHandler, ZenodoSandboxHandler] def __init__(self, doi, **kwargs): super().__init__(**kwargs) self.doi = doi for handler in self.handlers: if handler.is_compatible(self.doi): self.urls = handler.get_urls(self.doi) break else: raise NotImplementedError(f"No handler for doi: {doi}") PK!RO#reprobench/task_sources/doi/base.pyclass BaseDOIHandler(object): @classmethod def is_compatible(cls, doi): return False @classmethod def get_urls(cls, doi): return [] PK!Sʺ%reprobench/task_sources/doi/zenodo.pyimport requests from reprobench.task_sources.doi.base import BaseDOIHandler class ZenodoHandler(BaseDOIHandler): doi_prefix = "10.5281/zenodo." api_url = "https://zenodo.org/api" @classmethod def is_compatible(cls, doi): return doi.startswith(cls.doi_prefix) @classmethod def get_urls(cls, doi): record_id = doi[len(cls.doi_prefix) :] # remove doi_prefix url = "{}/records/{}".format(cls.api_url, record_id) record = requests.get(url).json() return [file["links"]["self"] for file in record["files"]] class ZenodoSandboxHandler(ZenodoHandler): doi_prefix = "10.5072/zenodo." api_url = "https://sandbox.zenodo.org/api" PK!a reprobench/task_sources/local.pyfrom pathspec import PathSpec from pathlib import Path from .base import BaseTaskSource class LocalSource(BaseTaskSource): def __init__(self, path=None, patterns="", **kwargs): super().__init__(path) self.patterns = patterns def setup(self): spec = PathSpec.from_lines("gitwildmatch", self.patterns.splitlines()) matches = spec.match_tree(self.path) return map(lambda match: Path(self.path) / match, matches) PK! DDreprobench/task_sources/url.pyfrom pathlib import Path from zipfile import ZipFile from loguru import logger from reprobench.utils import download_file from .local import LocalSource class UrlSource(LocalSource): def __init__( self, urls=[], path=None, patterns="", skip_existing=True, extract_archives=True, **kwargs, ): super().__init__(path, patterns=patterns) self.urls = urls self.extract_archives = extract_archives self.skip_existing = skip_existing def extract_zip(self, path): extract_path = Path(path) / ".." / path.stem if not extract_path.is_dir(): with ZipFile(path) as zip: zip.extractall(extract_path) def setup(self): root = Path(self.path) root.mkdir(parents=True, exist_ok=True) for url in self.urls: filename = url.split("/")[-1].split("?")[0] path = root / filename if not path.exists() or not self.skip_existing: logger.debug(f"Downloading {url} to {path}") download_file(url, path) else: logger.debug(f"Skipping already downloaded file {path}") if self.extract_archives and path.suffix == ".zip": self.extract_zip(path) return super().setup() PK!Creprobench/tools/executable.pyimport subprocess import tempfile import shutil from pathlib import Path from uuid import uuid4 from reprobench.core.bases import Tool from reprobench.utils import find_executable, silent_run class ExecutableTool(Tool): name = "Basic Executable Tool" path = None PK!9Rreprobench/tools/reprozip.pyimport subprocess import tempfile import shutil from pathlib import Path from uuid import uuid4 from reprobench.core.bases import Tool from reprobench.utils import find_executable, silent_run class ReprozipTool(Tool): name = "Reprozip-based Tool" path = None runner = "directory" REQUIRED_PATHS = [ str((Path(find_executable("reprounzip")) / ".." / "..").resolve()), tempfile.gettempdir(), ] def __init__(self): self.reprounzip = find_executable("reprounzip") self.dir = f"{tempfile.gettempdir()}/reprounzip-{uuid4()}" self.base_command = [self.reprounzip, self.runner] def setup(self): silent_run(self.base_command + ["setup", self.path, self.dir]) def cmdline(self, context): return self.base_command + ["run", self.dir] def teardown(self): silent_run(self.base_command + ["destroy", self.dir]) PK!!mreprobench/utils.pyimport importlib import logging import subprocess from shutil import which import requests from tqdm import tqdm from reprobench.core.exceptions import ExecutableNotFoundError log = logging.getLogger(__name__) def find_executable(executable): path = which(executable) if path is None: raise ExecutableNotFoundError return path def silent_run(command): log.debug(f"Running: {command}") return subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) def import_class(path): module_path, tail = ".".join(path.split(".")[:-1]), path.split(".")[-1] module = importlib.import_module(module_path) return getattr(module, tail) def copyfileobj(fsrc, fdst, callback, length=16 * 1024): while True: buf = fsrc.read(length) if not buf: break fdst.write(buf) callback(len(buf)) def download_file(url, dest): r = requests.get(url, stream=True) with tqdm( total=int(r.headers["content-length"]), unit="B", unit_scale=True, unit_divisor=1024, ) as progress_bar: progress_bar.set_postfix(file=dest, refresh=False) with open(dest, "wb") as f: copyfileobj(r.raw, f, progress_bar.update) PK!HJ.:+reprobench-0.3.3.dist-info/entry_points.txtN+I/N.,()*J-(OJKΰE0r3s2PK!XB33"reprobench-0.3.3.dist-info/LICENSEMIT License Copyright (c) 2019 Rakha Kanz Kautsar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!Hu)GTU reprobench-0.3.3.dist-info/WHEEL HM K-*ϳR03rOK-J,/R(O-)$qzd&Y)r$UV&UrPK!H] @#reprobench-0.3.3.dist-info/METADATAoo0S!Uj]7Z`u: 7%׿[ 7N|9˾k<_P2m"#^BL h lTeM[U"Ou}P^JJ4Ϝۈ\$U6WiY%RYf pfXJr 0vk}NyŃ2qLo&Cz]sHٯJFe2\_/mL='|H fKL'n a@ɯ0^cSl L#)-6?n2bƲY `>{m;t/t5hs[>a}1sj}=|޻ba'4_ ,cKݻ>hJ3S`TIKQM3/. 0*yİR4S!J9F^&s)%bN! j5i *i%w1 p %#K|Ҭz&`Y!KjzɋQy>~Й)" :1LE @g;[A8̱9;;u4GTXRJF<N?﷨Jފ6|O.;]KiDPӠ"?]rDwA iy<A7Rsxp+0ϊhK Ks/ya("k;ʥJ~Gdas㫸`eZto`u`ȼLؼáJ1ILVLJ8;QV Щp2g߼k)Kl!J,F@3طځ#zb5qRs=7s-{$!V پ#<GF \Dh>+߳q{vDBkq(p @p6鞇W=k|J9(4S$ ubUpw$u}u?m5waaSV qyL9$lA4ÑٟFNyEPgG[dL,! bq_i9AWAޙ[MW`OHRjI;D[h*"Ohq,|B1u||I_I3 ߵy3o}7Wۈ3WQ'n'P8x ONkjb fu8xcf* P]r?ю(ؘ+`&*v*Xn/d1pђ2q&dUbyb; nY?"2MR[LSpӳl<PK!8&&reprobench/__init__.pyPK! Zreprobench/console/main.pyPK!+( reprobench/core/bases.pyPK!D2 O reprobench/core/bootstrap.pyPK!o/TTreprobench/core/db.pyPK!"]+;77!reprobench/core/exceptions.pyPK!:Juu"reprobench/core/schema.pyPK!#5LL %reprobench/executors/__init__.pyPK!LD&reprobench/executors/db.pyPK!0Q/*reprobench/executors/psmon.pyPK!3b b !3reprobench/executors/runsolver.pyPK!HK>><reprobench/runners/__init__.pyPK!=m m +=reprobench/runners/local.pyPK!P$Jreprobench/runners/slurm/__init__.pyPK!Z.Yreprobench/runners/slurm/slurm.compile.job.tplPK!9>=*Yreprobench/runners/slurm/slurm.run.job.tplPK! W22([reprobench/runners/slurm/slurm_worker.pyPK!&ı!z`reprobench/runners/slurm/utils.pyPK!#jdreprobench/task_sources/__init__.pyPK!ϗ?dreprobench/task_sources/base.pyPK!11'tereprobench/task_sources/doi/__init__.pyPK!RO#greprobench/task_sources/doi/base.pyPK!Sʺ%hreprobench/task_sources/doi/zenodo.pyPK!a kreprobench/task_sources/local.pyPK! DDmreprobench/task_sources/url.pyPK!CWsreprobench/tools/executable.pyPK!9Rtreprobench/tools/reprozip.pyPK!!mixreprobench/utils.pyPK!HJ.:+}reprobench-0.3.3.dist-info/entry_points.txtPK!XB33"~reprobench-0.3.3.dist-info/LICENSEPK!Hu)GTU treprobench-0.3.3.dist-info/WHEELPK!H] @#reprobench-0.3.3.dist-info/METADATAPK!H5ѯ !Qreprobench-0.3.3.dist-info/RECORDPK!!