diff --git a/artiq/browser/files.py b/artiq/browser/files.py index 103ca28db..e03f26483 100644 --- a/artiq/browser/files.py +++ b/artiq/browser/files.py @@ -107,7 +107,7 @@ class Hdf5FileSystemModel(QtWidgets.QFileSystemModel): v = ("artiq_version: {}\nrepo_rev: {}\nfile: {}\n" "class_name: {}\nrid: {}\nstart_time: {}").format( h5["artiq_version"][()], expid["repo_rev"], - expid["file"], expid["class_name"], + expid.get("file", ""), expid["class_name"], h5["rid"][()], start_time) return v except: @@ -179,7 +179,7 @@ class FilesDock(QtWidgets.QDockWidget): v = { "artiq_version": f["artiq_version"][()], "repo_rev": expid["repo_rev"], - "file": expid["file"], + "file": expid.get("file", ""), "class_name": expid["class_name"], "rid": f["rid"][()], "start_time": start_time, diff --git a/artiq/dashboard/experiments.py b/artiq/dashboard/experiments.py index 6095a3466..871894a66 100644 --- a/artiq/dashboard/experiments.py +++ b/artiq/dashboard/experiments.py @@ -726,7 +726,7 @@ class ExperimentManager: else: repo_match = "repo_rev" not in expid if (repo_match and - expid["file"] == file and + ("file" in expid and expid["file"] == file) and expid["class_name"] == class_name): rids.append(rid) asyncio.ensure_future(self._request_term_multiple(rids)) diff --git a/artiq/dashboard/schedule.py b/artiq/dashboard/schedule.py index 59ec64880..d6b74a0a7 100644 --- a/artiq/dashboard/schedule.py +++ b/artiq/dashboard/schedule.py @@ -48,7 +48,7 @@ class Model(DictSyncModel): else: return "Outside repo." elif column == 6: - return v["expid"]["file"] + return v["expid"].get("file", "") elif column == 7: if v["expid"]["class_name"] is None: return "" diff --git a/artiq/frontend/artiq_client.py b/artiq/frontend/artiq_client.py index 7194a4e45..993fe0ee4 100755 --- a/artiq/frontend/artiq_client.py +++ b/artiq/frontend/artiq_client.py @@ -67,6 +67,9 @@ def get_argparser(): parser_add.add_argument("-r", "--revision", default=None, help="use a specific repository revision " "(defaults to head, ignored without -R)") + parser_add.add_argument("--content", default=False, + action="store_true", + help="submit by content") parser_add.add_argument("-c", "--class-name", default=None, help="name of the class to run") parser_add.add_argument("file", metavar="FILE", @@ -134,12 +137,18 @@ def _action_submit(remote, args): expid = { "log_level": logging.WARNING + args.quiet*10 - args.verbose*10, - "file": args.file, "class_name": args.class_name, "arguments": arguments, } - if args.repository: - expid["repo_rev"] = args.revision + if args.content: + with open(args.file, "r") as f: + expid["content"] = f.read() + if args.repository: + raise ValueError("Repository cannot be used when submitting by content") + else: + expid["file"] = args.file + if args.repository: + expid["repo_rev"] = args.revision if args.timed is None: due_date = None else: @@ -207,7 +216,7 @@ def _show_schedule(schedule): row.append(expid["repo_rev"]) else: row.append("Outside repo.") - row.append(expid["file"]) + row.append(expid.get("file", "")) if expid["class_name"] is None: row.append("") else: diff --git a/artiq/master/worker.py b/artiq/master/worker.py index 36d5a202f..1267626ca 100644 --- a/artiq/master/worker.py +++ b/artiq/master/worker.py @@ -74,7 +74,7 @@ class Worker: return None def _get_log_source(self): - return "worker({},{})".format(self.rid, self.filename) + return "worker({},{})".format(self.rid, self.filename if self.filename is not None else "") async def _create_process(self, log_level): if self.ipc is not None: @@ -260,7 +260,8 @@ class Worker: async def build(self, rid, pipeline_name, wd, expid, priority, timeout=15.0): self.rid = rid - self.filename = os.path.basename(expid["file"]) + if "file" in expid: + self.filename = os.path.basename(expid["file"]) await self._create_process(expid["log_level"]) await self._worker_action( {"action": "build", diff --git a/artiq/master/worker_impl.py b/artiq/master/worker_impl.py index 0467a8096..345ee058b 100644 --- a/artiq/master/worker_impl.py +++ b/artiq/master/worker_impl.py @@ -13,6 +13,7 @@ import inspect import logging import traceback from collections import OrderedDict +import types import h5py @@ -129,11 +130,17 @@ class CCB: issue = staticmethod(make_parent_action("ccb_issue")) -def get_experiment(file, class_name): +def get_experiment_from_file(file, class_name): module = tools.file_import(file, prefix="artiq_worker_") return tools.get_experiment(module, class_name) +def get_experiment_from_content(content, class_name): + module = types.ModuleType("expcontent") + exec(content, module.__dict__) + return tools.get_experiment(module, class_name) + + register_experiment = make_parent_action("register_experiment") @@ -271,15 +278,19 @@ def main(): start_time = time.time() rid = obj["rid"] expid = obj["expid"] - if obj["wd"] is not None: - # Using repository - experiment_file = os.path.join(obj["wd"], expid["file"]) - repository_path = obj["wd"] + if "file" in expid: + if obj["wd"] is not None: + # Using repository + experiment_file = os.path.join(obj["wd"], expid["file"]) + repository_path = obj["wd"] + else: + experiment_file = expid["file"] + repository_path = None + setup_diagnostics(experiment_file, repository_path) + exp = get_experiment_from_file(experiment_file, expid["class_name"]) else: - experiment_file = expid["file"] - repository_path = None - setup_diagnostics(experiment_file, repository_path) - exp = get_experiment(experiment_file, expid["class_name"]) + setup_diagnostics("", None) + exp = get_experiment_from_content(expid["content"], expid["class_name"]) device_mgr.virtual_devices["scheduler"].set_run_info( rid, obj["pipeline_name"], expid, obj["priority"]) start_local_time = time.localtime(start_time)