config-cli-gui 0.1.4__tar.gz → 0.1.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.
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/HISTORY.md +72 -50
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/PKG-INFO +1 -1
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/README.md +5 -0
- config_cli_gui-0.1.6/config.yaml +51 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/usage/config.md +5 -5
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui/_version.py +16 -3
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui/cli.py +5 -5
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui/config.py +63 -98
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui/docs.py +9 -11
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui/gui.py +38 -37
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui.egg-info/PKG-INFO +1 -1
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui.egg-info/SOURCES.txt +1 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/cli/cli_example.py +5 -5
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/config/config_example.py +28 -24
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/core/logging.py +6 -6
- config_cli_gui-0.1.6/tests/example_project/gui/config.yaml +51 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/gui/gui_example.py +6 -6
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/test_generic_cli.py +9 -9
- config_cli_gui-0.1.4/config.yaml +0 -51
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/FUNDING.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/actions/setup-environment/action.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/dependabot.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/init.sh +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/release_message.sh +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/update_funding.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/workflows/main.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/workflows/release.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.github/workflows/update_readme.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.gitignore +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.idea/runConfigurations/config_generate.xml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.pre-commit-config.yaml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/.readthedocs.yaml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/LICENSE +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/Makefile +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/.nav.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/_static/img/favicon.png +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/_static/img/logo.png +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/css/custom.css +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/develop/contributing.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/develop/make_windows.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/develop/naming_convention.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/funding/funding.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/getting-started/install.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/getting-started/virtual-environment.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/index.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/docs/usage/cli.md +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/mkdocs.yml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/pyproject.toml +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/scripts/show_filelist.ps1 +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/scripts/show_tree.ps1 +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/scripts/show_tree.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/scripts/update_readme.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/setup.cfg +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui.egg-info/dependency_links.txt +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui.egg-info/entry_points.txt +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui.egg-info/requires.txt +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/src/config_cli_gui.egg-info/top_level.txt +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/template.yml.url +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/__main__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/cli/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/cli/__main__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/config/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/core/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/core/base.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/gui/__init__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/tests/example_project/gui/__main__.py +0 -0
- {config_cli_gui-0.1.4 → config_cli_gui-0.1.6}/uv.lock +0 -0
|
@@ -1,50 +1,72 @@
|
|
|
1
|
-
Changelog
|
|
2
|
-
=========
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
(
|
|
6
|
-
|
|
7
|
-
- Update
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
0.1.
|
|
14
|
-
------------------
|
|
15
|
-
- Feat #5:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
- Feat #
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
1
|
+
Changelog
|
|
2
|
+
=========
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
0.1.5 (2025-11-12)
|
|
6
|
+
------------------
|
|
7
|
+
- Docs: Update HISTORY.md for release 0.1.5. [paul]
|
|
8
|
+
- #8 switch to value instead of default. [paul]
|
|
9
|
+
- #8 adjust doc: how to develop. [paul]
|
|
10
|
+
- #8 allow direct access to attributes without get_category #8. [paul]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
0.1.4 (2025-06-23)
|
|
14
|
+
------------------
|
|
15
|
+
- Feat #5: extensive renaming. [Paul Magister]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
0.1.3 (2025-06-23)
|
|
19
|
+
------------------
|
|
20
|
+
- Update README.md from docs/index.md. [github-actions]
|
|
21
|
+
- Feat #5: extensive renaming and adjustments. [Paul Magister]
|
|
22
|
+
- Feat #5: extensive renaming. [Paul Magister]
|
|
23
|
+
- Feat #5: extensive renaming. [Paul Magister]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
0.1.2 (2025-06-23)
|
|
27
|
+
------------------
|
|
28
|
+
- Feat #5: improve dialog. [Paul Magister]
|
|
29
|
+
- Feat #5: Bugfig: use specific dialog for path parameters. [Paul
|
|
30
|
+
Magister]
|
|
31
|
+
- Fix #4: fix test. [Paul Magister]
|
|
32
|
+
- Fix #4: write yaml file with nicely with comments. [Paul Magister]
|
|
33
|
+
- Fix #4: add comments to the yaml. [Paul Magister]
|
|
34
|
+
- Fix #4: make new special datatypes work (Color, Path): correct
|
|
35
|
+
load/save lifecycle. [Paul Magister]
|
|
36
|
+
- Feat #3: some advanced parameter types as example. [Paul Magister]
|
|
37
|
+
- Feat #3: delete build workflow since it is not necessary for the lib.
|
|
38
|
+
[Paul Magister]
|
|
39
|
+
- Feat #3: add verbose and quiet doc to auto generated config. [Paul
|
|
40
|
+
Magister]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
0.1.1 (2025-06-22)
|
|
44
|
+
------------------
|
|
45
|
+
- Feat #3: avoid type_ [Paul Magister]
|
|
46
|
+
- Feat #3: Improved paramter types: [Paul Magister]
|
|
47
|
+
- Feat #3: Improved paramter types: * move example project to tests *
|
|
48
|
+
remove type_ * add improved widgets to gui_generator.py * separate
|
|
49
|
+
module for docs_generator.py. [Paul Magister]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
0.1.0 (2025-06-22)
|
|
53
|
+
------------------
|
|
54
|
+
- Remove unnecessary example files and deps. [Paul Magister]
|
|
55
|
+
- Update README.md from docs/index.md. [github-actions]
|
|
56
|
+
- Fix doc: formatting. [Paul Magister]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
0.0.2 (2025-06-22)
|
|
60
|
+
------------------
|
|
61
|
+
- Remove _version.py. [Paul Magister]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
0.0.1 (2025-06-22)
|
|
65
|
+
------------------
|
|
66
|
+
- Fix fmt. [Paul Magister]
|
|
67
|
+
- Doc update. [Paul Magister]
|
|
68
|
+
- First actual commit: cleanup. [Paul Magister]
|
|
69
|
+
- ✅ Project renamed from template. [github-actions[bot]]
|
|
70
|
+
- Initial commit. [Paul Magister]
|
|
71
|
+
|
|
72
|
+
|
|
@@ -30,6 +30,11 @@ You can install `config-cli-gui` using pip:
|
|
|
30
30
|
pip install config-cli-gui
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## Contribution
|
|
34
|
+
|
|
35
|
+
Refer to this how-to in the referenced project for getting started to install and develop on this project:
|
|
36
|
+
https://github.com/pamagister/python-template-project
|
|
37
|
+
|
|
33
38
|
---
|
|
34
39
|
|
|
35
40
|
## ✨ Features
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
app:
|
|
2
|
+
# Date format to use | type=str, default value=%Y-%m-%d
|
|
3
|
+
date_format: '%Y-%m-%d'
|
|
4
|
+
# Enable logging to console | type=bool, default value=True
|
|
5
|
+
enable_console_logging: true
|
|
6
|
+
# Enable logging to file | type=bool, default value=True
|
|
7
|
+
enable_file_logging: true
|
|
8
|
+
# Number of backup log files to keep | type=int, default value=5
|
|
9
|
+
log_backup_count: 5
|
|
10
|
+
# Maximum log file size in MB before rotation | type=int, default value=10
|
|
11
|
+
log_file_max_size: 10
|
|
12
|
+
# Log message format style | type=str, default value=detailed
|
|
13
|
+
log_format: detailed
|
|
14
|
+
# Logging level for the application | type=str, default value=INFO
|
|
15
|
+
log_level: INFO
|
|
16
|
+
# Maximum number of worker threads | type=int, default value=4
|
|
17
|
+
max_workers: 4
|
|
18
|
+
cli:
|
|
19
|
+
# Include elevation data in waypoints | type=bool, default value=True [CLI]
|
|
20
|
+
elevation: true
|
|
21
|
+
# Extract starting points of each track as waypoint | type=bool, default value=True [CLI]
|
|
22
|
+
extract_waypoints: true
|
|
23
|
+
# Path to input (file or folder) | type=str, default value= [CLI]
|
|
24
|
+
input: ''
|
|
25
|
+
# Maximum distance between two waypoints | type=int, default value=20 [CLI]
|
|
26
|
+
min_dist: 20
|
|
27
|
+
# Path to output destination | type=str, default value= [CLI]
|
|
28
|
+
output: ''
|
|
29
|
+
gui:
|
|
30
|
+
# Automatically scroll to the newest log entries | type=bool, default value=True
|
|
31
|
+
auto_scroll_log: true
|
|
32
|
+
# Height of the log window in pixels | type=int, default value=200
|
|
33
|
+
log_window_height: 200
|
|
34
|
+
# Maximum number of log lines to keep in GUI | type=int, default value=1000
|
|
35
|
+
max_log_lines: 1000
|
|
36
|
+
# GUI theme setting | type=str, default value=light
|
|
37
|
+
theme: light
|
|
38
|
+
# Default window height | type=int, default value=600
|
|
39
|
+
window_height: 600
|
|
40
|
+
# Default window width | type=int, default value=800
|
|
41
|
+
window_width: 800
|
|
42
|
+
misc:
|
|
43
|
+
# Color setting for the application | type=Color, default value=#ff0000
|
|
44
|
+
some_color:
|
|
45
|
+
- 255
|
|
46
|
+
- 0
|
|
47
|
+
- 0
|
|
48
|
+
# Date setting for the application | type=datetime, default value=2025-11-12 22:24:14.121460
|
|
49
|
+
some_date: '2025-11-12T22:24:14.121460'
|
|
50
|
+
# Path to the file to use | type=PosixPath, default value=some_file.txt
|
|
51
|
+
some_file: some_file.txt
|
|
@@ -39,9 +39,9 @@ The parameters in the cli category can be accessed via the command line interfac
|
|
|
39
39
|
|
|
40
40
|
## Category "misc"
|
|
41
41
|
|
|
42
|
-
| Name | Type
|
|
43
|
-
|
|
44
|
-
| some_file |
|
|
45
|
-
| some_color | Color
|
|
46
|
-
| some_date | datetime
|
|
42
|
+
| Name | Type | Description | Default | Choices |
|
|
43
|
+
|------------|-----------|-----------------------------------|-----------------------------------------------------|---------|
|
|
44
|
+
| some_file | PosixPath | Path to the file to use | PosixPath('some_file.txt') | - |
|
|
45
|
+
| some_color | Color | Color setting for the application | Color(255, 0, 0) | - |
|
|
46
|
+
| some_date | datetime | Date setting for the application | datetime.datetime(2025, 11, 12, 22, 24, 14, 121460) | - |
|
|
47
47
|
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
3
|
|
|
4
|
-
__all__ = [
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
TYPE_CHECKING = False
|
|
7
14
|
if TYPE_CHECKING:
|
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
|
9
16
|
from typing import Union
|
|
10
17
|
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
12
20
|
else:
|
|
13
21
|
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
14
23
|
|
|
15
24
|
version: str
|
|
16
25
|
__version__: str
|
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
|
18
27
|
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
19
30
|
|
|
20
|
-
__version__ = version = '0.1.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.6'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 6)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = 'gd42f29151'
|
|
@@ -46,7 +46,7 @@ class CliGenerator:
|
|
|
46
46
|
|
|
47
47
|
# Generate arguments from CLI config parameters
|
|
48
48
|
for param in cli_params:
|
|
49
|
-
param_type = type(param.
|
|
49
|
+
param_type = type(param.value)
|
|
50
50
|
|
|
51
51
|
if param.required and param.cli_arg is None:
|
|
52
52
|
# Positional argument
|
|
@@ -54,8 +54,8 @@ class CliGenerator:
|
|
|
54
54
|
else:
|
|
55
55
|
# Optional argument
|
|
56
56
|
kwargs = {
|
|
57
|
-
"
|
|
58
|
-
"help": f"{param.help} (
|
|
57
|
+
"value": argparse.SUPPRESS,
|
|
58
|
+
"help": f"{param.help} (value: {param.value})",
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
# Handle different parameter types
|
|
@@ -67,8 +67,8 @@ class CliGenerator:
|
|
|
67
67
|
elif param_type == float:
|
|
68
68
|
kwargs["type"] = float
|
|
69
69
|
elif param_type == bool:
|
|
70
|
-
kwargs["action"] = "store_true" if not param.
|
|
71
|
-
kwargs["help"] = f"{param.help} (
|
|
70
|
+
kwargs["action"] = "store_true" if not param.value else "store_false"
|
|
71
|
+
kwargs["help"] = f"{param.help} (value: {param.value})"
|
|
72
72
|
elif param_type == str:
|
|
73
73
|
kwargs["type"] = str
|
|
74
74
|
|
|
@@ -48,7 +48,7 @@ class ConfigParameter:
|
|
|
48
48
|
"""Represents a single configuration parameter with all its metadata."""
|
|
49
49
|
|
|
50
50
|
name: str
|
|
51
|
-
|
|
51
|
+
value: Any
|
|
52
52
|
choices: list | tuple | None = None
|
|
53
53
|
help: str = ""
|
|
54
54
|
cli_arg: str = None
|
|
@@ -59,13 +59,13 @@ class ConfigParameter:
|
|
|
59
59
|
def __post_init__(self):
|
|
60
60
|
if self.is_cli and self.cli_arg is None and not self.required:
|
|
61
61
|
self.cli_arg = f"--{self.name}"
|
|
62
|
-
if isinstance(self.
|
|
62
|
+
if isinstance(self.value, bool) and self.choices is None:
|
|
63
63
|
self.choices = [True, False]
|
|
64
64
|
|
|
65
65
|
@property
|
|
66
66
|
def type_(self) -> type:
|
|
67
|
-
"""Get the type from the
|
|
68
|
-
return type(self.
|
|
67
|
+
"""Get the type from the value."""
|
|
68
|
+
return type(self.value)
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
class ConfigCategory(BaseModel, ABC):
|
|
@@ -73,14 +73,13 @@ class ConfigCategory(BaseModel, ABC):
|
|
|
73
73
|
|
|
74
74
|
@abstractmethod
|
|
75
75
|
def get_category_name(self) -> str:
|
|
76
|
-
"""Return the category name for this configuration group."""
|
|
77
76
|
pass
|
|
78
77
|
|
|
79
78
|
def get_parameters(self) -> list[ConfigParameter]:
|
|
80
79
|
"""Get all ConfigParameter objects from this category."""
|
|
81
80
|
parameters = []
|
|
82
81
|
for field_name in self.__class__.model_fields:
|
|
83
|
-
param =
|
|
82
|
+
param = self.model_fields[field_name].default
|
|
84
83
|
if isinstance(param, ConfigParameter):
|
|
85
84
|
param.category = self.get_category_name()
|
|
86
85
|
parameters.append(param)
|
|
@@ -99,19 +98,18 @@ class ConfigManager:
|
|
|
99
98
|
"""
|
|
100
99
|
self._categories: dict[str, ConfigCategory] = {}
|
|
101
100
|
|
|
101
|
+
# Register categories and make accessible as attributes
|
|
102
102
|
for category in categories:
|
|
103
|
-
if isinstance(category, ConfigCategory):
|
|
104
|
-
self.add_category(category.get_category_name(), category)
|
|
105
|
-
else:
|
|
103
|
+
if not isinstance(category, ConfigCategory):
|
|
106
104
|
raise TypeError(
|
|
107
|
-
f"Category must be an instance of
|
|
105
|
+
f"Category must be an instance of ConfigCategory, got {type(category)}"
|
|
108
106
|
)
|
|
107
|
+
name = category.get_category_name()
|
|
108
|
+
self.add_category(name, category)
|
|
109
109
|
|
|
110
|
-
# Load from file if provided
|
|
111
110
|
if config_file:
|
|
112
111
|
self.load_from_file(config_file)
|
|
113
112
|
|
|
114
|
-
# Override with provided kwargs
|
|
115
113
|
self._apply_kwargs(kwargs)
|
|
116
114
|
|
|
117
115
|
def add_category(self, name: str, category: ConfigCategory):
|
|
@@ -122,58 +120,46 @@ class ConfigManager:
|
|
|
122
120
|
category: Configuration category instance
|
|
123
121
|
"""
|
|
124
122
|
self._categories[name] = category
|
|
123
|
+
setattr(self, name, category)
|
|
125
124
|
|
|
126
125
|
def get_category(self, name: str) -> ConfigCategory:
|
|
127
|
-
"""Get a configuration category by name."""
|
|
128
126
|
return self._categories.get(name)
|
|
129
127
|
|
|
130
128
|
def _apply_kwargs(self, kwargs: dict[str, Any]):
|
|
131
|
-
"""Apply keyword
|
|
129
|
+
"""Apply keyword overrides: category__param=value"""
|
|
132
130
|
for key, value in kwargs.items():
|
|
133
131
|
if "__" in key:
|
|
134
132
|
category_name, param_name = key.split("__", 1)
|
|
135
133
|
if category_name in self._categories:
|
|
136
134
|
category = self._categories[category_name]
|
|
137
135
|
if hasattr(category, param_name):
|
|
138
|
-
|
|
139
|
-
if isinstance(param, ConfigParameter):
|
|
140
|
-
param.default = value
|
|
136
|
+
setattr(category, param_name, value)
|
|
141
137
|
|
|
142
138
|
def load_from_file(self, config_file: str):
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if not config_path.exists():
|
|
139
|
+
path = Path(config_file)
|
|
140
|
+
if not path.exists():
|
|
146
141
|
raise FileNotFoundError(f"Configuration file not found: {config_file}")
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# Handle special types
|
|
169
|
-
if isinstance(param.default, Color) and isinstance(param_value, list):
|
|
170
|
-
param.default = Color.from_list(param_value)
|
|
171
|
-
elif isinstance(param.default, Path):
|
|
172
|
-
param.default = Path(param_value)
|
|
173
|
-
elif isinstance(param.default, datetime):
|
|
174
|
-
param.default = datetime.fromisoformat(param_value)
|
|
175
|
-
else:
|
|
176
|
-
param.default = param_value
|
|
142
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
143
|
+
data = yaml.safe_load(f) if path.suffix in [".yml", ".yaml"] else json.load(f)
|
|
144
|
+
self._apply_config_data(data)
|
|
145
|
+
|
|
146
|
+
def _apply_config_data(self, data: dict):
|
|
147
|
+
for category_name, category_data in data.items():
|
|
148
|
+
category = self._categories.get(category_name)
|
|
149
|
+
if not category:
|
|
150
|
+
continue
|
|
151
|
+
for param_name, param_value in category_data.items():
|
|
152
|
+
if hasattr(category, param_name):
|
|
153
|
+
param = category.model_fields.get(param_name).default
|
|
154
|
+
if isinstance(param, ConfigParameter):
|
|
155
|
+
default_value = getattr(category, param_name)
|
|
156
|
+
if isinstance(default_value, Color) and isinstance(param_value, list):
|
|
157
|
+
param_value = Color.from_list(param_value)
|
|
158
|
+
elif isinstance(default_value, Path):
|
|
159
|
+
param_value = Path(param_value)
|
|
160
|
+
elif isinstance(default_value, datetime):
|
|
161
|
+
param_value = datetime.fromisoformat(param_value)
|
|
162
|
+
setattr(getattr(self, category_name), param_name, param_value)
|
|
177
163
|
|
|
178
164
|
def save_to_file(self, config_file: str, format_: str = "auto"):
|
|
179
165
|
"""Save current configuration to file with enhanced YAML formatting and comments.
|
|
@@ -182,34 +168,28 @@ class ConfigManager:
|
|
|
182
168
|
config_file (str): The path to the configuration file.
|
|
183
169
|
format_ (str): The format to save the file in ('auto', 'json', 'yaml').
|
|
184
170
|
"""
|
|
185
|
-
|
|
186
|
-
|
|
171
|
+
path = Path(config_file)
|
|
172
|
+
data = self.to_dict()
|
|
187
173
|
|
|
188
|
-
# Determine format
|
|
189
174
|
if format_ == "auto":
|
|
190
|
-
format_ = "yaml" if
|
|
191
|
-
|
|
192
|
-
# Ensure directory exists
|
|
193
|
-
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
175
|
+
format_ = "yaml" if path.suffix in [".yml", ".yaml"] else "json"
|
|
194
176
|
|
|
195
|
-
|
|
177
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
196
179
|
if format_ == "yaml":
|
|
197
|
-
yaml.dump(
|
|
180
|
+
yaml.dump(data, f, indent=2)
|
|
198
181
|
else:
|
|
199
|
-
json.dump(
|
|
182
|
+
json.dump(data, f, indent=2)
|
|
200
183
|
|
|
201
|
-
# Append comments for YAML files
|
|
202
184
|
if format_ == "yaml":
|
|
203
|
-
self._append_comments_to_yaml(
|
|
185
|
+
self._append_comments_to_yaml(path)
|
|
204
186
|
|
|
205
187
|
def to_dict(self) -> dict[str, Any]:
|
|
206
|
-
"""Convert configuration to dictionary."""
|
|
207
188
|
result = {}
|
|
208
|
-
for
|
|
189
|
+
for name, category in self._categories.items():
|
|
209
190
|
category_dict = {}
|
|
210
191
|
for param in category.get_parameters():
|
|
211
|
-
value = param.
|
|
212
|
-
# Handle special types for serialization
|
|
192
|
+
value = getattr(category, param.name).value
|
|
213
193
|
if isinstance(value, Color):
|
|
214
194
|
value = value.to_list()
|
|
215
195
|
elif isinstance(value, Path):
|
|
@@ -217,64 +197,49 @@ class ConfigManager:
|
|
|
217
197
|
elif isinstance(value, datetime):
|
|
218
198
|
value = value.isoformat()
|
|
219
199
|
category_dict[param.name] = value
|
|
220
|
-
result[
|
|
200
|
+
result[name] = category_dict
|
|
221
201
|
return result
|
|
222
202
|
|
|
223
203
|
def get_all_parameters(self) -> list[ConfigParameter]:
|
|
224
|
-
|
|
225
|
-
parameters = []
|
|
226
|
-
for category in self._categories.values():
|
|
227
|
-
parameters.extend(category.get_parameters())
|
|
228
|
-
return parameters
|
|
204
|
+
return [p for c in self._categories.values() for p in c.get_parameters()]
|
|
229
205
|
|
|
230
206
|
def get_cli_parameters(self) -> list[ConfigParameter]:
|
|
231
|
-
|
|
232
|
-
cli_parameters = []
|
|
233
|
-
for category in self._categories.values():
|
|
234
|
-
for param in category.get_parameters():
|
|
235
|
-
if param.is_cli:
|
|
236
|
-
cli_parameters.append(param)
|
|
237
|
-
return cli_parameters
|
|
207
|
+
return [p for p in self.get_all_parameters() if p.is_cli]
|
|
238
208
|
|
|
239
|
-
def _append_comments_to_yaml(self,
|
|
209
|
+
def _append_comments_to_yaml(self, path: Path):
|
|
240
210
|
"""Appends comments to a YAML file based on ConfigParameter metadata.
|
|
241
211
|
|
|
242
212
|
Args:
|
|
243
213
|
config_path (Path): The path to the YAML configuration file.
|
|
244
214
|
"""
|
|
245
|
-
|
|
215
|
+
|
|
216
|
+
lines = path.read_text(encoding="utf-8").splitlines()
|
|
246
217
|
new_lines = []
|
|
247
218
|
all_parameters = {param.name: param for param in self.get_all_parameters()}
|
|
248
219
|
current_category = None
|
|
249
220
|
|
|
250
221
|
for line in lines:
|
|
251
|
-
|
|
252
|
-
# Check for category (e.g., 'app:')
|
|
253
|
-
# A category should end with ':', not start with '#', and not be indented.
|
|
222
|
+
stripped = line.strip()
|
|
254
223
|
if (
|
|
255
|
-
|
|
256
|
-
and not
|
|
257
|
-
and line.startswith(
|
|
224
|
+
stripped.endswith(":")
|
|
225
|
+
and not stripped.startswith("#")
|
|
226
|
+
and line.startswith(stripped)
|
|
258
227
|
):
|
|
259
|
-
current_category =
|
|
228
|
+
current_category = stripped[:-1]
|
|
260
229
|
new_lines.append(line)
|
|
261
230
|
else:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
parts = stripped_line.split(":", 1)
|
|
265
|
-
if len(parts) > 1: # This line might be a parameter definition
|
|
231
|
+
parts = stripped.split(":", 1)
|
|
232
|
+
if len(parts) > 1:
|
|
266
233
|
param_name = parts[0].strip()
|
|
267
234
|
if param_name in all_parameters:
|
|
268
235
|
param = all_parameters[param_name]
|
|
269
|
-
# Ensure the parameter belongs to the current category
|
|
270
|
-
# and is not a sub-item of a multi-line value
|
|
271
236
|
if current_category and param.category == current_category:
|
|
272
|
-
|
|
237
|
+
indent = " " * (len(line) - len(stripped))
|
|
273
238
|
comment = (
|
|
274
|
-
f"{
|
|
275
|
-
f"type={type(param.
|
|
239
|
+
f"{indent}# {param.help} | "
|
|
240
|
+
f"type={type(param.value).__name__}, default value={param.value}"
|
|
276
241
|
f"{' [CLI]' if param.is_cli else ''}"
|
|
277
242
|
)
|
|
278
243
|
new_lines.append(comment)
|
|
279
244
|
new_lines.append(line)
|
|
280
|
-
|
|
245
|
+
path.write_text("\n".join(new_lines), encoding="utf-8")
|
|
@@ -33,12 +33,12 @@ class DocumentationGenerator:
|
|
|
33
33
|
|
|
34
34
|
for param in category.get_parameters():
|
|
35
35
|
name = param.name
|
|
36
|
-
typ = type(param.
|
|
36
|
+
typ = type(param.value).__name__
|
|
37
37
|
desc = param.help
|
|
38
|
-
|
|
38
|
+
value = repr(param.value)
|
|
39
39
|
choices = str(param.choices) if param.choices else "-"
|
|
40
40
|
|
|
41
|
-
rows.append((name, typ, desc,
|
|
41
|
+
rows.append((name, typ, desc, value, choices))
|
|
42
42
|
|
|
43
43
|
if not rows:
|
|
44
44
|
continue
|
|
@@ -85,17 +85,15 @@ class DocumentationGenerator:
|
|
|
85
85
|
|
|
86
86
|
for param in cli_params:
|
|
87
87
|
cli_arg = f"`--{param.name}`" if not param.required else f"`{param.name}`"
|
|
88
|
-
typ = type(param.
|
|
88
|
+
typ = type(param.value).__name__
|
|
89
89
|
desc = param.help
|
|
90
|
-
|
|
91
|
-
"*required*"
|
|
92
|
-
if param.required or param.default in (None, "")
|
|
93
|
-
else repr(param.default)
|
|
90
|
+
value = (
|
|
91
|
+
"*required*" if param.required or param.value in (None, "") else repr(param.value)
|
|
94
92
|
)
|
|
95
93
|
choices = str(param.choices) if param.choices else "-"
|
|
96
94
|
|
|
97
|
-
rows.append((cli_arg, typ, desc,
|
|
98
|
-
if
|
|
95
|
+
rows.append((cli_arg, typ, desc, value, choices))
|
|
96
|
+
if value == "*required*":
|
|
99
97
|
required_params.append(param)
|
|
100
98
|
else:
|
|
101
99
|
optional_params.append(param)
|
|
@@ -165,7 +163,7 @@ class DocumentationGenerator:
|
|
|
165
163
|
for i, param in enumerate(optional_params[:3], 4):
|
|
166
164
|
if param.name in ["verbose", "quiet"]:
|
|
167
165
|
continue
|
|
168
|
-
example_value = param.choices[0] if param.choices else param.
|
|
166
|
+
example_value = param.choices[0] if param.choices else param.value
|
|
169
167
|
examples.append(
|
|
170
168
|
dedent(f"""
|
|
171
169
|
### {i}. With {param.name} parameter
|