python-template-project 0.2.0__py3-none-any.whl → 0.2.1__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.
@@ -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__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
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.2.0'
21
- __version_tuple__ = version_tuple = (0, 2, 0)
31
+ __version__ = version = '0.2.1'
32
+ __version_tuple__ = version_tuple = (0, 2, 1)
33
+
34
+ __commit_id__ = commit_id = None
@@ -5,7 +5,8 @@ This file uses the CliGenerator from the generic config framework.
5
5
 
6
6
  from pathlib import Path
7
7
 
8
- from generic_config_cli_gui.cli_generator import CliGenerator
8
+ from config_cli_gui.cli import CliGenerator
9
+
9
10
  from python_template_project.config.config import ConfigParameterManager
10
11
  from python_template_project.core.base import BaseGPXProcessor
11
12
  from python_template_project.core.logging import initialize_logging
@@ -22,19 +23,19 @@ def validate_config(config: ConfigParameterManager, logger) -> bool:
22
23
  True if configuration is valid, False otherwise
23
24
  """
24
25
  # Get CLI category and check required parameters
25
- cli_category = config.get_cli_category()
26
+ cli_category = config.cli
26
27
  if not cli_category:
27
28
  logger.error("No CLI configuration found")
28
29
  return False
29
30
 
30
31
  # Check if input parameter exists and has a value
31
- input_param = getattr(cli_category, "input", None)
32
- if not input_param or not input_param.default:
32
+ input_param = getattr(cli_category, "input")
33
+ if not input_param or not input_param.value:
33
34
  logger.error("Input is required")
34
35
  return False
35
36
 
36
37
  # Check if input file exists
37
- input_path = Path(input_param.default)
38
+ input_path = Path(input_param.value)
38
39
  if not input_path.exists():
39
40
  logger.error(f"File not found: {input_path}")
40
41
  return False
@@ -67,15 +68,15 @@ def run_main_processing(config: ConfigParameterManager) -> int:
67
68
  return 1
68
69
 
69
70
  # Get CLI parameters
70
- cli_category = config.get_cli_category()
71
- input_file = cli_category.input.default
72
- output_file = cli_category.output.default
73
- min_dist = cli_category.min_dist.default
74
- extract_waypoints = cli_category.extract_waypoints.default
71
+ cli_category = config.cli
72
+ input_file = cli_category.input.value
73
+ output_file = cli_category.output.value
74
+ min_dist = cli_category.min_dist.value
75
+ extract_waypoints = cli_category.extract_waypoints.value
75
76
 
76
77
  # Get app parameters
77
- app_category = config.get_category("app")
78
- date_format = app_category.date_format.default if app_category else "%Y-%m-%d"
78
+ app_category = config.app
79
+ date_format = app_category.date_format.value if app_category else "%Y-%m-%d"
79
80
 
80
81
  logger.info(f"Processing input: {input_file}")
81
82
 
@@ -1,20 +1,15 @@
1
- """Central configuration management for python-template-project project.
1
+ """Central configuration management for config-cli-gui project.
2
2
 
3
3
  This module provides a single source of truth for all configuration parameters
4
4
  organized in categories (CLI, App, GUI). It can generate config files, CLI modules,
5
5
  and documentation from the parameter definitions.
6
6
  """
7
7
 
8
- from generic_config_cli_gui.config_framework import (
9
- BaseConfigCategory,
10
- CliConfigCategory,
11
- ConfigManager,
12
- ConfigParameter,
13
- DocumentationGenerator,
14
- )
8
+ from config_cli_gui.config import ConfigCategory, ConfigManager, ConfigParameter
9
+ from config_cli_gui.docs import DocumentationGenerator
15
10
 
16
11
 
17
- class CliConfig(CliConfigCategory):
12
+ class CliConfig(ConfigCategory):
18
13
  """CLI-specific configuration parameters."""
19
14
 
20
15
  def get_category_name(self) -> str:
@@ -23,44 +18,43 @@ class CliConfig(CliConfigCategory):
23
18
  # Positional argument
24
19
  input: ConfigParameter = ConfigParameter(
25
20
  name="input",
26
- default="",
27
- type_=str,
21
+ value="",
28
22
  help="Path to input (file or folder)",
29
23
  required=True,
30
- cli_arg=None, # Positional argument
24
+ is_cli=True,
31
25
  )
32
26
 
33
27
  # Optional CLI arguments
34
28
  output: ConfigParameter = ConfigParameter(
35
29
  name="output",
36
- default="",
37
- type_=str,
30
+ value="",
38
31
  help="Path to output destination",
32
+ is_cli=True,
39
33
  )
40
34
 
41
35
  min_dist: ConfigParameter = ConfigParameter(
42
36
  name="min_dist",
43
- default=20,
44
- type_=int,
37
+ value=20,
45
38
  help="Maximum distance between two waypoints",
39
+ is_cli=True,
46
40
  )
47
41
 
48
42
  extract_waypoints: ConfigParameter = ConfigParameter(
49
43
  name="extract_waypoints",
50
- default=True,
51
- type_=bool,
44
+ value=True,
52
45
  help="Extract starting points of each track as waypoint",
46
+ is_cli=True,
53
47
  )
54
48
 
55
49
  elevation: ConfigParameter = ConfigParameter(
56
50
  name="elevation",
57
- default=True,
58
- type_=bool,
51
+ value=True,
59
52
  help="Include elevation data in waypoints",
53
+ is_cli=True,
60
54
  )
61
55
 
62
56
 
63
- class AppConfig(BaseConfigCategory):
57
+ class AppConfig(ConfigCategory):
64
58
  """Application-specific configuration parameters."""
65
59
 
66
60
  def get_category_name(self) -> str:
@@ -68,64 +62,56 @@ class AppConfig(BaseConfigCategory):
68
62
 
69
63
  date_format: ConfigParameter = ConfigParameter(
70
64
  name="date_format",
71
- default="%Y-%m-%d",
72
- type_=str,
65
+ value="%Y-%m-%d",
73
66
  help="Date format to use",
74
67
  )
75
68
 
76
69
  log_level: ConfigParameter = ConfigParameter(
77
70
  name="log_level",
78
- default="INFO",
79
- type_=str,
71
+ value="INFO",
80
72
  choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
81
73
  help="Logging level for the application",
82
74
  )
83
75
 
84
76
  log_file_max_size: ConfigParameter = ConfigParameter(
85
77
  name="log_file_max_size",
86
- default=10,
87
- type_=int,
78
+ value=10,
88
79
  help="Maximum log file size in MB before rotation",
89
80
  )
90
81
 
91
82
  log_backup_count: ConfigParameter = ConfigParameter(
92
83
  name="log_backup_count",
93
- default=5,
94
- type_=int,
84
+ value=5,
95
85
  help="Number of backup log files to keep",
96
86
  )
97
87
 
98
88
  log_format: ConfigParameter = ConfigParameter(
99
89
  name="log_format",
100
- default="detailed",
101
- type_=str,
90
+ value="detailed",
102
91
  choices=["simple", "detailed", "json"],
103
92
  help="Log message format style",
104
93
  )
105
94
 
106
95
  max_workers: ConfigParameter = ConfigParameter(
107
96
  name="max_workers",
108
- default=4,
109
- type_=int,
97
+ value=4,
110
98
  help="Maximum number of worker threads",
111
99
  )
112
100
 
113
101
  enable_file_logging: ConfigParameter = ConfigParameter(
114
102
  name="enable_file_logging",
115
- default=True,
116
- type_=bool,
103
+ value=True,
117
104
  help="Enable logging to file",
118
105
  )
119
106
 
120
107
  enable_console_logging: ConfigParameter = ConfigParameter(
121
108
  name="enable_console_logging",
122
- default=True,
123
- type_=bool,
109
+ value=True,
124
110
  help="Enable logging to console",
125
111
  )
126
112
 
127
113
 
128
- class GuiConfig(BaseConfigCategory):
114
+ class GuiConfig(ConfigCategory):
129
115
  """GUI-specific configuration parameters."""
130
116
 
131
117
  def get_category_name(self) -> str:
@@ -133,44 +119,38 @@ class GuiConfig(BaseConfigCategory):
133
119
 
134
120
  theme: ConfigParameter = ConfigParameter(
135
121
  name="theme",
136
- default="light",
137
- type_=str,
122
+ value="light",
138
123
  choices=["light", "dark", "auto"],
139
124
  help="GUI theme setting",
140
125
  )
141
126
 
142
127
  window_width: ConfigParameter = ConfigParameter(
143
128
  name="window_width",
144
- default=800,
145
- type_=int,
129
+ value=800,
146
130
  help="Default window width",
147
131
  )
148
132
 
149
133
  window_height: ConfigParameter = ConfigParameter(
150
134
  name="window_height",
151
- default=600,
152
- type_=int,
135
+ value=600,
153
136
  help="Default window height",
154
137
  )
155
138
 
156
139
  log_window_height: ConfigParameter = ConfigParameter(
157
140
  name="log_window_height",
158
- default=200,
159
- type_=int,
141
+ value=200,
160
142
  help="Height of the log window in pixels",
161
143
  )
162
144
 
163
145
  auto_scroll_log: ConfigParameter = ConfigParameter(
164
146
  name="auto_scroll_log",
165
- default=True,
166
- type_=bool,
147
+ value=True,
167
148
  help="Automatically scroll to newest log entries",
168
149
  )
169
150
 
170
151
  max_log_lines: ConfigParameter = ConfigParameter(
171
152
  name="max_log_lines",
172
- default=1000,
173
- type_=int,
153
+ value=1000,
174
154
  help="Maximum number of log lines to keep in GUI",
175
155
  )
176
156
 
@@ -178,14 +158,14 @@ class GuiConfig(BaseConfigCategory):
178
158
  class ConfigParameterManager(ConfigManager): # Inherit from ConfigManager
179
159
  """Main configuration manager that handles all parameter categories."""
180
160
 
161
+ cli: CliConfig
162
+ app: AppConfig
163
+ gui: GuiConfig
164
+
181
165
  def __init__(self, config_file: str | None = None, **kwargs):
182
166
  # Erst den Parent initialisieren
183
- super().__init__(config_file, **kwargs)
184
-
185
- # Dann die Kategorien hinzufügen
186
- self.add_category("cli", CliConfig())
187
- self.add_category("app", AppConfig())
188
- self.add_category("gui", GuiConfig())
167
+ categories = (CliConfig(), AppConfig(), GuiConfig())
168
+ super().__init__(categories, config_file, **kwargs)
189
169
 
190
170
 
191
171
  def main():
@@ -194,14 +174,14 @@ def main():
194
174
  default_cli_doc: str = "../../docs/usage/cli.md"
195
175
  default_config_doc: str = "../../docs/usage/config.md"
196
176
  config_manager = ConfigParameterManager()
197
- docGen = DocumentationGenerator(config_manager)
198
- docGen.generate_default_config_file(output_file=default_config)
177
+ doc_gen = DocumentationGenerator(config_manager)
178
+ doc_gen.generate_default_config_file(output_file=default_config)
199
179
  print(f"Generated: {default_config}")
200
180
 
201
- docGen.generate_config_markdown_doc(output_file=default_config_doc)
181
+ doc_gen.generate_config_markdown_doc(output_file=default_config_doc)
202
182
  print(f"Generated: {default_config_doc}")
203
183
 
204
- docGen.generate_cli_markdown_doc(output_file=default_cli_doc)
184
+ doc_gen.generate_cli_markdown_doc(output_file=default_cli_doc)
205
185
  print(f"Generated: {default_cli_doc}")
206
186
 
207
187
 
@@ -12,7 +12,7 @@ import logging.handlers
12
12
  import sys
13
13
  from pathlib import Path
14
14
 
15
- from generic_config_cli_gui.config_framework import ConfigManager
15
+ from python_template_project.config.config import ConfigParameterManager
16
16
 
17
17
 
18
18
  class GuiLogHandler(logging.Handler):
@@ -39,7 +39,7 @@ class GuiLogHandler(logging.Handler):
39
39
  class LoggerManager:
40
40
  """Manages all logging configuration and handlers."""
41
41
 
42
- def __init__(self, config: ConfigManager):
42
+ def __init__(self, config: ConfigParameterManager):
43
43
  self.config = config
44
44
  self.logger = logging.getLogger("python_template_project")
45
45
  self.gui_handler = None
@@ -53,7 +53,7 @@ class LoggerManager:
53
53
  self.logger.handlers.clear()
54
54
 
55
55
  # Set log level from config
56
- log_level = getattr(logging, self.config.get_category("app").log_level.default.upper())
56
+ log_level = getattr(logging, self.config.app.log_level.value.upper())
57
57
  self.logger.setLevel(log_level)
58
58
 
59
59
  # Create formatters
@@ -142,15 +142,15 @@ class LoggerManager:
142
142
  self.logger.setLevel(log_level)
143
143
 
144
144
  # Update config
145
- self.config.get_category("app").log_level.default = level.upper()
145
+ self.config.app.log_level.value = level.upper()
146
146
 
147
147
  def log_config_summary(self):
148
148
  """Log current configuration summary."""
149
149
  self.logger.info("=== Configuration Summary ===")
150
- self.logger.info(f"Log level: {self.config.get_category('app').log_level.default}")
151
- self.logger.info(f"Input: {self.config.get_category('cli').input.default}")
152
- self.logger.info(f"Output: {self.config.get_category('cli').output.default}")
153
- self.logger.info(f"Max workers: {self.config.get_category('app').max_workers.default}")
150
+ self.logger.info(f"Log level: {self.config.app.log_level.value}")
151
+ self.logger.info(f"Input: {self.config.cli.input.value}")
152
+ self.logger.info(f"Output: {self.config.cli.output.value}")
153
+ self.logger.info(f"Max workers: {self.config.app.max_workers.value}")
154
154
  self.logger.info("==============================")
155
155
 
156
156
 
@@ -158,7 +158,7 @@ class LoggerManager:
158
158
  _logger_manager = None
159
159
 
160
160
 
161
- def initialize_logging(config: ConfigManager) -> LoggerManager:
161
+ def initialize_logging(config: ConfigParameterManager) -> LoggerManager:
162
162
  """Initialize the global logging system.
163
163
 
164
164
  Args:
@@ -17,7 +17,8 @@ from functools import partial
17
17
  from pathlib import Path
18
18
  from tkinter import filedialog, messagebox, ttk
19
19
 
20
- from generic_config_cli_gui.gui_generator import SettingsDialogGenerator
20
+ from config_cli_gui.gui import SettingsDialogGenerator
21
+
21
22
  from python_template_project.config.config import ConfigParameterManager
22
23
  from python_template_project.core.base import BaseGPXProcessor
23
24
  from python_template_project.core.logging import (
@@ -241,9 +242,7 @@ class MainGui:
241
242
 
242
243
  # Log level selector
243
244
  ttk.Label(log_controls, text="Log Level:").pack(side=tk.LEFT, padx=(10, 5))
244
- self.log_level_var = tk.StringVar(
245
- value=self.config_manager.get_category("app").log_level.default
246
- )
245
+ self.log_level_var = tk.StringVar(value=self.config_manager.app.log_level.value)
247
246
  log_level_combo = ttk.Combobox(
248
247
  log_controls,
249
248
  textvariable=self.log_level_var,
@@ -456,10 +455,10 @@ class MainGui:
456
455
  # Create and run project
457
456
  project = BaseGPXProcessor(
458
457
  files_to_process, # Pass selected files
459
- self.config_manager.get_category("cli").output.default,
460
- self.config_manager.get_category("cli").min_dist.default,
461
- self.config_manager.get_category("app").date_format.default,
462
- self.config_manager.get_category("cli").elevation.default,
458
+ self.config_manager.cli.output.value,
459
+ self.config_manager.cli.min_dist.value,
460
+ self.config_manager.app.date_format.value,
461
+ self.config_manager.cli.elevation.value,
463
462
  self.logger,
464
463
  )
465
464
  # implement switch case for different processing modes
@@ -506,7 +505,7 @@ class MainGui:
506
505
  if dialog.result == "ok":
507
506
  self.logger.info("Settings updated successfully")
508
507
  # Update log level selector if it changed
509
- self.log_level_var.set(self.config_manager.get_category("app").log_level.default)
508
+ self.log_level_var.set(self.config_manager.app.log_level.value)
510
509
 
511
510
  def _open_help(self):
512
511
  """Open help documentation in browser."""
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-template-project
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Feature-rich Python project template designed for robustness and ease of use.
5
5
  Author: pamagister
6
6
  Requires-Python: <3.12,>=3.10
7
7
  Description-Content-Type: text/markdown
8
8
  License-File: LICENSE
9
+ Requires-Dist: config-cli-gui>=0.2.0
9
10
  Requires-Dist: fastkml>=1.1.0
10
11
  Requires-Dist: gpxpy>=1.6.2
11
- Requires-Dist: pydantic>=2.11.7
12
- Requires-Dist: pyyaml>=6.0.2
13
12
  Requires-Dist: shapely>=2.1.1
14
13
  Requires-Dist: srtm-py>=0.3.7
15
14
  Provides-Extra: dev
@@ -62,7 +61,7 @@ Getting started on developing your own project based on this template
62
61
 
63
62
  2. **Give a name to your project**
64
63
  For example: `my-python-project`
65
- *(Hyphens may ne used as project name; they are converted during renaming internally to underscores for packages.)*
64
+ *(Hyphens may be used as project name; they are converted during renaming internally to underscores for packages.)*
66
65
 
67
66
  3. **Set write permissions**
68
67
  Go to: `Repository -> Settings -> Actions -> General -> Workflow permissions`
@@ -0,0 +1,23 @@
1
+ __init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ firewall_handler.py,sha256=qT0dInkeKHulpCQD2x3HH7_lMxz4lwdkIPxEvBSl3No,7336
3
+ main.py,sha256=d6ZpnMxcPBf860gQmuYwPVJg1di6r2CR6gSvFLkAZkk,4448
4
+ python_template_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ python_template_project/__main__.py,sha256=JS28LiHoU9Xo4c_RvyLBTDKHqdZRRytJLj6qyrg26mQ,195
6
+ python_template_project/_version.py,sha256=vYqoJTG51NOUmYyL0xt8asRK8vUT4lGAdal_EZ59mvw,704
7
+ python_template_project/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ python_template_project/cli/__main__.py,sha256=qnGkoNNbhnvLTA24X8YTEDxRqDbNEAzgjp9Ki_JYIhs,184
9
+ python_template_project/cli/cli.py,sha256=L5UiEeLzgYbbbKeWPKCVG7i509ou9mi6yNMc2K2UG_c,4201
10
+ python_template_project/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ python_template_project/config/config.py,sha256=oTub_qmH4CbQQc7L354qJE5N3CRhajr0o8XULAUvzA8,5359
12
+ python_template_project/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ python_template_project/core/base.py,sha256=3S_DSPgPom0wCS_tgdEaNpWub0r8GlytmjKtXlIypRA,28045
14
+ python_template_project/core/logging.py,sha256=b5Z0g7XVj4fy7nYVfWMTSz8pV6tcXrNEJ6Y7pNJKfWA,6932
15
+ python_template_project/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ python_template_project/gui/__main__.py,sha256=5AaiBxNCAKWcuSQFcQ_lsEQfEsXm6BA5-i5MScdf30c,184
17
+ python_template_project/gui/gui.py,sha256=8jmwvrJlyez66cnkIoosq65KAx75WmAfwJUUiluh6TY,21647
18
+ python_template_project-0.2.1.dist-info/licenses/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
19
+ python_template_project-0.2.1.dist-info/METADATA,sha256=515IGLkUpYAHUiCaHrIsh_FXJd4H6RojXZmLQ8_RZos,7924
20
+ python_template_project-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ python_template_project-0.2.1.dist-info/entry_points.txt,sha256=LmgMiqxIVRdD7gkktVBBR0CR4AhkugqlgifuglkPhtI,148
22
+ python_template_project-0.2.1.dist-info/top_level.txt,sha256=O-Yg0tzEwgH58MhdcYWpJsgZSUPg6SBbOUwlmVHZo3A,55
23
+ python_template_project-0.2.1.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  __init__
2
2
  firewall_handler
3
- generic_config_cli_gui
4
3
  main
5
4
  python_template_project
File without changes
@@ -1,177 +0,0 @@
1
- # generic_config_cli_gui/cli.py
2
- """Generic CLI generator for configuration framework."""
3
-
4
- import argparse
5
- import traceback
6
- from collections.abc import Callable
7
- from typing import Any
8
-
9
- from generic_config_cli_gui.config_framework import ConfigManager
10
-
11
-
12
- class CliGenerator:
13
- """Generates CLI interface from ConfigManager."""
14
-
15
- def __init__(self, config_manager: ConfigManager, app_name: str = "app"):
16
- self.config_manager = config_manager
17
- self.app_name = app_name
18
-
19
- def create_argument_parser(self, description: str = None) -> argparse.ArgumentParser:
20
- """Create argument parser from configuration."""
21
- if description is None:
22
- description = f"Command line interface for {self.app_name}"
23
-
24
- parser = argparse.ArgumentParser(
25
- description=description,
26
- formatter_class=argparse.RawDescriptionHelpFormatter,
27
- )
28
-
29
- # Config file argument
30
- parser.add_argument(
31
- "--config",
32
- default=None,
33
- help="Path to configuration file (JSON or YAML)",
34
- )
35
-
36
- # Verbose/quiet options for log level override
37
- parser.add_argument(
38
- "-v", "--verbose", action="store_true", help="Enable verbose logging (DEBUG level)"
39
- )
40
- parser.add_argument(
41
- "-q", "--quiet", action="store_true", help="Enable quiet mode (WARNING level only)"
42
- )
43
-
44
- # Get CLI parameters
45
- cli_params = self.config_manager.get_cli_parameters()
46
-
47
- # Generate arguments from CLI config parameters
48
- for param in cli_params:
49
- if param.required and param.cli_arg is None:
50
- # Positional argument
51
- parser.add_argument(param.name, help=param.help)
52
- else:
53
- # Optional argument
54
- kwargs = {
55
- "default": argparse.SUPPRESS,
56
- "help": f"{param.help} (default: {param.default})",
57
- }
58
-
59
- # Handle different parameter types
60
- if param.choices and param.type_ != bool:
61
- kwargs["choices"] = param.choices
62
-
63
- if param.type_ == int:
64
- kwargs["type"] = int
65
- elif param.type_ == float:
66
- kwargs["type"] = float
67
- elif param.type_ == bool:
68
- kwargs["action"] = "store_true" if not param.default else "store_false"
69
- kwargs["help"] = f"{param.help} (default: {param.default})"
70
- elif param.type_ == str:
71
- kwargs["type"] = str
72
-
73
- parser.add_argument(param.cli_arg, **kwargs)
74
-
75
- return parser
76
-
77
- def create_config_overrides(self, args: argparse.Namespace) -> dict[str, Any]:
78
- """Create configuration overrides from CLI arguments."""
79
- cli_params = self.config_manager.get_cli_parameters()
80
- overrides = {}
81
-
82
- for param in cli_params:
83
- if hasattr(args, param.name):
84
- arg_value = getattr(args, param.name)
85
- # Add CLI category prefix for override system
86
- overrides[f"cli__{param.name}"] = arg_value
87
-
88
- # Handle log level overrides from verbose/quiet flags
89
- if hasattr(args, "verbose") and args.verbose:
90
- overrides["app__log_level"] = "DEBUG"
91
- elif hasattr(args, "quiet") and args.quiet:
92
- overrides["app__log_level"] = "WARNING"
93
-
94
- return overrides
95
-
96
- def run_cli(
97
- self,
98
- main_function: Callable[[ConfigManager], int],
99
- description: str = None,
100
- validator: Callable[[ConfigManager], bool] = None,
101
- ) -> int:
102
- """Run the CLI application with error handling.
103
-
104
- Args:
105
- main_function: Function that takes ConfigManager and returns exit code
106
- description: CLI description
107
- validator: Optional function to validate configuration
108
-
109
- Returns:
110
- Exit code
111
- """
112
- logger = None
113
-
114
- try:
115
- # Parse command line arguments
116
- parser = self.create_argument_parser(description)
117
- args = parser.parse_args()
118
-
119
- # Create configuration overrides from CLI arguments
120
- cli_overrides = self.create_config_overrides(args)
121
-
122
- # Create new config manager with overrides
123
- config_file = args.config if hasattr(args, "config") and args.config else None
124
- updated_config = ConfigManager(config_file=config_file, **cli_overrides)
125
-
126
- # Copy categories from original config manager
127
- for name, category in self.config_manager._categories.items():
128
- updated_config.add_category(name, category)
129
-
130
- # Apply overrides again after copying categories
131
- updated_config._apply_kwargs(cli_overrides)
132
-
133
- # Try to get logger if logging is configured
134
- try:
135
- from .logging import get_logger
136
-
137
- logger = get_logger(f"{self.app_name}.cli")
138
- logger.info(f"Starting {self.app_name} CLI")
139
- logger.debug(f"Command line arguments: {vars(args)}")
140
- except ImportError:
141
- pass
142
-
143
- # Validate configuration if validator provided
144
- if validator and not validator(updated_config):
145
- if logger:
146
- logger.error("Configuration validation failed")
147
- else:
148
- print("Configuration validation failed")
149
- return 1
150
-
151
- # Run main function
152
- return main_function(updated_config)
153
-
154
- except FileNotFoundError as e:
155
- if logger:
156
- logger.error(f"File not found: {e}")
157
- logger.debug("Full traceback:", exc_info=True)
158
- else:
159
- print(f"Error: {e}")
160
- traceback.print_exc()
161
- return 1
162
-
163
- except KeyboardInterrupt:
164
- if logger:
165
- logger.warning("Process interrupted by user")
166
- else:
167
- print("Process interrupted by user")
168
- return 130
169
-
170
- except Exception as e:
171
- if logger:
172
- logger.error(f"Unexpected error: {e}")
173
- logger.debug("Full traceback:", exc_info=True)
174
- else:
175
- print(f"Unexpected error: {e}")
176
- traceback.print_exc()
177
- return 1
@@ -1,362 +0,0 @@
1
- # config_framework/core.py
2
- """Generic configuration framework for Python applications."""
3
-
4
- import json
5
- from abc import ABC, abstractmethod
6
- from dataclasses import dataclass
7
- from pathlib import Path
8
- from textwrap import dedent
9
- from typing import Any
10
-
11
- import yaml
12
- from pydantic import BaseModel
13
-
14
-
15
- @dataclass
16
- class ConfigParameter:
17
- """Represents a single configuration parameter with all its metadata."""
18
-
19
- name: str
20
- default: Any
21
- type_: type
22
- choices: list[str | bool] = None
23
- help: str = ""
24
- cli_arg: str = None
25
- required: bool = False
26
- category: str = "general"
27
-
28
- def __post_init__(self):
29
- if self.cli_arg is None and not self.required:
30
- self.cli_arg = f"--{self.name}"
31
- if self.type_ is bool and self.choices is None:
32
- self.choices = [True, False]
33
-
34
-
35
- class BaseConfigCategory(BaseModel, ABC):
36
- """Base class for configuration categories."""
37
-
38
- @abstractmethod
39
- def get_category_name(self) -> str:
40
- """Return the category name for this configuration group."""
41
- pass
42
-
43
- def get_parameters(self) -> list[ConfigParameter]:
44
- """Get all ConfigParameter objects from this category."""
45
- parameters = []
46
- for field_name in self.model_fields:
47
- param = getattr(self, field_name)
48
- if isinstance(param, ConfigParameter):
49
- param.category = self.get_category_name()
50
- parameters.append(param)
51
- return parameters
52
-
53
-
54
- class CliConfigCategory(BaseConfigCategory):
55
- """Base class for CLI-specific configuration parameters."""
56
-
57
- def get_category_name(self) -> str:
58
- return "cli"
59
-
60
-
61
- class ConfigManager:
62
- """Generic configuration manager that can handle multiple configuration categories."""
63
-
64
- def __init__(self, config_file: str = None, **kwargs):
65
- """Initialize configuration manager.
66
-
67
- Args:
68
- config_file: Path to configuration file (JSON or YAML)
69
- **kwargs: Override parameters in format category__parameter
70
- """
71
- self._categories: dict[str, BaseConfigCategory] = {}
72
- self._cli_category_name: str = None
73
-
74
- # Load from file if provided
75
- if config_file:
76
- self.load_from_file(config_file)
77
-
78
- # Override with provided kwargs
79
- self._apply_kwargs(kwargs)
80
-
81
- def add_category(self, name: str, category: BaseConfigCategory):
82
- """Add a configuration category.
83
-
84
- Args:
85
- name: Name of the category (e.g., 'cli', 'app', 'gui')
86
- category: Configuration category instance
87
- """
88
- self._categories[name] = category
89
- if isinstance(category, CliConfigCategory):
90
- self._cli_category_name = name
91
-
92
- def get_category(self, name: str) -> BaseConfigCategory:
93
- """Get a configuration category by name."""
94
- return self._categories.get(name)
95
-
96
- def get_cli_category(self) -> BaseConfigCategory | None:
97
- """Get the CLI configuration category."""
98
- if self._cli_category_name:
99
- return self._categories[self._cli_category_name]
100
- return None
101
-
102
- def _apply_kwargs(self, kwargs: dict[str, Any]):
103
- """Apply keyword arguments to override configuration values."""
104
- for key, value in kwargs.items():
105
- if "__" in key:
106
- category_name, param_name = key.split("__", 1)
107
- if category_name in self._categories:
108
- category = self._categories[category_name]
109
- if hasattr(category, param_name):
110
- param = getattr(category, param_name)
111
- if isinstance(param, ConfigParameter):
112
- param.default = value
113
-
114
- def load_from_file(self, config_file: str):
115
- """Load configuration from JSON or YAML file."""
116
- config_path = Path(config_file)
117
- if not config_path.exists():
118
- raise FileNotFoundError(f"Configuration file not found: {config_file}")
119
-
120
- with open(config_path, "r", encoding="utf-8") as f:
121
- if config_path.suffix.lower() in [".yml", ".yaml"]:
122
- config_data = yaml.safe_load(f)
123
- else:
124
- config_data = json.load(f)
125
-
126
- # Apply loaded configuration
127
- for category_name, category_data in config_data.items():
128
- if category_name in self._categories:
129
- category = self._categories[category_name]
130
- for param_name, param_value in category_data.items():
131
- if hasattr(category, param_name):
132
- param = getattr(category, param_name)
133
- if isinstance(param, ConfigParameter):
134
- param.default = param_value
135
-
136
- def save_to_file(self, config_file: str, format_: str = "auto"):
137
- """Save current configuration to file."""
138
- config_path = Path(config_file)
139
- config_data = self.to_dict()
140
-
141
- # Determine format
142
- if format_ == "auto":
143
- format_ = "yaml" if config_path.suffix.lower() in [".yml", ".yaml"] else "json"
144
-
145
- # Ensure directory exists
146
- config_path.parent.mkdir(parents=True, exist_ok=True)
147
-
148
- with open(config_path, "w", encoding="utf-8") as f:
149
- if format_ == "yaml":
150
- yaml.dump(config_data, f, default_flow_style=False, indent=2)
151
- else:
152
- json.dump(config_data, f, indent=2)
153
-
154
- def to_dict(self) -> dict[str, Any]:
155
- """Convert configuration to dictionary."""
156
- result = {}
157
- for category_name, category in self._categories.items():
158
- category_dict = {}
159
- for param in category.get_parameters():
160
- category_dict[param.name] = param.default
161
- result[category_name] = category_dict
162
- return result
163
-
164
- def get_all_parameters(self) -> list[ConfigParameter]:
165
- """Get all parameters from all categories."""
166
- parameters = []
167
- for category in self._categories.values():
168
- parameters.extend(category.get_parameters())
169
- return parameters
170
-
171
- def get_cli_parameters(self) -> list[ConfigParameter]:
172
- """Get only CLI parameters."""
173
- cli_category = self.get_cli_category()
174
- if cli_category:
175
- return cli_category.get_parameters()
176
- return []
177
-
178
-
179
- class DocumentationGenerator:
180
- """Generates documentation and configuration files from ConfigManager."""
181
-
182
- def __init__(self, config_manager: ConfigManager):
183
- self.config_manager = config_manager
184
-
185
- def generate_config_markdown_doc(self, output_file: str):
186
- """Generate Markdown documentation for all configuration parameters."""
187
-
188
- def pad(s, width):
189
- return s + " " * (width - len(s))
190
-
191
- markdown_content = dedent("""
192
- # Configuration Parameters
193
-
194
- These parameters are available to configure the behavior of your application.
195
- The parameters in the cli category can be accessed via the command line interface.
196
-
197
- """).lstrip()
198
-
199
- for category_name, category in self.config_manager._categories.items():
200
- markdown_content += f'## Category "{category_name}"\n\n'
201
-
202
- # Collect all parameters for this category
203
- rows = []
204
- header = ["Name", "Type", "Description", "Default", "Choices"]
205
-
206
- for param in category.get_parameters():
207
- name = param.name
208
- typ = param.type_.__name__
209
- desc = param.help
210
- default = repr(param.default)
211
- choices = str(param.choices) if param.choices else "-"
212
-
213
- rows.append((name, typ, desc, default, choices))
214
-
215
- if not rows:
216
- continue
217
-
218
- # Calculate column widths
219
- all_rows = [header] + rows
220
- widths = [max(len(str(col)) for col in column) for column in zip(*all_rows)]
221
-
222
- # Create Markdown table
223
- table = (
224
- "| "
225
- + " | ".join(pad(h, w) for h, w in zip(header, widths))
226
- + " |\n"
227
- + "|-"
228
- + "-|-".join("-" * w for w in widths)
229
- + "-|\n"
230
- )
231
- for row in rows:
232
- table += "| " + " | ".join(pad(str(col), w) for col, w in zip(row, widths)) + " |\n"
233
-
234
- markdown_content += table + "\n"
235
-
236
- # Write to file
237
- Path(output_file).parent.mkdir(parents=True, exist_ok=True)
238
- with open(output_file, "w", encoding="utf-8") as f:
239
- f.write(markdown_content)
240
-
241
- def generate_default_config_file(self, output_file: str):
242
- """Generate a default configuration file with all parameters and descriptions."""
243
- output_path = Path(output_file)
244
- output_path.parent.mkdir(parents=True, exist_ok=True)
245
-
246
- with open(output_path, "w", encoding="utf-8") as f:
247
- f.write("# Configuration File\n")
248
- f.write("# This file was auto-generated. Modify as needed.\n\n")
249
-
250
- for category_name, category in self.config_manager._categories.items():
251
- f.write(f"# {category_name.upper()} Configuration\n")
252
- f.write(f"{category_name}:\n")
253
-
254
- for param in category.get_parameters():
255
- f.write(f" # {param.help}\n")
256
- if param.choices:
257
- f.write(f" # Choices: {param.choices}\n")
258
- f.write(f" # Type: {param.type_.__name__}\n")
259
- f.write(f" {param.name}: {repr(param.default)}\n\n")
260
-
261
- f.write("\n")
262
-
263
- def generate_cli_markdown_doc(self, output_file: str, app_name: str = "app"):
264
- """Generate Markdown CLI documentation."""
265
- cli_params = self.config_manager.get_cli_parameters()
266
-
267
- if not cli_params:
268
- return
269
-
270
- rows = []
271
- required_params = []
272
- optional_params = []
273
-
274
- for param in cli_params:
275
- cli_arg = f"`--{param.name}`" if not param.required else f"`{param.name}`"
276
- typ = param.type_.__name__
277
- desc = param.help
278
- default = (
279
- "*required*"
280
- if param.required or param.default in (None, "")
281
- else repr(param.default)
282
- )
283
- choices = str(param.choices) if param.choices else "-"
284
-
285
- rows.append((cli_arg, typ, desc, default, choices))
286
- if default == "*required*":
287
- required_params.append(param)
288
- else:
289
- optional_params.append(param)
290
-
291
- # Generate table
292
- def pad(s, width):
293
- return s + " " * (width - len(s))
294
-
295
- widths = [max(len(str(col)) for col in column) for column in zip(*rows)]
296
- header = ["Option", "Type", "Description", "Default", "Choices"]
297
-
298
- table = (
299
- "| "
300
- + " | ".join(pad(h, w) for h, w in zip(header, widths))
301
- + " |\n"
302
- + "|-"
303
- + "-|-".join("-" * w for w in widths)
304
- + "-|\n"
305
- )
306
- for row in rows:
307
- table += "| " + " | ".join(pad(str(col), w) for col, w in zip(row, widths)) + " |\n"
308
-
309
- # Generate examples
310
- examples = []
311
- required_arg = required_params[0].name if required_params else "example.input"
312
-
313
- examples.append(
314
- dedent(
315
- f"""
316
- ### 1. Basic usage
317
-
318
- ```bash
319
- python -m {app_name} {required_arg}
320
- ```
321
- """
322
- )
323
- )
324
-
325
- # Add more examples with optional parameters
326
- for i, param in enumerate(optional_params[:3], 2):
327
- if param.name in ["verbose", "quiet"]:
328
- continue
329
- example_value = param.choices[0] if param.choices else param.default
330
- examples.append(
331
- dedent(f"""
332
- ### {i}. With {param.name} parameter
333
-
334
- ```bash
335
- python -m {app_name} --{param.name} {example_value} {required_arg}
336
- ```
337
- """)
338
- )
339
-
340
- markdown = dedent(
341
- f"""
342
- # Command Line Interface
343
-
344
- Command line options for {app_name}
345
-
346
- ```bash
347
- python -m {app_name} [OPTIONS] {required_arg if required_params else ""}
348
- ```
349
-
350
- ## Options
351
-
352
- {table}
353
-
354
- ## Examples
355
-
356
- {"".join(examples)}
357
- """
358
- ).strip()
359
-
360
- Path(output_file).parent.mkdir(parents=True, exist_ok=True)
361
- with open(output_file, "w", encoding="utf-8") as f:
362
- f.write(markdown)
@@ -1,225 +0,0 @@
1
- # config_framework/gui.py
2
- """Generic GUI settings dialog generator for configuration framework."""
3
-
4
- import tkinter as tk
5
- from tkinter import messagebox, ttk
6
-
7
- from generic_config_cli_gui.config_framework import ConfigManager, ConfigParameter
8
-
9
-
10
- class ToolTip:
11
- """Create a tooltip for a given widget."""
12
-
13
- def __init__(self, widget, text="widget info"):
14
- self.widget = widget
15
- self.text = text
16
- self.tipwindow = None
17
- self.id = None
18
- self.x = self.y = 0
19
-
20
- self.widget.bind("<Enter>", self.on_enter)
21
- self.widget.bind("<Leave>", self.on_leave)
22
-
23
- def on_enter(self, event=None):
24
- self.show_tooltip()
25
-
26
- def on_leave(self, event=None):
27
- self.hide_tooltip()
28
-
29
- def show_tooltip(self):
30
- if self.tipwindow or not self.text:
31
- return
32
- x, y, cx, cy = self.widget.bbox("insert")
33
- x = x + self.widget.winfo_rootx() + 25
34
- y = y + cy + self.widget.winfo_rooty() + 25
35
- self.tipwindow = tw = tk.Toplevel(self.widget)
36
- tw.wm_overrideredirect(True)
37
- tw.wm_geometry(f"+{x}+{y}")
38
- label = tk.Label(
39
- tw,
40
- text=self.text,
41
- justify=tk.LEFT,
42
- background="#ffffe0",
43
- relief=tk.SOLID,
44
- borderwidth=1,
45
- font=("tahoma", "8", "normal"),
46
- )
47
- label.pack(ipadx=1)
48
-
49
- def hide_tooltip(self):
50
- tw = self.tipwindow
51
- self.tipwindow = None
52
- if tw:
53
- tw.destroy()
54
-
55
-
56
- class SettingsDialogGenerator:
57
- """Generates settings dialog from ConfigManager."""
58
-
59
- def __init__(self, config_manager: ConfigManager):
60
- self.config_manager = config_manager
61
-
62
- def create_settings_dialog(
63
- self, parent, title="Settings", config_file="config.yaml"
64
- ) -> "GenericSettingsDialog":
65
- """Create a settings dialog for the configuration."""
66
- return GenericSettingsDialog(parent, self.config_manager, title, config_file)
67
-
68
-
69
- class GenericSettingsDialog:
70
- """Generic settings dialog for ConfigManager."""
71
-
72
- def __init__(
73
- self, parent, config_manager: ConfigManager, title="Settings", config_file="config.yaml"
74
- ):
75
- self.parent = parent
76
- self.config_manager = config_manager
77
- self.config_file = config_file
78
- self.result = None
79
- self.widgets = {}
80
-
81
- # Create dialog window
82
- self.dialog = tk.Toplevel(parent)
83
- self.dialog.title(title)
84
- self.dialog.geometry("600x500")
85
- self.dialog.transient(parent)
86
- self.dialog.grab_set()
87
-
88
- # Center the dialog
89
- self.dialog.geometry(f"+{int(parent.winfo_rootx() + 50)}+{int(parent.winfo_rooty() + 50)}")
90
-
91
- self._create_widgets()
92
-
93
- # Handle window closing
94
- self.dialog.protocol("WM_DELETE_WINDOW", self._on_cancel)
95
-
96
- def _create_widgets(self):
97
- """Create the settings dialog widgets."""
98
- # Main frame
99
- main_frame = ttk.Frame(self.dialog)
100
- main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
101
-
102
- # Create notebook for tabs
103
- self.notebook = ttk.Notebook(main_frame)
104
- self.notebook.pack(fill=tk.BOTH, expand=True)
105
-
106
- # Create tabs for each configuration category
107
- for category_name, category in self.config_manager._categories.items():
108
- self._create_category_tab(category_name, category)
109
-
110
- # Button frame
111
- button_frame = ttk.Frame(main_frame)
112
- button_frame.pack(fill=tk.X, pady=(10, 0))
113
-
114
- ttk.Button(button_frame, text="OK", command=self._on_ok).pack(side=tk.RIGHT, padx=(5, 0))
115
- ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(side=tk.RIGHT)
116
-
117
- def _create_category_tab(self, category_name: str, category):
118
- """Create a tab for a configuration category."""
119
- # Create tab frame
120
- tab_frame = ttk.Frame(self.notebook)
121
- self.notebook.add(tab_frame, text=category_name.title())
122
-
123
- # Create scrollable frame
124
- canvas = tk.Canvas(tab_frame)
125
- scrollbar = ttk.Scrollbar(tab_frame, orient="vertical", command=canvas.yview)
126
- scrollable_frame = ttk.Frame(canvas)
127
-
128
- scrollable_frame.bind(
129
- "<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
130
- )
131
-
132
- canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
133
- canvas.configure(yscrollcommand=scrollbar.set)
134
-
135
- # Add parameters
136
- self._add_category_parameters(scrollable_frame, category_name, category)
137
-
138
- canvas.pack(side="left", fill="both", expand=True)
139
- scrollbar.pack(side="right", fill="y")
140
-
141
- def _add_category_parameters(self, parent, category_name: str, category):
142
- """Add parameter widgets for a specific category."""
143
- row = 0
144
- parameters = category.get_parameters()
145
-
146
- for param in parameters:
147
- if param.required:
148
- # Skip required parameters as they are not configurable in GUI
149
- continue
150
-
151
- # Create label
152
- label = ttk.Label(parent, text=f"{param.name}:")
153
- label.grid(row=row, column=0, sticky="w", padx=5, pady=2)
154
-
155
- # Create appropriate widget based on parameter type
156
- widget = self._create_parameter_widget(parent, param)
157
- widget.grid(row=row, column=1, sticky="ew", padx=5, pady=2)
158
-
159
- # Add tooltip
160
- ToolTip(label, param.help)
161
- ToolTip(widget, param.help)
162
-
163
- # Store widget reference
164
- self.widgets[f"{category_name}__{param.name}"] = widget
165
-
166
- row += 1
167
-
168
- # Configure column weights
169
- parent.columnconfigure(1, weight=1)
170
-
171
- def _create_parameter_widget(self, parent, param: ConfigParameter):
172
- """Create appropriate widget for parameter type."""
173
- if param.type_ == bool:
174
- # Checkbox for boolean values
175
- var = tk.BooleanVar(value=param.default)
176
- widget = ttk.Checkbutton(parent, variable=var)
177
- widget.var = var
178
- return widget
179
-
180
- elif param.choices and param.type_ != bool:
181
- # Combobox for choices
182
- var = tk.StringVar(value=str(param.default))
183
- widget = ttk.Combobox(parent, textvariable=var, values=param.choices, state="readonly")
184
- widget.var = var
185
- return widget
186
-
187
- elif param.type_ == int:
188
- # Spinbox for integers
189
- var = tk.IntVar(value=param.default)
190
- widget = ttk.Spinbox(parent, from_=-999999, to=999999, textvariable=var)
191
- widget.var = var
192
- return widget
193
-
194
- else: # str or other types
195
- # Entry for strings
196
- var = tk.StringVar(value=str(param.default))
197
- widget = ttk.Entry(parent, textvariable=var)
198
- widget.var = var
199
- return widget
200
-
201
- def _on_ok(self):
202
- """Handle OK button click."""
203
- try:
204
- # Update configuration with widget values
205
- overrides = {}
206
- for key, widget in self.widgets.items():
207
- value = widget.var.get()
208
- overrides[key] = value
209
-
210
- # Apply overrides to config manager
211
- self.config_manager._apply_kwargs(overrides)
212
-
213
- # Save to file
214
- self.config_manager.save_to_file(self.config_file)
215
-
216
- self.result = "ok"
217
- self.dialog.destroy()
218
-
219
- except Exception as e:
220
- messagebox.showerror("Error", f"Failed to save configuration: {e}")
221
-
222
- def _on_cancel(self):
223
- """Handle Cancel button click."""
224
- self.result = "cancel"
225
- self.dialog.destroy()
@@ -1,27 +0,0 @@
1
- __init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- firewall_handler.py,sha256=qT0dInkeKHulpCQD2x3HH7_lMxz4lwdkIPxEvBSl3No,7336
3
- main.py,sha256=d6ZpnMxcPBf860gQmuYwPVJg1di6r2CR6gSvFLkAZkk,4448
4
- generic_config_cli_gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- generic_config_cli_gui/cli_generator.py,sha256=9eN8Lyv3d6bY8LlOoHyAeFAqu1IQFQcQm-zpZ_CjsS4,6396
6
- generic_config_cli_gui/config_framework.py,sha256=0DttxYz_tnB_qJ6wM-KMwiGXsQzCEr9UbDh84DpxdGM,12514
7
- generic_config_cli_gui/gui_generator.py,sha256=nB8pRp1eihOG2Cfiz58nY1M6bHfVMGhirW-9NkmhteQ,7628
8
- python_template_project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- python_template_project/__main__.py,sha256=JS28LiHoU9Xo4c_RvyLBTDKHqdZRRytJLj6qyrg26mQ,195
10
- python_template_project/_version.py,sha256=iB5DfB5V6YB5Wo4JmvS-txT42QtmGaWcWp3udRT7zCI,511
11
- python_template_project/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- python_template_project/cli/__main__.py,sha256=qnGkoNNbhnvLTA24X8YTEDxRqDbNEAzgjp9Ki_JYIhs,184
13
- python_template_project/cli/cli.py,sha256=j7LliSLjrntcjDJ5uWpVu8RxriA67xNnytLJYfsrWys,4284
14
- python_template_project/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- python_template_project/config/config.py,sha256=nI4Lh2VC4wHnDnJ8e1T0cuzbv1FDMkpGKmtMJ0pMBFY,5804
16
- python_template_project/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- python_template_project/core/base.py,sha256=3S_DSPgPom0wCS_tgdEaNpWub0r8GlytmjKtXlIypRA,28045
18
- python_template_project/core/logging.py,sha256=kfoEWppFh2ihQMFcSXpb4A2mYUnJdYxwXrYScCCAhZo,7015
19
- python_template_project/gui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- python_template_project/gui/__main__.py,sha256=5AaiBxNCAKWcuSQFcQ_lsEQfEsXm6BA5-i5MScdf30c,184
21
- python_template_project/gui/gui.py,sha256=bh51l_TPt-Oa6zbV9WMJJFJPQKW_-Kz1M-TyCJ3ppao,21794
22
- python_template_project-0.2.0.dist-info/licenses/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
23
- python_template_project-0.2.0.dist-info/METADATA,sha256=dhQLBOZC2Z94kIji4GZaTYiO_LYLOlxnUXbVVDSilhs,7948
24
- python_template_project-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
- python_template_project-0.2.0.dist-info/entry_points.txt,sha256=LmgMiqxIVRdD7gkktVBBR0CR4AhkugqlgifuglkPhtI,148
26
- python_template_project-0.2.0.dist-info/top_level.txt,sha256=xtUN1Sz5ZjTDsvgfGQpSu4sxObl1HOHgzgb4JAQ1MtA,78
27
- python_template_project-0.2.0.dist-info/RECORD,,