socx-cli 0.13.4__tar.gz → 0.13.6__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 (113) hide show
  1. {socx_cli-0.13.4 → socx_cli-0.13.6}/PKG-INFO +1 -1
  2. {socx_cli-0.13.4 → socx_cli-0.13.6}/pyproject.toml +1 -1
  3. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/_config.py +12 -57
  4. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/_settings.py +149 -21
  5. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/_paths.py +1 -1
  6. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/enums.py +0 -1
  7. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/paths.py +6 -0
  8. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/io/log.py +1 -4
  9. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/logging.yaml +2 -3
  10. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/regression.yaml +0 -19
  11. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/rich_click.yaml +2 -3
  12. {socx_cli-0.13.4 → socx_cli-0.13.6}/.gitignore +0 -0
  13. {socx_cli-0.13.4 → socx_cli-0.13.6}/LICENSE +0 -0
  14. {socx_cli-0.13.4 → socx_cli-0.13.6}/README.md +0 -0
  15. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/__init__.py +0 -0
  16. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/__main__.py +0 -0
  17. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/__init__.py +0 -0
  18. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/_cli.py +0 -0
  19. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/_jinja.py +0 -0
  20. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/callbacks.py +0 -0
  21. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/cfg.py +0 -0
  22. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/cli.py +0 -0
  23. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/params.py +0 -0
  24. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/cli/types.py +0 -0
  25. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/__init__.py +0 -0
  26. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/converters.py +0 -0
  27. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/encoders.py +0 -0
  28. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/formatters.py +0 -0
  29. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/serializers.py +0 -0
  30. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/config/validators.py +0 -0
  31. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/__init__.py +0 -0
  32. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/encoder.py +0 -0
  33. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/funcs.py +0 -0
  34. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/metadata.py +0 -0
  35. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/schema/__init__.py +0 -0
  36. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/schema/git/__init__.py +0 -0
  37. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/schema/git/git.py +0 -0
  38. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/schema/git/manifest.py +0 -0
  39. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/schema/plugin.py +0 -0
  40. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/schema/types.py +0 -0
  41. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/core/serializer.py +0 -0
  42. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/git/__init__.py +0 -0
  43. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/git/_git.py +0 -0
  44. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/git/_manifest.py +0 -0
  45. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/git/_ssh.py +0 -0
  46. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/io/__init__.py +0 -0
  47. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/io/console.py +0 -0
  48. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/io/decorators.py +0 -0
  49. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/__init__.py +0 -0
  50. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/mixins/__init__.py +0 -0
  51. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/mixins/proxy.py +0 -0
  52. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/mixins/uid.py +0 -0
  53. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/singleton/__init__.py +0 -0
  54. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/singleton/singleton.py +0 -0
  55. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/visitor/__init__.py +0 -0
  56. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/visitor/protocol.py +0 -0
  57. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/patterns/visitor/traversal.py +0 -0
  58. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/regression/__init__.py +0 -0
  59. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/regression/progress.py +0 -0
  60. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/regression/regression.py +0 -0
  61. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/regression/status.py +0 -0
  62. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/regression/test.py +0 -0
  63. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/regression/validator.py +0 -0
  64. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/regression/visitor.py +0 -0
  65. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/cli.yaml +0 -0
  66. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/console.yaml +0 -0
  67. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/git.yaml +0 -0
  68. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/plugins.yaml +0 -0
  69. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/settings/settings.yaml +0 -0
  70. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/static/sql/socx.sql +0 -0
  71. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/utils/__init__.py +0 -0
  72. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx/utils/decorators.py +0 -0
  73. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/config/__init__.py +0 -0
  74. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/config/_config.py +0 -0
  75. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/config/edit.py +0 -0
  76. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/__init__.py +0 -0
  77. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/arguments.py +0 -0
  78. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/callbacks.py +0 -0
  79. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/cli.py +0 -0
  80. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/manifest.py +0 -0
  81. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/renderables.py +0 -0
  82. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/summary.py +0 -0
  83. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/git/utils.py +0 -0
  84. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/plugin/__init__.py +0 -0
  85. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/plugin/example.py +0 -0
  86. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/plugin/schema.py +0 -0
  87. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/regression/__init__.py +0 -0
  88. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/regression/_run.py +0 -0
  89. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/regression/callbacks.py +0 -0
  90. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/regression/cli.py +0 -0
  91. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/regression/run.py +0 -0
  92. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/regression/tui.py +0 -0
  93. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/version/__init__.py +0 -0
  94. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_plugins/version/__main__.py +0 -0
  95. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/__init__.py +0 -0
  96. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/__init__.py +0 -0
  97. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/__main__.py +0 -0
  98. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/app.py +0 -0
  99. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/bindings/__init__.py +0 -0
  100. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/bindings/vim/__init__.py +0 -0
  101. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/bindings/vim/mode.py +0 -0
  102. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/bindings/vim/vim.py +0 -0
  103. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/containers.py +0 -0
  104. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/details.py +0 -0
  105. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/dialog.py +0 -0
  106. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/mixins/__init__.py +0 -0
  107. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/mixins/composable.py +0 -0
  108. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/preview.py +0 -0
  109. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/table.py +0 -0
  110. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/tree.py +0 -0
  111. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/regression/widget.py +0 -0
  112. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/static/tcss/regression/app.tcss +0 -0
  113. {socx_cli-0.13.4 → socx_cli-0.13.6}/socx_tui/static/tcss/regression/preview.tcss +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: socx-cli
3
- Version: 0.13.4
3
+ Version: 0.13.6
4
4
  Summary: System on chip verification and tooling infrastructure.
5
5
  Project-URL: Issues, https://github.com/sagikimhi/socx-cli/issues
6
6
  Project-URL: Homepage, https://sagikimhi.dev/socx-cli
@@ -29,7 +29,7 @@ socx = 'socx.__main__:main'
29
29
  [project]
30
30
  name = "socx-cli"
31
31
  readme = "README.md"
32
- version = "0.13.4"
32
+ version = "0.13.6"
33
33
  license = "Apache-2.0"
34
34
  authors = [{ name = "Sagi Kimhi", email = "sagi.kim5@gmail.com" }]
35
35
  maintainers = [{ name = "Sagi Kimhi", email = "sagi.kim5@gmail.com" }]
@@ -8,9 +8,7 @@ import contextvars as ctx
8
8
  from textwrap import dedent
9
9
  from typing import Any
10
10
  from pathlib import Path
11
- from collections import ChainMap
12
11
 
13
- from dynaconf.utils import ensure_a_list
14
12
  from werkzeug.local import LocalProxy
15
13
 
16
14
  from socx.config import converters
@@ -109,70 +107,27 @@ def get_settings(
109
107
  path: str | Path | None = None,
110
108
  /,
111
109
  user_overrides: bool = False,
112
- local_overrides: bool = False,
110
+ project_overrides: bool = False,
113
111
  extra_overrides: list[str | Path] | None = None,
114
112
  **kwargs: Any,
115
113
  ) -> Settings:
116
114
  """Create a configured ``Settings`` instance, including overrides."""
117
- from socx.core import paths
118
- from socx.core import metadata
119
- from socx.config.serializers import ModuleSerializer
120
-
121
- if isinstance(path, str):
122
- path = Path(path)
123
-
124
- includes = []
125
- settings_file = ensure_a_list(path or paths.APP_CONFIG_FILE)
126
-
127
- if user_overrides:
128
- includes.extend(get_user_config_files())
129
-
130
- if local_overrides:
131
- includes.extend(get_local_config_files())
132
-
133
- if extra_overrides:
134
- includes.extend(
135
- Path(p) if isinstance(p, str) else p for p in extra_overrides
136
- )
137
-
138
- root = includes[-1].parent if includes else Path.cwd()
139
- settings_kwargs = dict(
140
- preload=settings_file, root_path=root, settings_file=includes
141
- )
142
- kwargs = dict(
143
- ChainMap(
144
- kwargs,
145
- settings_kwargs,
146
- ModuleSerializer.serialize(paths),
147
- ModuleSerializer.serialize(metadata),
148
- ),
115
+ return Settings(
116
+ user_overrides=user_overrides,
117
+ project_overrides=project_overrides,
118
+ **kwargs,
149
119
  )
150
- rv = Settings(**kwargs)
151
- return rv
152
120
 
153
121
 
154
122
  converters.init()
155
-
156
123
  _tokens = []
157
-
158
124
  _settings_cv: ctx.ContextVar[Settings] = ctx.ContextVar("settings")
159
-
160
- _default_settings: Settings = get_settings()
161
-
162
- try:
163
- _user_settings: Settings = get_settings(user_overrides=True)
164
- except Exception:
165
- _user_settings = _default_settings
166
-
167
- try:
168
- _local_settings: Settings = get_settings(
169
- user_overrides=True, local_overrides=True
170
- )
171
- except Exception:
172
- _local_settings = _default_settings
173
- _local_settings.update(_user_settings)
174
-
175
- settings: SettingsProxy = LocalProxy( # type: ignore[assignment]
125
+ _default_settings: Settings = Settings(
126
+ project_overrides=False, user_overrides=False
127
+ )
128
+ _user_settings: Settings = Settings(project_overrides=False)
129
+ _local_settings: Settings = Settings()
130
+ settings: SettingsProxy = LocalProxy(
176
131
  _settings_cv,
177
132
  unbound_message=dedent("""
178
133
  Working outside of application context.
@@ -180,7 +135,7 @@ settings: SettingsProxy = LocalProxy( # type: ignore[assignment]
180
135
  Attempted to use functionality that expected a current application to
181
136
  be set. To solve this, set up an app context.
182
137
  """),
183
- )
138
+ ) # ty:ignore[invalid-assignment]
184
139
 
185
140
  if "--no-configure" in sys.argv or "-NC" in sys.argv:
186
141
  _tokens.append(_settings_cv.set(_default_settings))
@@ -9,14 +9,16 @@ from collections import ChainMap
9
9
  from collections.abc import Callable
10
10
 
11
11
  from dynaconf import LazySettings, get_history
12
+ from dynaconf.loaders import env_loader
12
13
  from dynaconf.base import SourceMetadata, ensure_a_list
13
14
  from dynaconf.utils.boxing import DynaBox
14
15
  from dynaconf.utils.inspect import get_debug_info, _get_data_by_key
15
16
  from dynaconf.utils.parse_conf import unparse_conf_data
16
17
  from pydantic_core import to_jsonable_python
17
18
 
18
- from socx.config.serializers import SettingsSerializer
19
-
19
+ from socx.core import paths, metadata
20
+ from socx.core.enums import SettingsFormat
21
+ from socx.config.serializers import SettingsSerializer, ModuleSerializer
20
22
 
21
23
  logger = logging.getLogger(__name__)
22
24
 
@@ -29,8 +31,10 @@ VT = TypeVar("VT")
29
31
  SETTINGS_DEFAULTS: dict[str, Any] = dict(
30
32
  env="default",
31
33
  envvar="SOCX_SETTINGS_PATH",
34
+ preload=[paths.APP_CONFIG_FILE],
32
35
  encoding="utf-8",
33
36
  auto_cast=True,
37
+ root_path=paths.PROJECT_ROOT_DIR,
34
38
  load_dotenv=True,
35
39
  environments=False,
36
40
  dotted_lookup=True,
@@ -46,15 +50,51 @@ SETTINGS_DEFAULTS: dict[str, Any] = dict(
46
50
 
47
51
 
48
52
  class Settings(LazySettings):
49
- """Singleton settings instance of loaded `socx` configurations."""
53
+ """Application settings class."""
50
54
 
51
55
  def __init__(self, wrapped=None, **kwargs: Any) -> None:
52
- kwargs = dict(ChainMap(kwargs, SETTINGS_DEFAULTS))
56
+ default_kwargs = SETTINGS_DEFAULTS
57
+ if kwargs.get("project_overrides", True):
58
+ project_files = self.get_project_config_files()
59
+ if project_files:
60
+ default_kwargs["settings_file"] = [project_files[-1]]
61
+ kwargs = dict(
62
+ ChainMap(
63
+ kwargs,
64
+ default_kwargs,
65
+ ModuleSerializer.serialize(paths),
66
+ ModuleSerializer.serialize(metadata),
67
+ )
68
+ )
53
69
  LazySettings.__init__(self, wrapped=wrapped, **kwargs)
54
- if hasattr(self, "dynaconf_include"):
55
- for file in self.dynaconf_include:
56
- if file not in self.loaded_files:
57
- self.load_file(path=file)
70
+
71
+ def _setup(self) -> None:
72
+ super()._setup()
73
+ self._load_overrides()
74
+
75
+ def _load_overrides(self) -> None:
76
+ user_overrides = self.get("user_overrides", True)
77
+ project_overrides = self.get("project_overrides", True)
78
+ includes = self.get_settings_overrides(
79
+ user_overrides=user_overrides, project_overrides=project_overrides
80
+ )
81
+
82
+ if includes:
83
+ for file in includes:
84
+ logger.debug("loading settings overrides from: '%s'")
85
+ try:
86
+ self.load_file(file)
87
+ except Exception:
88
+ logger.exception(
89
+ "Failed to load settings file: '%s'" % str(file)
90
+ )
91
+ continue
92
+ else:
93
+ logger.debug("loaded settings overrides from: '%s'")
94
+
95
+ last_loader = self.loaders and self.loaders[-1]
96
+ if last_loader is env_loader:
97
+ env_loader.load(self._store)
58
98
 
59
99
  def __contains__(self, key):
60
100
  return self.exists(key) or (
@@ -77,19 +117,6 @@ class Settings(LazySettings):
77
117
  """Get the root path of the current settings instance."""
78
118
  return Path(self._root_path)
79
119
 
80
- @root.setter
81
- def root(self, value: str | Path) -> None:
82
- if isinstance(value, str):
83
- value = Path(value)
84
-
85
- if value.is_file() and value.exists():
86
- self.set(
87
- "SETTINGS_FILE_FOR_DYNACONF",
88
- value,
89
- loader_identifier="init_settings_module",
90
- )
91
- self.reload()
92
-
93
120
  @property
94
121
  def history(self) -> tuple[dict[str, Any], ...]:
95
122
  """Get the history of this instance.
@@ -274,6 +301,107 @@ class Settings(LazySettings):
274
301
  rv = cls._transform(rv, fn, skip_values=skip_values)
275
302
  return rv
276
303
 
304
+ @classmethod
305
+ def get_settings_overrides(
306
+ cls, user_overrides: bool = True, project_overrides: bool = True
307
+ ) -> list[Path]:
308
+ """Get a list of project settings file overrides."""
309
+ overrides = []
310
+ if user_overrides:
311
+ overrides.extend(cls.get_user_config_files())
312
+ if project_overrides:
313
+ overrides.extend(cls.get_project_config_files())
314
+ return overrides
315
+
316
+ @classmethod
317
+ def get_project_config_files(cls) -> list[Path]:
318
+ """Get a list of local settings file paths found in parent folders.
319
+
320
+ Description:
321
+ ------------
322
+ After initialization, `socx` searches parent directories for any file
323
+ named '.socx.<ext>' where <ext> is any file extension supported by the
324
+ `socx` configuration system.
325
+
326
+ Local configuration overrides are any files named '.socx.<ext>' which
327
+ found in any of the parent directories of the current working
328
+ directory, where <ext> may be any one of: '.yaml', '.yml', '.json', or
329
+ '.toml'.
330
+
331
+ For reference, it works similar to git:
332
+
333
+ 1. first, default app configurations are loaded to initialize to
334
+ app's core functionality.
335
+
336
+ 2. second, global user configurations are loaded from the user's
337
+ config directory, determined according to the 'XDG Base Directory
338
+ Specification'
339
+ (https://specifications.freedesktop.org/basedir-spec).
340
+
341
+ 3. last, a search for local configuration files is done, matching
342
+ (and loading) any configuration files named '.socx.yaml' found in
343
+ any of the parent directories starting the search at the current
344
+ working
345
+ directory.
346
+
347
+ Returns:
348
+ --------
349
+ An ordered list of `Path` objects pointing at configuration files to
350
+ be loaded in that exact order to preserve the described overrides
351
+ order.
352
+
353
+ """
354
+ from socx.core.paths import LOCAL_CONFIG_FILE, LOCAL_CONFIG_FILENAME
355
+
356
+ rv = []
357
+ for parent in LOCAL_CONFIG_FILE.parents:
358
+ cfg_file = parent / LOCAL_CONFIG_FILENAME
359
+ for member in SettingsFormat:
360
+ rv.extend(
361
+ cfg_file.with_suffix(ext)
362
+ for ext in member.extensions
363
+ if cfg_file.with_suffix(ext).is_file()
364
+ )
365
+ return rv
366
+
367
+ @classmethod
368
+ def get_user_config_files(cls) -> list[Path]:
369
+ """Get a list of all local config files found in parent folders.
370
+
371
+ Description:
372
+ ------------
373
+ User configuration files are any configuration files who's format is
374
+ supported and are located under the XDG_CONFIG_HOME directory.
375
+
376
+ Local configuration overrides are any files named '.socx.yaml' which
377
+ found in any of the parent directories of the current working
378
+ directory.
379
+
380
+ For reference, it works similar to git:
381
+
382
+ 1. first, default app configurations are loaded to initialize to
383
+ app's core functionality.
384
+
385
+ 2. second, global user configurations are loaded from the user's
386
+ config directory, determined according to the 'XDG Base Directory
387
+ Specification'
388
+ (https://specifications.freedesktop.org/basedir-spec).
389
+
390
+ 3. last, a search for local configuration files is done, matching
391
+ (and loading) any configuration files named '.socx.yaml' found in
392
+ any of the parent directories starting the search at the current
393
+ working directory.
394
+
395
+ Returns:
396
+ --------
397
+ An ordered list of `Path` objects pointing at configuration files to be
398
+ loaded in that exact order to preserve the described overrides order.
399
+
400
+ """
401
+ from socx.core.paths import USER_CONFIG_FILE
402
+
403
+ return [USER_CONFIG_FILE] if USER_CONFIG_FILE.exists() else []
404
+
277
405
  @classmethod
278
406
  def _transform(
279
407
  cls,
@@ -36,7 +36,7 @@ def find_project_root(start_path: Path) -> Path:
36
36
 
37
37
  if not start_path.exists():
38
38
  err = f"Path '{start_path}' does not exist."
39
- raise OSError(err)
39
+ raise FileNotFoundError(err)
40
40
 
41
41
  if start_path.is_file():
42
42
  start_path = start_path.parent
@@ -25,7 +25,6 @@ class SettingsFormat(AutoNumber):
25
25
  Python = ".python"
26
26
 
27
27
  def __init__(self, extension: str, *extensions: str) -> None:
28
- self.extension = extension
29
28
  self.extensions = [extension, *extensions]
30
29
 
31
30
  @classmethod
@@ -19,8 +19,10 @@ __all__ = (
19
19
  "USER_RUNTIME_DIR",
20
20
  "USER_LOG_FILENAME",
21
21
  "USER_CONFIG_FILENAME",
22
+ "USER_ROTATING_LOG_FILENAME",
22
23
  "USER_LOG_FILE",
23
24
  "USER_CONFIG_FILE",
25
+ "USER_ROTATING_LOG_FILE",
24
26
  # Local Paths
25
27
  "LOCAL_CONFIG_FILENAME",
26
28
  "LOCAL_CONFIG_FILE",
@@ -46,8 +48,12 @@ from socx.core._paths import USER_CONFIG_DIR as USER_CONFIG_DIR
46
48
  from socx.core._paths import USER_RUNTIME_DIR as USER_RUNTIME_DIR
47
49
  from socx.core._paths import USER_LOG_FILENAME as USER_LOG_FILENAME
48
50
  from socx.core._paths import USER_CONFIG_FILENAME as USER_CONFIG_FILENAME
51
+ from socx.core._paths import (
52
+ USER_ROTATING_LOG_FILENAME as USER_ROTATING_LOG_FILENAME,
53
+ )
49
54
  from socx.core._paths import USER_LOG_FILE as USER_LOG_FILE
50
55
  from socx.core._paths import USER_CONFIG_FILE as USER_CONFIG_FILE
56
+ from socx.core._paths import USER_ROTATING_LOG_FILE as USER_ROTATING_LOG_FILE
51
57
 
52
58
  # Local Paths
53
59
  from socx.core._paths import LOCAL_CONFIG_FILE as LOCAL_CONFIG_FILE
@@ -177,7 +177,6 @@ def _get_rotating_file_handler(
177
177
 
178
178
  mode = mode or "w"
179
179
  handler = logging.handlers.RotatingFileHandler(
180
- # no particular reason for size or backup count - arbitrarily chosen
181
180
  path,
182
181
  mode=mode,
183
182
  maxBytes=MBs(10),
@@ -246,9 +245,7 @@ def get_logger(name: str, filename: str | None = None) -> logging.Logger:
246
245
  """Return a child logger configured with optional file output."""
247
246
  rv = logger.getChild(name)
248
247
  if filename is not None:
249
- handler = _get_rotating_file_handler(filename)
250
- handler.setFormatter(DEFAULT_CHILD_FORMATTER)
251
- rv.addHandler(handler)
248
+ rv.addHandler(_get_file_handler(filename))
252
249
  return rv
253
250
 
254
251
 
@@ -1,7 +1,7 @@
1
1
  logging:
2
2
  handlers:
3
3
  file:
4
- path: "@get this.paths.user_log_file"
4
+ path: "@jinja {{this.paths.user_log_file}}"
5
5
  level: DEBUG
6
6
  console:
7
7
  level: INFO
@@ -12,8 +12,7 @@ logging:
12
12
  False
13
13
  {%- endif -%}"
14
14
  rotating_file:
15
- level: DEBUG
16
- path: "@get this.paths.user_rotating_log_file"
15
+ path: "@jinja {{this.paths.user_rotating_log_file}}"
17
16
 
18
17
  formatters:
19
18
  default:
@@ -3,7 +3,6 @@ regression:
3
3
  params:
4
4
  inline: false
5
5
  inline_no_clear: true
6
-
7
6
  paths:
8
7
  app_dir: >-
9
8
  @jinja {{'{}/../socx_tui'.format(this.paths.app_root_dir) | realpath}}
@@ -11,30 +10,12 @@ regression:
11
10
  @jinja {{'{}/static'.format(this.regression.tui.paths.app_dir) | realpath}}
12
11
  tcss_dir: >-
13
12
  @jinja {{'{}/tcss/regression'.format(this.regression.tui.paths.static_dir) | realpath}}
14
-
15
- table:
16
- view:
17
- opts:
18
- cursor_type: row
19
- show_header: true
20
- show_row_labels: true
21
- show_cursor: true
22
- zebra_stripes: true
23
- fixed_rows: 0
24
- fixed_columns: 0
25
-
26
- model:
27
- opts:
28
- exclude:
29
- - exec
30
-
31
13
  run:
32
14
  input:
33
15
  filename: ~
34
16
  directory: ~
35
17
  output:
36
18
  directory: "@path workrun/regression"
37
- path: ~
38
19
  test_cls: "socx:Test"
39
20
  regression_cls: "socx:Regression"
40
21
  max_runs_in_parallel: 10
@@ -214,11 +214,10 @@ rich_click:
214
214
  # type: NotRequired[Optional[Union[str, "Text"]]]
215
215
  footer_text: |
216
216
  @markdown @jinja :cd: [bold yellow]Cwd[/]: [italic]{{'{}'.format(this.cli.params.cwd)}}[/]
217
+ :open_file_folder: [bold yellow]Root[/]: [italic]{{'{}'.format(this.paths.project_root_dir)}}[/]
217
218
  {% if this.settings_file -%}
218
- :open_file_folder: [bold yellow]Root[/]: [italic]{{'{}'.format(this._root_path)}}[/]
219
219
  :wrench: [bold yellow]Config[/]: [italic]{{'{}'.format(this.settings_file[-1])}}[/]
220
220
  {% else -%}
221
- :open_file_folder: [bold yellow]Root[/]: [italic]{{'{}'.format(this._root_path)}}[/]
222
221
  :wrench: [bold yellow]Config[/]: [italic red]Not found[/]
223
222
  {% endif -%}
224
223
  :link: [bold yellow]Documentation[/]: [italic link]https://sagikimhi.dev/socx-cli[/]
@@ -277,7 +276,7 @@ rich_click:
277
276
  # type: NotRequired["PaddingDimensions"]
278
277
  padding_epilog: ~
279
278
  # type: NotRequired["PaddingDimensions"]
280
- padding_footer_text: [0,2]
279
+ padding_footer_text: [0, 2]
281
280
  # type: NotRequired["PaddingDimensions"]
282
281
  padding_errors_panel: ~
283
282
  # type: NotRequired["PaddingDimensions"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes