environment, artiq_run: introduce interactive arguments

pull/2352/head
Sébastien Bourdeauducq 2024-02-26 19:30:31 +08:00
parent a0450555e2
commit 7688f380b1
3 changed files with 53 additions and 2 deletions

View File

@ -41,6 +41,7 @@ Highlights:
+ CTRL+SHIFT+C cascades experiment windows
* Datasets can now be associated with units and scale factors, and displayed accordingly in the dashboard
including applets, like widgets such as ``NumberValue`` already did in earlier ARTIQ versions.
* Experiments can now request arguments interactively from the user at any time.
* Persistent datasets are now stored in a LMDB database for improved performance.
* Python's built-in types (such as ``float``, or ``List[...]``) can now be used in type annotations on
kernel functions.

View File

@ -13,7 +13,7 @@ import h5py
from llvmlite import binding as llvm
from sipyco import common_args
from sipyco import common_args, pyon
from artiq import __version__ as artiq_version
from artiq.language.environment import EnvExperiment, ProcessArgumentManager
@ -166,9 +166,28 @@ def get_argparser(with_file=True):
return parser
class ArgumentManager(ProcessArgumentManager):
def get_interactive(self, interactive_arglist):
result = dict()
for key, processor, group, tooltip in interactive_arglist:
success = False
while not success:
user_input = input("{}:{} (group={}, tooltip={}): ".format(
key, type(processor).__name__, group, tooltip))
try:
user_input_deser = pyon.decode(user_input)
value = processor.process(user_input_deser)
except:
logger.error("failed to process user input, retrying", exc_info=True)
else:
success = True
result[key] = value
return result
def _build_experiment(device_mgr, dataset_mgr, args):
arguments = parse_arguments(args.arguments)
argument_mgr = ProcessArgumentManager(arguments)
argument_mgr = ArgumentManager(arguments)
managers = (device_mgr, dataset_mgr, argument_mgr, {})
if hasattr(args, "file"):
is_tar = tarfile.is_tarfile(args.file)

View File

@ -1,5 +1,7 @@
from collections import OrderedDict
from inspect import isclass
from contextlib import contextmanager
from types import SimpleNamespace
from sipyco import pyon
@ -212,6 +214,9 @@ class TraceArgumentManager:
self.requested_args[key] = processor, group, tooltip
return None
def get_interactive(self, interactive_arglist):
raise NotImplementedError
class ProcessArgumentManager:
def __init__(self, unprocessed_arguments):
@ -233,6 +238,10 @@ class ProcessArgumentManager:
raise AttributeError("Supplied argument(s) not queried in experiment: " +
", ".join(unprocessed))
def get_interactive(self, interactive_arglist):
raise NotImplementedError
class HasEnvironment:
"""Provides methods to manage the environment of an experiment (arguments,
devices, datasets)."""
@ -322,6 +331,28 @@ class HasEnvironment:
kernel_invariants = getattr(self, "kernel_invariants", set())
self.kernel_invariants = kernel_invariants | {key}
@contextmanager
def interactive(self):
"""Request arguments from the user interactively.
This context manager returns a namespace object on which the method
`setattr_argument` should be called, with the usual semantics.
When the context manager terminates, the experiment is blocked
and the user is presented with the requested argument widgets.
After the user enters values, the experiment is resumed and
the namespace contains the values of the arguments."""
interactive_arglist = []
namespace = SimpleNamespace()
def setattr_argument(key, processor=None, group=None, tooltip=None):
interactive_arglist.append((key, processor, group, tooltip))
namespace.setattr_argument = setattr_argument
yield namespace
del namespace.setattr_argument
argdict = self.__argument_mgr.get_interactive(interactive_arglist)
for key, value in argdict.items():
setattr(namespace, key, value)
def get_device_db(self):
"""Returns the full contents of the device database."""
return self.__device_mgr.get_device_db()