hpcflow-new2 0.2.0a37__tar.gz → 0.2.0a38__tar.gz

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.
Files changed (102) hide show
  1. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/PKG-INFO +1 -1
  2. hpcflow_new2-0.2.0a38/hpcflow/_version.py +1 -0
  3. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/__init__.py +12 -4
  4. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/app.py +12 -4
  5. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/config/callbacks.py +18 -0
  6. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/config/config.py +14 -13
  7. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/config/config_file.py +5 -2
  8. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/config/errors.py +3 -2
  9. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/actions.py +1 -1
  10. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/command_files.py +2 -2
  11. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/utils.py +1 -6
  12. hpcflow_new2-0.2.0a38/hpcflow/sdk/log.py +41 -0
  13. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/runtime.py +14 -13
  14. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/jobscript.py +1 -1
  15. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/pyproject.toml +2 -2
  16. hpcflow_new2-0.2.0a37/hpcflow/_version.py +0 -1
  17. hpcflow_new2-0.2.0a37/hpcflow/api/config.py +0 -17
  18. hpcflow_new2-0.2.0a37/hpcflow/sdk/log.py +0 -26
  19. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/README.md +0 -0
  20. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/__init__.py +0 -0
  21. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/__pyinstaller/__init__.py +0 -0
  22. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/__pyinstaller/hook-hpcflow.py +0 -0
  23. /hpcflow_new2-0.2.0a37/hpcflow/api/__init__.py → /hpcflow_new2-0.2.0a38/hpcflow/api.py +0 -0
  24. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/cli.py +0 -0
  25. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/examples.ipynb +0 -0
  26. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/api.py +0 -0
  27. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/config/__init__.py +0 -0
  28. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/config/cli.py +0 -0
  29. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/__init__.py +0 -0
  30. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/commands.py +0 -0
  31. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/element.py +0 -0
  32. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/environment.py +0 -0
  33. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/errors.py +0 -0
  34. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/json_like.py +0 -0
  35. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/loop.py +0 -0
  36. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/object_list.py +0 -0
  37. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/parameters.py +0 -0
  38. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/task.py +0 -0
  39. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/task_schema.py +0 -0
  40. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/test_utils.py +0 -0
  41. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/validation.py +0 -0
  42. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/workflow.py +0 -0
  43. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/core/zarr_io.py +0 -0
  44. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/__init__.py +0 -0
  45. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/config_file_schema.yaml +0 -0
  46. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/config_schema.yaml +0 -0
  47. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/environments_spec_schema.yaml +0 -0
  48. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/files_spec_schema.yaml +0 -0
  49. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/parameters_spec_schema.yaml +0 -0
  50. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/task_schema_spec_schema.yaml +0 -0
  51. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/template_components/__init__.py +0 -0
  52. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/template_components/command_files.yaml +0 -0
  53. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/template_components/environments.yaml +0 -0
  54. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/template_components/parameters.yaml +0 -0
  55. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/template_components/task_schemas.yaml +0 -0
  56. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/data/workflow_spec_schema.yaml +0 -0
  57. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/demo/cli.py +0 -0
  58. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/demo/scripts/__init__.py +0 -0
  59. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/demo/scripts/demo_task_1_generate_t1_infile_1.py +0 -0
  60. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/demo/scripts/demo_task_1_generate_t1_infile_2.py +0 -0
  61. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/demo/scripts/demo_task_1_parse_p3.py +0 -0
  62. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/demo/scripts/generate_t1_file_01.py +0 -0
  63. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/demo/scripts/parse_t1_file_01.py +0 -0
  64. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/helper/__init__.py +0 -0
  65. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/helper/cli.py +0 -0
  66. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/helper/helper.py +0 -0
  67. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/helper/watcher.py +0 -0
  68. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/persistence/__init__.py +0 -0
  69. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/persistence/base.py +0 -0
  70. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/persistence/json.py +0 -0
  71. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/persistence/zarr.py +0 -0
  72. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/scripting/__init__.py +0 -0
  73. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/__init__.py +0 -0
  74. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/schedulers/__init__.py +0 -0
  75. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/schedulers/direct.py +0 -0
  76. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/schedulers/sge.py +0 -0
  77. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/schedulers/slurm.py +0 -0
  78. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/shells/__init__.py +0 -0
  79. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/shells/base.py +0 -0
  80. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/shells/bash.py +0 -0
  81. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/shells/os_version.py +0 -0
  82. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/shells/powershell.py +0 -0
  83. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/submission/submission.py +0 -0
  84. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/sdk/typing.py +0 -0
  85. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_action.py +0 -0
  86. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_app.py +0 -0
  87. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_cli.py +0 -0
  88. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_element.py +0 -0
  89. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_element_set.py +0 -0
  90. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_input_source.py +0 -0
  91. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_input_value.py +0 -0
  92. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_json_like.py +0 -0
  93. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_loop.py +0 -0
  94. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_object_list.py +0 -0
  95. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_resolve_elements.py +0 -0
  96. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_submission.py +0 -0
  97. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_task.py +0 -0
  98. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_task_objective.py +0 -0
  99. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_task_schema.py +0 -0
  100. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_utils.py +0 -0
  101. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/tests/unit/test_workflow.py +0 -0
  102. {hpcflow_new2-0.2.0a37 → hpcflow_new2-0.2.0a38}/hpcflow/viz_demo.ipynb +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hpcflow-new2
3
- Version: 0.2.0a37
3
+ Version: 0.2.0a38
4
4
  Summary: Computational workflow management
5
5
  License: MIT
6
6
  Author: aplowman
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0a38"
@@ -3,15 +3,23 @@ import logging
3
3
  import os
4
4
  import sys
5
5
 
6
- _SDK_CONSOLE_LOG_LEVEL = os.environ.get("HPCFLOW_SDK_CONSOLE_LOG_LEVEL", "ERROR")
6
+ _SDK_CONSOLE_LOG_LEVEL = os.environ.get("HPCFLOW_SDK_CONSOLE_LOG_LEVEL", "WARNING")
7
7
 
8
- SDK_logger = logging.getLogger()
9
- SDK_logger.setLevel("DEBUG")
8
+
9
+ def get_SDK_logger(name=None):
10
+ """Get a logger with prefix of "hpcflow_sdk" instead of "hpcflow.sdk" to ensure the
11
+ handlers of the SDK logger and app logger are distinct."""
12
+ name = ".".join(["hpcflow_sdk"] + (name or __name__).split(".")[2:])
13
+ return logging.getLogger(name)
14
+
15
+
16
+ _SDK_logger = get_SDK_logger()
17
+ _SDK_logger.setLevel("DEBUG")
10
18
 
11
19
  _sh = logging.StreamHandler()
12
20
  _sh.setFormatter(logging.Formatter("%(levelname)s %(name)s: %(message)s"))
13
21
  _sh.setLevel(_SDK_CONSOLE_LOG_LEVEL)
14
- SDK_logger.addHandler(_sh)
22
+ _SDK_logger.addHandler(_sh)
15
23
 
16
24
  if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
17
25
  import multiprocessing
@@ -3,7 +3,6 @@
3
3
  from functools import wraps
4
4
  from importlib import resources, import_module
5
5
  from pathlib import Path
6
- import time
7
6
  from typing import Dict
8
7
  import warnings
9
8
 
@@ -17,7 +16,7 @@ from hpcflow.sdk.persistence import ALL_STORE_FORMATS, DEFAULT_STORE_FORMAT
17
16
  from hpcflow.sdk.submission.shells import ALL_SHELLS
18
17
  from .core.json_like import JSONLike
19
18
  from .core.utils import read_YAML, read_YAML_file
20
- from . import api, SDK_logger
19
+ from . import api, get_SDK_logger
21
20
  from .config import Config
22
21
  from .config.cli import get_config_CLI
23
22
  from .config.errors import ConfigError
@@ -62,7 +61,7 @@ from .helper.cli import get_helper_CLI
62
61
  from .log import AppLog
63
62
  from .runtime import RunTimeInfo
64
63
 
65
- SDK_logger = SDK_logger.getChild(__name__)
64
+ SDK_logger = get_SDK_logger(__name__)
66
65
 
67
66
 
68
67
  class BaseApp:
@@ -139,6 +138,9 @@ class BaseApp:
139
138
  api_method.__doc__ = func.__doc__.format(app_name=name)
140
139
  setattr(self, func.__name__, api_method)
141
140
 
141
+ def __repr__(self):
142
+ return f"{self.__class__.__name__}(name={self.name}, version={self.version})"
143
+
142
144
  @property
143
145
  def template_components(self):
144
146
  if not self.is_template_components_loaded:
@@ -339,15 +341,21 @@ class BaseApp:
339
341
  return self._config
340
342
 
341
343
  def _load_config(self, config_dir, config_invocation_key, **overrides):
342
- self.logger.debug("Loading configuration.")
344
+ self.logger.info("Loading configuration.")
343
345
  self._config = Config(
344
346
  app=self,
345
347
  options=self.config_options,
346
348
  config_dir=config_dir,
347
349
  config_invocation_key=config_invocation_key,
348
350
  logger=self.config_logger,
351
+ variables={"app_name": self.name, "app_version": self.version},
349
352
  **overrides,
350
353
  )
354
+ self.log.update_console_level(self.config.get("log_console_level"))
355
+ self.log.add_file_logger(
356
+ path=self.config.get("log_file_path"),
357
+ level=self.config.get("log_file_level"),
358
+ )
351
359
  self.logger.info(f"Configuration loaded from: {self.config.config_file_path}")
352
360
 
353
361
  def load_config(self, config_dir=None, config_invocation_key=None, **overrides):
@@ -1,9 +1,27 @@
1
1
  """Module that defines built-in callback functions for configuration item values."""
2
2
 
3
3
 
4
+ import re
4
5
  import fsspec
5
6
 
6
7
 
8
+ def callback_vars(config, value):
9
+ """Substitute configuration variables."""
10
+
11
+ def vars_repl(match_obj):
12
+ var_name = match_obj.groups()[0]
13
+ return config._variables[var_name]
14
+
15
+ vars_join = "|".join(list(config._variables.keys()))
16
+ vars_regex = f"\<\<({vars_join})\>\>"
17
+ value = re.sub(
18
+ pattern=vars_regex,
19
+ repl=vars_repl,
20
+ string=value,
21
+ )
22
+ return value
23
+
24
+
7
25
  def callback_file_paths(config, file_path):
8
26
  if isinstance(file_path, list):
9
27
  return [config._resolve_path(i) for i in file_path]
@@ -9,7 +9,7 @@ import uuid
9
9
  from dataclasses import dataclass, field
10
10
  from hashlib import new
11
11
  from pathlib import Path
12
- from typing import Any, Dict, List, Optional, Union
12
+ from typing import Dict, List, Optional, Tuple, Union
13
13
 
14
14
  from fsspec.registry import known_implementations as fsspec_protocols
15
15
  from platformdirs import user_data_dir
@@ -20,6 +20,7 @@ from hpcflow.sdk.log import AppLog
20
20
 
21
21
  from .callbacks import (
22
22
  callback_bool,
23
+ callback_vars,
23
24
  callback_file_paths,
24
25
  set_callback_file_paths,
25
26
  check_load_data_files,
@@ -47,7 +48,7 @@ DEFAULT_CONFIG_FILE = {
47
48
  "config": {
48
49
  "machine": socket.gethostname(),
49
50
  "telemetry": True,
50
- "log_file_path": "logs/app.log",
51
+ "log_file_path": "logs/<<app_name>>_v<<app_version>>.log",
51
52
  "environment_sources": [],
52
53
  "task_schema_sources": [],
53
54
  "command_file_sources": [],
@@ -104,13 +105,14 @@ class Config:
104
105
  logger,
105
106
  uid=None,
106
107
  callbacks=None,
108
+ variables=None,
107
109
  **overrides,
108
110
  ):
109
-
110
111
  self._app = app
111
112
  self._options = options
112
113
  self._overrides = overrides
113
114
  self._logger = logger
115
+ self._variables = variables or {}
114
116
 
115
117
  # Callbacks are run on get:
116
118
  self._get_callbacks = {
@@ -118,7 +120,10 @@ class Config:
118
120
  "environment_sources": (callback_file_paths,),
119
121
  "parameter_sources": (callback_file_paths,),
120
122
  "command_file_sources": (callback_file_paths,),
121
- "log_file_path": (callback_file_paths,),
123
+ "log_file_path": (
124
+ callback_vars,
125
+ callback_file_paths,
126
+ ),
122
127
  "telemetry": (callback_bool,),
123
128
  **(callbacks or {}),
124
129
  }
@@ -318,7 +323,10 @@ class Config:
318
323
  self._logger.debug(
319
324
  f"Invoking `config.get` callback ({cb.__name__!r}) for item {name!r}={value!r}"
320
325
  )
321
- value = cb(self, value)
326
+ try:
327
+ value = cb(self, value)
328
+ except Exception as err:
329
+ raise ConfigItemCallbackError(name, cb, err)
322
330
  return value
323
331
 
324
332
  def get(
@@ -361,10 +369,7 @@ class Config:
361
369
  )
362
370
 
363
371
  if callback:
364
- try:
365
- val = self._get_callback_value(name, val)
366
- except Exception as err:
367
- raise ConfigItemCallbackError(name, err)
372
+ val = self._get_callback_value(name, val)
368
373
 
369
374
  if as_str:
370
375
  if isinstance(val, (list, tuple, set)):
@@ -382,7 +387,6 @@ class Config:
382
387
  return value
383
388
 
384
389
  def set(self, name, value, is_json=False, callback=True):
385
-
386
390
  self._logger.debug(f"Attempting to set config item {name!r} to {value!r}.")
387
391
 
388
392
  if name not in self._configurable_keys:
@@ -396,7 +400,6 @@ class Config:
396
400
  file_val = self._get_callback_value(name, file_val_raw)
397
401
 
398
402
  if callback_val != current_val:
399
-
400
403
  was_in_modified = False
401
404
  was_in_unset = False
402
405
  prev_modified_val = None
@@ -426,7 +429,6 @@ class Config:
426
429
  cb(self, callback_val)
427
430
 
428
431
  except ConfigValidationError as err:
429
-
430
432
  # revert:
431
433
  if modified_updated:
432
434
  if was_in_modified:
@@ -445,7 +447,6 @@ class Config:
445
447
  print(f"value is already: {callback_val!r}")
446
448
 
447
449
  def unset(self, name):
448
-
449
450
  if name not in self._configurable_keys:
450
451
  raise ConfigNonConfigurableError(name=name)
451
452
  if name in self._unset_keys or not self._file.is_item_set(name):
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import copy
4
+ import fnmatch
4
5
  import io
5
6
  import logging
6
7
  import os
@@ -45,7 +46,9 @@ class ConfigFile:
45
46
  for c_name_i, c_dat_i in self.data["configs"].items():
46
47
  is_match = True
47
48
  for match_k, match_v in c_dat_i["invocation"]["match"].items():
48
- if getattr(self.config._app.run_time_info, match_k) != match_v:
49
+ # test for a matching glob pattern:
50
+ k_value = getattr(self.config._app.run_time_info, match_k)
51
+ if not fnmatch.filter(names=[k_value], pat=match_v):
49
52
  is_match = False
50
53
  break
51
54
  if is_match:
@@ -180,7 +183,7 @@ class ConfigFile:
180
183
  raise ConfigDefaultValidationError(err) from None
181
184
 
182
185
  yaml = YAML(typ="rt")
183
- with config_path.open("w") as handle:
186
+ with config_path.open("w", newline="\n") as handle:
184
187
  yaml.dump(default_config, handle)
185
188
 
186
189
  def _load_file_data(self):
@@ -39,9 +39,10 @@ class ConfigFileValidationError(ConfigError):
39
39
 
40
40
 
41
41
  class ConfigItemCallbackError(ConfigError):
42
- def __init__(self, name, err, message=None):
42
+ def __init__(self, name, callback, err, message=None):
43
43
  self.message = message or (
44
- f"Callback function for configuration item {name!r} failed: \n\n{err!r}"
44
+ f"Callback function {callback.__name__!r} for configuration item {name!r} "
45
+ f"failed with exception: \n\n{err!r}"
45
46
  )
46
47
  super().__init__(self.message)
47
48
 
@@ -411,7 +411,7 @@ class ElementActionRun:
411
411
  app_package_name=self.app.package_name,
412
412
  app_name=self.app.name,
413
413
  cfg_dir=self.app.config.config_directory,
414
- cfg_invoc_key=self.app.config._file.invoc_key,
414
+ cfg_invoc_key=self.app.config.config_invocation_key,
415
415
  script_main_func=script_main_func,
416
416
  )
417
417
 
@@ -167,7 +167,7 @@ class InputFileGenerator(JSONLike):
167
167
  app_package_name=self.app.package_name,
168
168
  app_name=self.app.name,
169
169
  cfg_dir=self.app.config.config_directory,
170
- cfg_invoc_key=self.app.config._file.invoc_key,
170
+ cfg_invoc_key=self.app.config.config_invocation_key,
171
171
  script_main_func=script_main_func,
172
172
  file_path=self.input_file.name.value(),
173
173
  )
@@ -259,7 +259,7 @@ class OutputFileParser(JSONLike):
259
259
  app_package_name=self.app.package_name,
260
260
  app_name=self.app.name,
261
261
  cfg_dir=self.app.config.config_directory,
262
- cfg_invoc_key=self.app.config._file.invoc_key,
262
+ cfg_invoc_key=self.app.config.config_invocation_key,
263
263
  script_main_func=script_main_func,
264
264
  param_name=f"outputs.{self.output.typ}",
265
265
  )
@@ -10,10 +10,8 @@ import re
10
10
  import socket
11
11
  import string
12
12
  from datetime import datetime, timezone
13
- from typing import Mapping, List, Union
13
+ from typing import Mapping
14
14
 
15
- import zarr
16
- import numpy.typing as npt
17
15
  from ruamel.yaml import YAML
18
16
  import sentry_sdk
19
17
 
@@ -116,7 +114,6 @@ def group_by_dict_key_values(lst, *keys):
116
114
  grouped = [[lst[0]]]
117
115
  for lst_item in lst[1:]:
118
116
  for group_idx, group in enumerate(grouped):
119
-
120
117
  try:
121
118
  is_vals_equal = all(lst_item[k] == group[0][k] for k in keys)
122
119
 
@@ -162,7 +159,6 @@ def get_in_container(cont, path, cast_indices=False):
162
159
 
163
160
 
164
161
  def set_in_container(cont, path, value, ensure_path=False):
165
-
166
162
  if ensure_path:
167
163
  num_path = len(path)
168
164
  for idx in range(1, num_path):
@@ -291,7 +287,6 @@ def check_in_object_list(spec_name, spec_pos=1, obj_list_pos=2):
291
287
  def decorator(func):
292
288
  @wraps(func)
293
289
  def wrap(*args, **kwargs):
294
-
295
290
  spec = args[spec_pos]
296
291
  obj_list = args[obj_list_pos]
297
292
  if spec[spec_name] not in obj_list:
@@ -0,0 +1,41 @@
1
+ import logging
2
+
3
+
4
+ class AppLog:
5
+ DEFAULT_LOG_CONSOLE_LEVEL = "WARNING"
6
+ DEFAULT_LOG_FILE_LEVEL = "INFO"
7
+
8
+ def __init__(self, app, log_console_level=None):
9
+ self.app = app
10
+ self.logger = logging.getLogger(app.package_name)
11
+ self.logger.setLevel(logging.DEBUG)
12
+ self.console_handler = self._add_console_logger(
13
+ level=log_console_level or AppLog.DEFAULT_LOG_CONSOLE_LEVEL
14
+ )
15
+
16
+ def _add_console_logger(self, level, fmt=None):
17
+ fmt = fmt or "%(levelname)s %(name)s: %(message)s"
18
+ handler = logging.StreamHandler()
19
+ handler.setFormatter(logging.Formatter(fmt))
20
+ handler.setLevel(level)
21
+ self.logger.addHandler(handler)
22
+ return handler
23
+
24
+ def update_console_level(self, new_level):
25
+ if new_level:
26
+ self.console_handler.setLevel(new_level.upper())
27
+
28
+ def add_file_logger(self, path, level=None, fmt=None, max_bytes=None):
29
+ fmt = fmt or f"%(asctime)s %(levelname)s %(name)s: %(message)s"
30
+ level = level or AppLog.DEFAULT_LOG_FILE_LEVEL
31
+ max_bytes = max_bytes or int(10e6)
32
+
33
+ if not path.parent.is_dir():
34
+ self.logger.info(f"Generating log file parent directory: {path.parent!r}")
35
+ path.parent.mkdir(exist_ok=True, parents=True)
36
+
37
+ handler = logging.handlers.RotatingFileHandler(filename=path, maxBytes=max_bytes)
38
+ handler.setFormatter(logging.Formatter(fmt))
39
+ handler.setLevel(level.upper())
40
+ self.logger.addHandler(handler)
41
+ return handler
@@ -41,11 +41,13 @@ class RunTimeInfo(PrettyPrinter):
41
41
  self.is_frozen = is_frozen
42
42
  self.working_dir = os.getcwd()
43
43
  self.logger = logger
44
- self.machine = socket.gethostname()
44
+ self.hostname = socket.gethostname()
45
45
 
46
46
  path_exec = Path(sys.executable)
47
47
  path_argv = Path(sys.argv[0])
48
48
 
49
+ self.in_ipython = False
50
+
49
51
  if self.is_frozen:
50
52
  self.bundle_dir = Path(bundle_dir)
51
53
  self.executable_path = path_argv
@@ -57,6 +59,12 @@ class RunTimeInfo(PrettyPrinter):
57
59
  self.resolved_script_path = path_argv.absolute()
58
60
  self.python_executable_path = path_exec
59
61
 
62
+ try:
63
+ get_ipython
64
+ self.in_ipython = True
65
+ except NameError:
66
+ pass
67
+
60
68
  self.python_version = platform.python_version()
61
69
  self.is_venv = hasattr(sys, "real_prefix") or sys.base_prefix != sys.prefix
62
70
  self.is_conda_venv = "CONDA_PREFIX" in os.environ
@@ -101,6 +109,7 @@ class RunTimeInfo(PrettyPrinter):
101
109
  "is_conda_venv",
102
110
  "executable_name",
103
111
  "python_version",
112
+ "in_ipython",
104
113
  ):
105
114
  sentry_sdk.set_tag(f"rti.{k}", v)
106
115
 
@@ -108,9 +117,10 @@ class RunTimeInfo(PrettyPrinter):
108
117
  out = {
109
118
  "is_frozen": self.is_frozen,
110
119
  "python_version": self.python_version,
111
- "machine": self.machine,
120
+ "hostname": self.hostname,
112
121
  "working_dir": self.working_dir,
113
122
  "invocation_command": self.get_invocation_command(),
123
+ "in_ipython": self.in_ipython,
114
124
  }
115
125
  if self.is_frozen:
116
126
  out.update(
@@ -140,8 +150,7 @@ class RunTimeInfo(PrettyPrinter):
140
150
 
141
151
  def __repr__(self):
142
152
  out = f"{self.__class__.__name__}("
143
- for k, v in self._get_members().items():
144
- out += f"{k}={v!r}"
153
+ out += ", ".join(f"{k}={v!r}" for k, v in self._get_members().items())
145
154
  return out
146
155
 
147
156
  def _set_venv_path(self):
@@ -168,16 +177,8 @@ class RunTimeInfo(PrettyPrinter):
168
177
  if self.is_frozen:
169
178
  return [str(self.resolved_executable_path)]
170
179
  else:
171
- in_ipython = False
172
- try:
173
- get_ipython
174
- in_ipython = True
175
- except NameError:
176
- pass
177
-
178
- if in_ipython:
180
+ if self.in_ipython:
179
181
  app_module = import_module(self.package_name)
180
-
181
182
  CLI_path = Path(*app_module.__path__, "cli.py")
182
183
  command = [str(self.python_executable_path), str(CLI_path)]
183
184
 
@@ -682,7 +682,7 @@ class Jobscript(JSONLike):
682
682
  "env_setup": env_setup,
683
683
  "app_invoc": app_invoc,
684
684
  "config_dir": str(self.app.config.config_directory),
685
- "config_invoc_key": self.app.config._file.invoc_key,
685
+ "config_invoc_key": self.app.config.config_invocation_key,
686
686
  "workflow_path": self.workflow.path,
687
687
  "sub_idx": self.submission.index,
688
688
  "js_idx": self.index,
@@ -1,7 +1,7 @@
1
1
 
2
2
  [tool.poetry]
3
3
  name = "hpcflow-new2"
4
- version = "0.2.0a37"
4
+ version = "0.2.0a38"
5
5
 
6
6
  description = "Computational workflow management"
7
7
  authors = ["aplowman <adam.plowman@manchester.ac.uk>"]
@@ -57,7 +57,7 @@ hook-dirs = "hpcflow.__pyinstaller:get_hook_dirs"
57
57
 
58
58
  [tool.commitizen]
59
59
  name = "cz_conventional_commits"
60
- version = "0.2.0a37"
60
+ version = "0.2.0a38"
61
61
  tag_format = "v$version"
62
62
  version_files = [
63
63
  "pyproject.toml:version",
@@ -1 +0,0 @@
1
- __version__ = "0.2.0a37"
@@ -1,17 +0,0 @@
1
- from hpcflow.config import Config
2
-
3
-
4
- def get_config_file_contents():
5
- return Config.config_file_contents
6
-
7
-
8
- def show_config_file_contents():
9
- print(get_config_file_contents())
10
-
11
-
12
- def show_config():
13
- print(Config.to_string(exclude=["config_file_contents"]))
14
-
15
-
16
- def check():
17
- Config.check_data_files()
@@ -1,26 +0,0 @@
1
- import logging
2
-
3
-
4
- class AppLog:
5
-
6
- DEFAULT_LOG_LEVEL_CONSOLE = "WARNING"
7
- DEFAULT_LOG_LEVEL_FILE = "INFO"
8
- DEFAULT_LOG_FILE_PATH = "app.log"
9
-
10
- def __init__(self, app, log_console_level=None):
11
- self.app = app
12
- self.logger = logging.getLogger(app.package_name)
13
- self.logger.setLevel(logging.DEBUG)
14
- self.console_handler = self._add_console_logger(
15
- level=log_console_level or AppLog.DEFAULT_LOG_LEVEL_CONSOLE
16
- )
17
- # print(f"log_console_level: {log_console_level}")
18
-
19
- def _add_console_logger(self, level, fmt=None):
20
- if not fmt:
21
- fmt = "%(levelname)s %(name)s: %(message)s"
22
- handler = logging.StreamHandler()
23
- handler.setFormatter(logging.Formatter(fmt))
24
- handler.setLevel(level)
25
- self.logger.addHandler(handler)
26
- return handler