sdsstools 1.9.2__py3-none-any.whl → 1.9.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ import pathlib
15
15
  import re
16
16
  from copy import deepcopy
17
17
 
18
- from typing import Any, Dict, Optional, Type, TypeVar, Union
18
+ from typing import Any, Dict, Optional, Type, Union
19
19
 
20
20
  import yaml
21
21
  from typing_extensions import Self
@@ -330,7 +330,7 @@ class Configuration(RecursiveDict):
330
330
  if isinstance(config, dict):
331
331
  return config
332
332
  elif isinstance(config, (str, pathlib.Path)):
333
- return read_yaml_file(config)
333
+ return read_yaml_file(config, return_class=self.__class__)
334
334
  else:
335
335
  raise ValueError("Invalid config of type {}".format(type(config)))
336
336
 
@@ -385,16 +385,37 @@ class Configuration(RecursiveDict):
385
385
  return self
386
386
 
387
387
 
388
- ReturnClass = TypeVar("ReturnClass", bound=dict)
389
-
390
-
391
388
  def read_yaml_file(
392
389
  path: AnyPath,
393
390
  use_extends: bool = True,
394
391
  loader: Any = yaml.FullLoader,
395
- return_class: Type[ReturnClass] = Configuration,
396
- ) -> ReturnClass:
397
- """Read a YAML file and returns a dictionary."""
392
+ return_class: Type | None = None,
393
+ use_variables: bool = True,
394
+ ):
395
+ """Reads a YAML file and returns a dictionary.
396
+
397
+ Parameters
398
+ ----------
399
+ path
400
+ The path to the YAML file or a file-like object.
401
+ use_extends
402
+ If :obj:`True`, looks for lines starting with ``#!extends <basefile>`` and
403
+ merges the current file with the base file. This requires both the current and
404
+ base files to be formatted as dictionaries.
405
+ loader
406
+ The YAML loader to use.
407
+ return_class
408
+ Casts the read YAML data to ``return_class`` if possible.
409
+ use_variables
410
+ If :obj:`True`, looks for a ``variables`` section in the YAML file and
411
+ replaces occurrences of ``$(VARNAME)`` with the value of that variable.
412
+
413
+ Returns
414
+ -------
415
+ yaml_data
416
+ The YAML data as an object of type ``return_class``.
417
+
418
+ """
398
419
 
399
420
  if isinstance(path, (str, pathlib.Path)):
400
421
  fp = open(path, "r")
@@ -402,12 +423,24 @@ def read_yaml_file(
402
423
  fp = path
403
424
 
404
425
  fp.seek(0)
405
- config: Union[ConfigType, None] = yaml.load(fp, Loader=loader)
426
+ yaml_data: Union[ConfigType, None] = yaml.load(fp, Loader=loader)
427
+
428
+ if yaml_data is None or yaml_data == {}:
429
+ return (
430
+ return_class({}) if return_class and issubclass(return_class, dict) else {}
431
+ )
432
+
433
+ if use_variables and isinstance(yaml_data, dict):
434
+ variables = yaml_data.get("variables", {})
435
+
436
+ fp.seek(0)
437
+ file_content = fp.read()
406
438
 
407
- if config is None or config == {}:
408
- return return_class({})
439
+ for var, value in variables.items():
440
+ file_content = re.sub(rf"\$\(\s*{var}\s*\)", str(value), file_content)
441
+ yaml_data = yaml.load(file_content, Loader=loader)
409
442
 
410
- if use_extends:
443
+ if use_extends and isinstance(yaml_data, dict):
411
444
  fp.seek(0)
412
445
  for line in fp.readlines():
413
446
  if line.strip().startswith("#!extends"):
@@ -418,10 +451,10 @@ def read_yaml_file(
418
451
  raise FileExistsError(f"cannot find !extends file {base_file}.")
419
452
 
420
453
  base = read_yaml_file(base_file, use_extends=False, return_class=dict)
421
- new_config = merge_config(base, config)
422
- return return_class(new_config)
454
+ new_config = merge_config(base, yaml_data)
455
+ return return_class(new_config) if return_class else new_config
423
456
 
424
457
  elif line.strip().startswith("#") or line.strip() == "":
425
458
  continue
426
459
 
427
- return return_class(config)
460
+ return return_class(yaml_data) if return_class else yaml_data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sdsstools
3
- Version: 1.9.2
3
+ Version: 1.9.4
4
4
  Summary: Small tools for SDSS products
5
5
  Project-URL: Homepage, https://github.com/sdss/sdsstools
6
6
  Project-URL: Repository, https://github.com/sdss/sdsstools
@@ -141,6 +141,25 @@ you can use `read_yaml_file` to parse the result
141
141
 
142
142
  The path to the base file must be absolute or relative to the location of the file to be extended.
143
143
 
144
+ ### Using variables in YAML files
145
+
146
+ `read_yaml_file` and `get_config` support the use of variables in YAML files. A special section `variables` can be defined at the top level of the YAML file, and any `$(VAR)` string in the file will be replaced by the value of the variable. For example
147
+
148
+ ```yaml
149
+ variables:
150
+ greeting: hello
151
+
152
+ cat1:
153
+ key1: $(greeting) world
154
+ ```
155
+
156
+ ```python
157
+ >>> read_yaml_file('file.yaml', use_variables=True)
158
+ {'cat1': {'key1': 'hello world'}, 'variables': {'greeting': 'hello'}}
159
+ ```
160
+
161
+ Environment variables can also be used by including a `${ENV_VAR}` string in the YAML file, which will be replaced with the content of the `$ENV_VAR` environment variable.
162
+
144
163
  ### The `Configuration` class
145
164
 
146
165
  By default `get_config()` and `read_yaml_file()` return a `Configuration` instance. For the most part a `Configuration` object is the same as a dictionary, and it can be used as such. It has two main differences:
@@ -1,7 +1,7 @@
1
1
  sdsstools/__init__.py,sha256=G8u94gRu4THFJvptEITykK0Q_3jHqhQY9p01j9QCQH4,744
2
2
  sdsstools/_tasks.py,sha256=U7fvxk9IExzE6voNjio2QqRTw-H9Upu1d0zKQlQFs5I,4263
3
3
  sdsstools/cli.py,sha256=e4BS4b1cJThCj5tytajr_VMUvmVP1xYq_r90EtEscg8,1348
4
- sdsstools/configuration.py,sha256=i2KkrjS5hE4o-d4VDwzWZKacxO-rNKewgDKoeqE3uuQ,13417
4
+ sdsstools/configuration.py,sha256=y_nmF_TN_o68ib2YIpdypHfwggvBpjz_dwKjNd0jzc8,14673
5
5
  sdsstools/daemonizer.py,sha256=TMzq3RgD0KqY6jmeeJASlCsw7CjDpLmF8QrENaxCHBU,7796
6
6
  sdsstools/logger.py,sha256=nGAC_hDK0dWVMAS-a-vBsw0oSCxAwx-ZsJUE5GMzILs,18382
7
7
  sdsstools/metadata.py,sha256=LZWO0KI-gVvzdJNIaVlIn4bBBHxbglU8BLW-MdlxOFA,3268
@@ -15,8 +15,8 @@ sdsstools/_vendor/toml/decoder.py,sha256=hWUJ3UQ43MGVER35rSUUj7Hdh85Vat8Od_B4HeF
15
15
  sdsstools/_vendor/toml/encoder.py,sha256=OBRwH2tRUhRI8nJgKBwKbkyQnBAuhcUf60r3wykjPco,9989
16
16
  sdsstools/_vendor/toml/ordered.py,sha256=aW5woa5xOqR4BjIz9t10_lghxyhF54KQ7FqUNVv7WJ0,334
17
17
  sdsstools/_vendor/toml/tz.py,sha256=8TAiXrTqU08sE0ruz2TXH_pFY2rlwNKE47MSE4rDo8Y,618
18
- sdsstools-1.9.2.dist-info/METADATA,sha256=9bnqmiMtQf6NMdheCumFfHFjGL9YvAvRzPnUqjceNPw,15027
19
- sdsstools-1.9.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- sdsstools-1.9.2.dist-info/entry_points.txt,sha256=H1UefQFMHyzmGcdNib_qxiOfFSx3351wr4c2ax0PJbQ,87
21
- sdsstools-1.9.2.dist-info/licenses/LICENSE.md,sha256=_7dAUQQ5Ph_x1hcFXhi9aHBcqq9H11zco12eO4B3Cyg,1504
22
- sdsstools-1.9.2.dist-info/RECORD,,
18
+ sdsstools-1.9.4.dist-info/METADATA,sha256=MXPK8lZT3NZeTdKjO10nznLWlh0ZPqPtlZG3J3wE9hU,15701
19
+ sdsstools-1.9.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
+ sdsstools-1.9.4.dist-info/entry_points.txt,sha256=H1UefQFMHyzmGcdNib_qxiOfFSx3351wr4c2ax0PJbQ,87
21
+ sdsstools-1.9.4.dist-info/licenses/LICENSE.md,sha256=_7dAUQQ5Ph_x1hcFXhi9aHBcqq9H11zco12eO4B3Cyg,1504
22
+ sdsstools-1.9.4.dist-info/RECORD,,