Coverage for python/lsst/ctrl/bps/parsl/configuration.py: 35%
43 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-20 09:31 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-07-20 09:31 +0000
1import logging
2import os
3from typing import Any, Literal, TypeVar, overload
5from lsst.ctrl.bps import BpsConfig
7__all__ = (
8 "get_bps_config_value",
9 "get_workflow_name",
10 "get_workflow_filename",
11 "set_parsl_logging",
12)
15T = TypeVar("T")
18# Default provided, not required
19@overload
20def get_bps_config_value(
21 config: BpsConfig,
22 key: str,
23 dataType: type[T],
24 default: T,
25) -> T:
26 ...
29# No default, but required
30@overload
31def get_bps_config_value(
32 config: BpsConfig,
33 key: str,
34 dataType: type[T],
35 default: T | None = None,
36 *,
37 required: Literal[True],
38) -> T:
39 ...
42# No default, not required
43@overload
44def get_bps_config_value(
45 config: BpsConfig,
46 key: str,
47 dataType: type[T],
48 default: T | None = None,
49) -> T | None:
50 ...
53def get_bps_config_value(
54 config: BpsConfig,
55 key: str,
56 dataType: type[T],
57 default: T | None = None,
58 *,
59 required: bool = False,
60) -> T | None:
61 """Get a value from the BPS configuration
63 I find this more useful than ``BpsConfig.__getitem__`` or
64 ``BpsConfig.get``.
66 Parameters
67 ----------
68 config : `BpsConfig`
69 Configuration from which to retrieve value.
70 key : `str`
71 Key name.
72 dataType : `type`
73 We require that the returned value have this type.
74 default : optional
75 Default value to be provided if ``key`` doesn't exist in the
76 ``config``. A default value of `None` means that there is no default.
77 required : `bool`, optional
78 If ``True``, the returned value may come from the configuration or from
79 the default, but it may not be `None`.
81 Returns
82 -------
83 value
84 Value for ``key`` in the `config`` if it exists, otherwise ``default``,
85 if provided.
87 Raises
88 ------
89 KeyError
90 If ``key`` is not in ``config`` and no default is provided but a value
91 is ``required``.
92 RuntimeError
93 If the value is not set or is of the wrong type.
94 """
95 options: dict[str, Any] = dict(expandEnvVars=True, replaceVars=True, required=required)
96 if default is not None:
97 options["default"] = default
98 found, value = config.search(key, options)
99 if not found and default is None:
100 if required:
101 raise KeyError(f"No value found for {key} and no default provided")
102 return None
103 if not isinstance(value, dataType):
104 raise RuntimeError(f"Configuration value {key}={value} is not of type {dataType}")
105 return value
108def get_workflow_name(config: BpsConfig) -> str:
109 """Get name of this workflow
111 The workflow name is constructed by joining the ``project`` and
112 ``campaign`` (if set; otherwise ``operator``) entries in the BPS
113 configuration.
115 Parameters
116 ----------
117 config : `BpsConfig`
118 BPS configuration.
120 Returns
121 -------
122 name : `str`
123 Workflow name.
124 """
125 project = get_bps_config_value(config, "project", str, "bps")
126 campaign = get_bps_config_value(
127 config, "campaign", str, get_bps_config_value(config, "operator", str, required=True)
128 )
129 return f"{project}.{campaign}"
132def get_workflow_filename(out_prefix: str) -> str:
133 """Get filename for persisting workflow
135 Parameters
136 ----------
137 out_prefix : `str`
138 Directory which should contain workflow file.
140 Returns
141 -------
142 filename : `str`
143 Filename for persisting workflow.
144 """
145 return os.path.join(out_prefix, "parsl_workflow.pickle")
148def set_parsl_logging(config: BpsConfig) -> int:
149 """Set parsl logging levels
151 The logging level is set by the ``parsl.log_level`` entry in the BPS
152 configuration.
154 Parameters
155 ----------
156 config : `BpsConfig`
157 BPS configuration.
159 Returns
160 -------
161 level : `int`
162 Logging level applied to ``parsl`` loggers.
163 """
164 level_name = get_bps_config_value(config, ".parsl.log_level", str, "INFO")
165 if level_name not in ("CRITICAL", "DEBUG", "ERROR", "FATAL", "INFO", "WARN"):
166 raise RuntimeError(f"Unrecognised parsl.log_level: {level_name}")
167 level: int = getattr(logging, level_name)
168 for name in logging.root.manager.loggerDict:
169 if name.startswith("parsl"):
170 logging.getLogger(name).setLevel(level)
171 logging.getLogger("database_manager").setLevel(logging.INFO)
172 return level