diff --git a/README.md b/README.md index b986e47c..7f006917 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Service generating unique, reproducible numbers to be used for seeding RNG used ## k4run ``` $ k4run --help -usage: k4run [--dry-run] [-v] [-n NUM_EVENTS] [-l] [--gdb] [--interactive-root] [-h] [config_files ...] +usage: k4run [--dry-run] [-v] [-n NUM_EVENTS] [-l] [--gdb] [--interactive-root] [--log-level {VERBOSE,DEBUG,INFO,WARNING,ERROR}] [-i] [-h] [config_files ...] Run job in the Key4hep framework @@ -58,6 +58,9 @@ options: -l, --list Print all the configurable components available in the framework and exit --gdb Attach gdb debugger --interactive-root Run with ROOT in interactive mode (e.g. to see plots) + --log-level {VERBOSE,DEBUG,INFO,WARNING,ERROR} + Set the log (output) level for python and the ApplicationMgr + -i,--interactive Start a Python command loop after reading the configuration files -h, --help show this help message and exit ``` When supplied with a Gaudi steering file `k4run --help file.py` also shows the settable properties of the Gaudi algorithms used in the file. Additionally, it is possible to add further arguments and use them in the steering file by using the Python `argparse.ArgumentParser` shared by `k4run`. diff --git a/doc/k4run-args.md b/doc/k4run-args.md index 5faf9e64..b25fde4e 100644 --- a/doc/k4run-args.md +++ b/doc/k4run-args.md @@ -47,3 +47,27 @@ please refer to its documentation for usage details. Use `parse_known_args()` instead of `parse_args()` so that the normal `k4run` arguments keep working. The `[0]` is necessary because the added arguments will be in the first element of the tuple returned from `parse_known_args`. + +# Interactive python prompt + +The `-i`, `--interactive` option for k4run in k4FWCore starts an interactive +Python command prompt after reading the configuration files. In this mode, +algorithm instances are accessible from the prompt. You can inspect them using +commands like `print(alg)` (where `alg` is the name of the python variable that +holds the algorithm) which displays information about the algorithm instance and +its properties. Most properties can be modified interactively. For example, +after `k4run steering.py -i`: + +``` python +>>> print(alg) +/***** Algorithm ExampleFunctionalProducer/ExampleFunctionalProducer ******************************* +|-OutputLevel = 0 (default: 0) +... +|-ExampleInt = 3 (default: 3) +>>> alg.ExampleInt = 4 +>>> print(alg) +/***** Algorithm ExampleFunctionalProducer/ExampleFunctionalProducer ******************************* +|-OutputLevel = 0 (default: 0) +... +|-ExampleInt = 4 (default: 3) +``` diff --git a/k4FWCore/scripts/k4run b/k4FWCore/scripts/k4run index 83bccec2..2beba83d 100755 --- a/k4FWCore/scripts/k4run +++ b/k4FWCore/scripts/k4run @@ -129,6 +129,21 @@ class DeprecatedStoreTrueAction(argparse.Action): setattr(namespace, self.dest, True) +def startInteractive(vars): + """Start an interactive session""" + import code + + # collect all global and local variables + vars = vars.copy() + + # start the interpreter + code.interact( + local=vars, + banner="Starting interactive Python session. Exit with Ctrl-D to resume the job.", + ) + return + + def main(): # ensure that we (and the subprocesses) use the C standard localization os.environ["LC_ALL"] = "C" @@ -180,6 +195,14 @@ def main(): choices=LOG_LEVELS, ) + parser.add_argument( + "-i", + "--interactive", + action="store_true", + help="Start a Python command loop after reading the configuration files", + default=False, + ) + # Once to parse the files and another time below to have the new parameters # in the namespace since parsing of the file happens when the namespace # has already been filled @@ -202,8 +225,9 @@ def main(): print(f"{item} (from {cfgdb[item]['lib']}), path: {path_to_component}") sys.exit() + config_ns = {} for file in opts[0].config_files: - load_file(file) + config_ns.update(load_file(file)) from Configurables import ApplicationMgr @@ -260,6 +284,9 @@ def main(): from Gaudi.Main import gaudimain + if opts.interactive: + startInteractive(config_ns) + gaudi = gaudimain() if not opts.dry_run: if not opts.interactive_root: diff --git a/python/k4FWCore/utils.py b/python/k4FWCore/utils.py index c5917595..5b743563 100644 --- a/python/k4FWCore/utils.py +++ b/python/k4FWCore/utils.py @@ -93,6 +93,7 @@ def load_file(opt_file: Union[str, os.PathLike]) -> None: check_wrong_imports(code) exec(compile(code, ofile.name, "exec"), namespace) + return namespace _logger = None