forgepy-cli 1.0.0__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.
- builders/__init__.py +0 -0
- builders/base_builder.py +17 -0
- builders/file_builder.py +31 -0
- builders/folder_builder.py +30 -0
- builders/python_tools_builder.py +64 -0
- cli/__init__.py +3 -0
- cli/command.py +52 -0
- cli/commands/__init__.py +32 -0
- cli/commands/component_command.py +177 -0
- cli/commands/config_command.py +174 -0
- cli/commands/create_command.py +161 -0
- cli/commands/list_command.py +40 -0
- cli/commands/version_command.py +29 -0
- cli/dispatcher.py +61 -0
- cli/parser.py +84 -0
- components/__init__.py +1 -0
- components/base_component.py +30 -0
- components/component_context.py +21 -0
- components/component_installer.py +44 -0
- components/component_manifest.py +102 -0
- components/component_metadata.py +50 -0
- components/component_registry.py +74 -0
- components/component_state.py +243 -0
- components/component_validation.py +88 -0
- components/github_actions_component.py +75 -0
- components/pytest_component.py +40 -0
- components/ruff_component.py +40 -0
- config/__init__.py +0 -0
- config/default_structure.py +18 -0
- config/user_config.py +238 -0
- config/version.py +16 -0
- core/__init__.py +0 -0
- core/environment_builder.py +33 -0
- core/git_builder.py +90 -0
- core/project_generator.py +125 -0
- core/requirements_installer.py +50 -0
- core/vscode_builder.py +103 -0
- forgepy_cli-1.0.0.dist-info/METADATA +176 -0
- forgepy_cli-1.0.0.dist-info/RECORD +74 -0
- forgepy_cli-1.0.0.dist-info/WHEEL +5 -0
- forgepy_cli-1.0.0.dist-info/entry_points.txt +2 -0
- forgepy_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
- forgepy_cli-1.0.0.dist-info/top_level.txt +8 -0
- main.py +20 -0
- models/__init__.py +1 -0
- models/project_config.py +87 -0
- templates/__init__.py +0 -0
- templates/app_template.py +20 -0
- templates/basic/basic_files.py +23 -0
- templates/basic/basic_template.py +32 -0
- templates/changelog_template.py +15 -0
- templates/cli/cli_files.py +70 -0
- templates/cli/cli_template.py +55 -0
- templates/env_template.py +20 -0
- templates/gitignore_template.py +27 -0
- templates/library/library_files.py +28 -0
- templates/library/library_template.py +56 -0
- templates/license_template.py +21 -0
- templates/pyproject_template.py +15 -0
- templates/readme_template.py +15 -0
- templates/requirements_template.py +13 -0
- templates/template_engine/base_template.py +58 -0
- templates/template_engine/file_template.py +76 -0
- templates/template_engine/package_name.py +30 -0
- templates/template_engine/template_context.py +24 -0
- templates/template_engine/template_files.py +10 -0
- templates/template_engine/template_metadata.py +59 -0
- templates/template_engine/template_registry.py +107 -0
- templates/template_manager.py +58 -0
- templates/vscode/__init__.py +0 -0
- templates/vscode/extensions_template.py +32 -0
- templates/vscode/launch_template.py +41 -0
- templates/vscode/settings_template.py +37 -0
- templates/vscode/tasks_template.py +66 -0
builders/__init__.py
ADDED
|
File without changes
|
builders/base_builder.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Module : Base Builder
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BaseBuilder:
|
|
10
|
+
"""
|
|
11
|
+
Parent class seluruh Builder ForgePy.
|
|
12
|
+
|
|
13
|
+
Digunakan sebagai parent agar seluruh Builder
|
|
14
|
+
mempunyai struktur yang konsisten.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
pass
|
builders/file_builder.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Module : File Builder
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from builders.base_builder import BaseBuilder
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FileBuilder(BaseBuilder):
|
|
14
|
+
|
|
15
|
+
def write(
|
|
16
|
+
self,
|
|
17
|
+
path: Path,
|
|
18
|
+
content: str,
|
|
19
|
+
) -> None:
|
|
20
|
+
|
|
21
|
+
path.parent.mkdir(
|
|
22
|
+
parents=True,
|
|
23
|
+
exist_ok=True,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
path.write_text(
|
|
27
|
+
content,
|
|
28
|
+
encoding="utf-8",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
print(f"[OK] File dibuat : {path}")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Module : Folder Builder
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from builders.base_builder import BaseBuilder
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FolderBuilder(BaseBuilder):
|
|
14
|
+
|
|
15
|
+
def create(
|
|
16
|
+
self,
|
|
17
|
+
root: Path,
|
|
18
|
+
folders: list[str],
|
|
19
|
+
) -> None:
|
|
20
|
+
|
|
21
|
+
for folder in folders:
|
|
22
|
+
|
|
23
|
+
path = root / folder
|
|
24
|
+
|
|
25
|
+
path.mkdir(
|
|
26
|
+
parents=True,
|
|
27
|
+
exist_ok=True,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
print(f"[OK] Folder dibuat : {path}")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Module : Python Tools Builder
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from builders.base_builder import BaseBuilder
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
PACKAGE_TOOL_UPDATE_TIMEOUT_SECONDS = 300
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PythonToolsBuilder(BaseBuilder):
|
|
18
|
+
"""
|
|
19
|
+
Mengupdate pip, setuptools, dan wheel
|
|
20
|
+
pada Virtual Environment.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def update(
|
|
24
|
+
self,
|
|
25
|
+
project_path: Path,
|
|
26
|
+
) -> None:
|
|
27
|
+
|
|
28
|
+
python = project_path / ".venv" / "Scripts" / "python.exe"
|
|
29
|
+
|
|
30
|
+
if not python.exists():
|
|
31
|
+
print("[WARNING] Virtual Environment belum tersedia.")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
packages = [
|
|
35
|
+
"pip",
|
|
36
|
+
"setuptools",
|
|
37
|
+
"wheel",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
for package in packages:
|
|
41
|
+
|
|
42
|
+
print(f"[INFO] Mengupdate {package}...")
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
subprocess.run(
|
|
46
|
+
[
|
|
47
|
+
str(python),
|
|
48
|
+
"-m",
|
|
49
|
+
"pip",
|
|
50
|
+
"install",
|
|
51
|
+
"--upgrade",
|
|
52
|
+
package,
|
|
53
|
+
],
|
|
54
|
+
check=True,
|
|
55
|
+
timeout=PACKAGE_TOOL_UPDATE_TIMEOUT_SECONDS,
|
|
56
|
+
)
|
|
57
|
+
except subprocess.SubprocessError as error:
|
|
58
|
+
print(
|
|
59
|
+
"[ERROR] Packaging-tool update failed for "
|
|
60
|
+
f"'{package}': {error}"
|
|
61
|
+
)
|
|
62
|
+
raise
|
|
63
|
+
|
|
64
|
+
print(f"[OK] {package} berhasil diupdate.")
|
cli/__init__.py
ADDED
cli/command.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Module : Base Command
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from argparse import ArgumentParser, Namespace
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Command(ABC):
|
|
13
|
+
"""
|
|
14
|
+
Kontrak dasar untuk seluruh command ForgePy.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
name: str = ""
|
|
18
|
+
summary: str = ""
|
|
19
|
+
description: str = ""
|
|
20
|
+
|
|
21
|
+
def validate_registration(self) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Memastikan metadata command lengkap sebelum didaftarkan.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
for field in (
|
|
27
|
+
"name",
|
|
28
|
+
"summary",
|
|
29
|
+
"description",
|
|
30
|
+
):
|
|
31
|
+
if not getattr(self, field):
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"Command '{type(self).__name__}' harus memiliki {field}."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def configure_parser(
|
|
37
|
+
self,
|
|
38
|
+
parser: ArgumentParser,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Mendaftarkan argumen khusus command.
|
|
42
|
+
|
|
43
|
+
Command tanpa argumen dapat menggunakan implementasi default.
|
|
44
|
+
"""
|
|
45
|
+
del parser
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def execute(self, args: Namespace) -> int:
|
|
49
|
+
"""
|
|
50
|
+
Menjalankan command dan mengembalikan status proses CLI.
|
|
51
|
+
"""
|
|
52
|
+
raise NotImplementedError
|
cli/commands/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ForgePy Commands
|
|
3
|
+
|
|
4
|
+
Seluruh command bawaan didaftarkan pada module ini.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from cli.command import Command
|
|
8
|
+
from cli.commands.component_command import ComponentCommand
|
|
9
|
+
from cli.commands.config_command import ConfigCommand
|
|
10
|
+
from cli.commands.create_command import CreateCommand
|
|
11
|
+
from cli.commands.list_command import ListCommand
|
|
12
|
+
from cli.commands.version_command import VersionCommand
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
DEFAULT_COMMAND = "create"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def create_commands() -> tuple[Command, ...]:
|
|
19
|
+
"""
|
|
20
|
+
Membuat seluruh command bawaan dalam urutan tampilan CLI.
|
|
21
|
+
|
|
22
|
+
Command baru hanya perlu ditambahkan pada catalog ini agar parser
|
|
23
|
+
dan dispatcher menggunakan registrasi yang sama.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
CreateCommand(),
|
|
28
|
+
VersionCommand(),
|
|
29
|
+
ListCommand(),
|
|
30
|
+
ConfigCommand(),
|
|
31
|
+
ComponentCommand(),
|
|
32
|
+
)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""CLI access to registered ForgePy components."""
|
|
2
|
+
|
|
3
|
+
from argparse import ArgumentParser, Namespace
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from cli.command import Command
|
|
7
|
+
from components.component_installer import (
|
|
8
|
+
ComponentAlreadyInstalledError,
|
|
9
|
+
ComponentInstaller,
|
|
10
|
+
)
|
|
11
|
+
from components.component_registry import ComponentRegistry
|
|
12
|
+
from components.component_state import (
|
|
13
|
+
ComponentStateStore,
|
|
14
|
+
ForgePyComponentStateError,
|
|
15
|
+
)
|
|
16
|
+
from components.component_validation import ComponentValidationError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ComponentCommand(Command):
|
|
20
|
+
"""List available or installed components, or install one."""
|
|
21
|
+
|
|
22
|
+
name = "component"
|
|
23
|
+
summary = "List, inspect, or add ForgePy components."
|
|
24
|
+
description = (
|
|
25
|
+
"List registered ForgePy components, inspect project-local installed "
|
|
26
|
+
"state, or add one to an existing project."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def __init__(self, registry: ComponentRegistry | None = None) -> None:
|
|
30
|
+
self._registry = registry
|
|
31
|
+
|
|
32
|
+
def configure_parser(self, parser: ArgumentParser) -> None:
|
|
33
|
+
actions = parser.add_subparsers(
|
|
34
|
+
dest="component_action",
|
|
35
|
+
title="component actions",
|
|
36
|
+
description="Available component actions",
|
|
37
|
+
metavar="ACTION",
|
|
38
|
+
required=True,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
actions.add_parser(
|
|
42
|
+
"list",
|
|
43
|
+
help="List registered built-in components.",
|
|
44
|
+
description=(
|
|
45
|
+
"List registered built-in component names and descriptions."
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
installed_parser = actions.add_parser(
|
|
50
|
+
"installed",
|
|
51
|
+
help="List components recorded as installed in a project.",
|
|
52
|
+
description=(
|
|
53
|
+
"List component names recorded in an explicitly supplied "
|
|
54
|
+
"existing project directory."
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
installed_parser.add_argument(
|
|
58
|
+
"--project",
|
|
59
|
+
required=True,
|
|
60
|
+
type=Path,
|
|
61
|
+
metavar="PATH",
|
|
62
|
+
help="Path to an existing project directory.",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
add_parser = actions.add_parser(
|
|
66
|
+
"add",
|
|
67
|
+
help="Add a registered component to an existing project.",
|
|
68
|
+
description=(
|
|
69
|
+
"Add a registered component to an explicitly supplied "
|
|
70
|
+
"existing project directory."
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
add_parser.add_argument(
|
|
74
|
+
"component_name",
|
|
75
|
+
metavar="NAME",
|
|
76
|
+
help="Registered component name.",
|
|
77
|
+
)
|
|
78
|
+
add_parser.add_argument(
|
|
79
|
+
"--project",
|
|
80
|
+
required=True,
|
|
81
|
+
type=Path,
|
|
82
|
+
metavar="PATH",
|
|
83
|
+
help="Path to an existing project directory.",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def execute(self, args: Namespace) -> int:
|
|
87
|
+
action = getattr(args, "component_action", None)
|
|
88
|
+
|
|
89
|
+
if action == "list":
|
|
90
|
+
self._list()
|
|
91
|
+
return 0
|
|
92
|
+
elif action == "installed":
|
|
93
|
+
return self._installed(args.project)
|
|
94
|
+
elif action == "add":
|
|
95
|
+
return self._add(args.component_name, args.project)
|
|
96
|
+
else:
|
|
97
|
+
print(f"[ERROR] Unknown component action: '{action}'.")
|
|
98
|
+
return 1
|
|
99
|
+
|
|
100
|
+
def _list(self) -> None:
|
|
101
|
+
print("=" * 40)
|
|
102
|
+
print(" ForgePy Components ")
|
|
103
|
+
print("=" * 40)
|
|
104
|
+
|
|
105
|
+
for component in self._get_registry().list_components():
|
|
106
|
+
metadata = component.metadata
|
|
107
|
+
print(f"- {metadata.name}: {metadata.description}")
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _installed(project_path: Path) -> int:
|
|
111
|
+
try:
|
|
112
|
+
installed_components = ComponentStateStore(project_path).load()
|
|
113
|
+
except ForgePyComponentStateError as error:
|
|
114
|
+
print(f"[ERROR] {error}")
|
|
115
|
+
return 1
|
|
116
|
+
except (TypeError, ValueError) as error:
|
|
117
|
+
print(f"[ERROR] Invalid project path '{project_path}': {error}")
|
|
118
|
+
return 1
|
|
119
|
+
|
|
120
|
+
if not installed_components:
|
|
121
|
+
print("No installed components.")
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
print("Installed components:")
|
|
125
|
+
for component_name in sorted(installed_components):
|
|
126
|
+
print(f"- {component_name}")
|
|
127
|
+
|
|
128
|
+
return 0
|
|
129
|
+
|
|
130
|
+
def _add(self, name: str, project_path: Path) -> int:
|
|
131
|
+
try:
|
|
132
|
+
self._get_registry().get(name)
|
|
133
|
+
except KeyError:
|
|
134
|
+
print(f"[ERROR] Unknown ForgePy component: '{name}'.")
|
|
135
|
+
return 1
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
ComponentInstaller(
|
|
139
|
+
registry=self._get_registry(),
|
|
140
|
+
).install(
|
|
141
|
+
name=name,
|
|
142
|
+
project_path=project_path,
|
|
143
|
+
)
|
|
144
|
+
except ComponentAlreadyInstalledError as error:
|
|
145
|
+
print(f"[ERROR] {error}")
|
|
146
|
+
return 1
|
|
147
|
+
except ComponentValidationError as error:
|
|
148
|
+
print(f"[ERROR] {error}")
|
|
149
|
+
return 1
|
|
150
|
+
except ForgePyComponentStateError as error:
|
|
151
|
+
print(f"[ERROR] {error}")
|
|
152
|
+
return 1
|
|
153
|
+
except (TypeError, ValueError) as error:
|
|
154
|
+
print(f"[ERROR] Invalid project path '{project_path}': {error}")
|
|
155
|
+
return 1
|
|
156
|
+
except FileExistsError as error:
|
|
157
|
+
target = error.filename or str(project_path)
|
|
158
|
+
print(
|
|
159
|
+
"[ERROR] Component installation refused because the target "
|
|
160
|
+
f"already exists: '{target}'."
|
|
161
|
+
)
|
|
162
|
+
return 1
|
|
163
|
+
except OSError as error:
|
|
164
|
+
print(f"[ERROR] Component installation failed: {error}")
|
|
165
|
+
return 1
|
|
166
|
+
|
|
167
|
+
print(
|
|
168
|
+
f"[OK] Component '{name}' added to project "
|
|
169
|
+
f"'{project_path}'."
|
|
170
|
+
)
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
def _get_registry(self) -> ComponentRegistry:
|
|
174
|
+
if self._registry is None:
|
|
175
|
+
self._registry = ComponentRegistry()
|
|
176
|
+
|
|
177
|
+
return self._registry
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Module : Config Command
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from argparse import ArgumentParser, Namespace
|
|
10
|
+
|
|
11
|
+
from cli.command import Command
|
|
12
|
+
from config.user_config import (
|
|
13
|
+
ConfigStore,
|
|
14
|
+
ForgePyConfigError,
|
|
15
|
+
UnknownConfigSettingError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConfigCommand(Command):
|
|
20
|
+
"""
|
|
21
|
+
Manage persistent ForgePy user configuration.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
name = "config"
|
|
25
|
+
summary = "Show or update ForgePy user configuration."
|
|
26
|
+
description = (
|
|
27
|
+
"Inspect or update the persistent ForgePy configuration stored "
|
|
28
|
+
"under the current user's home directory."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
store: ConfigStore | None = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
self._store = store
|
|
36
|
+
|
|
37
|
+
def configure_parser(
|
|
38
|
+
self,
|
|
39
|
+
parser: ArgumentParser,
|
|
40
|
+
) -> None:
|
|
41
|
+
actions = parser.add_subparsers(
|
|
42
|
+
dest="config_action",
|
|
43
|
+
title="config actions",
|
|
44
|
+
description="Available configuration actions",
|
|
45
|
+
metavar="ACTION",
|
|
46
|
+
required=True,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
actions.add_parser(
|
|
50
|
+
"show",
|
|
51
|
+
help="Show every supported configuration setting.",
|
|
52
|
+
description=(
|
|
53
|
+
"Show the effective ForgePy user configuration. "
|
|
54
|
+
"Missing files use safe defaults without being created."
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
set_parser = actions.add_parser(
|
|
59
|
+
"set",
|
|
60
|
+
help="Set and persist one supported configuration value.",
|
|
61
|
+
description=(
|
|
62
|
+
"Update one supported ForgePy configuration setting "
|
|
63
|
+
"while preserving all other values."
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
set_parser.add_argument(
|
|
67
|
+
"setting",
|
|
68
|
+
metavar="KEY",
|
|
69
|
+
help=(
|
|
70
|
+
"Setting to update. Supported keys: "
|
|
71
|
+
f"{self._supported_settings_text()}."
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
set_parser.add_argument(
|
|
75
|
+
"value",
|
|
76
|
+
metavar="VALUE",
|
|
77
|
+
help="String value to persist for the selected setting.",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
actions.add_parser(
|
|
81
|
+
"reset",
|
|
82
|
+
help="Reset and persist all settings to their defaults.",
|
|
83
|
+
description=(
|
|
84
|
+
"Replace the persisted ForgePy user configuration "
|
|
85
|
+
"with the safe defaults."
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def execute(self, args: Namespace) -> int:
|
|
90
|
+
action = getattr(
|
|
91
|
+
args,
|
|
92
|
+
"config_action",
|
|
93
|
+
None,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
if action == "show":
|
|
98
|
+
self._show()
|
|
99
|
+
elif action == "set":
|
|
100
|
+
self._set(
|
|
101
|
+
setting=args.setting,
|
|
102
|
+
value=args.value,
|
|
103
|
+
)
|
|
104
|
+
elif action == "reset":
|
|
105
|
+
self._reset()
|
|
106
|
+
else:
|
|
107
|
+
print(
|
|
108
|
+
"[ERROR] Unknown ForgePy configuration action: "
|
|
109
|
+
f"'{action}'."
|
|
110
|
+
)
|
|
111
|
+
return 1
|
|
112
|
+
except UnknownConfigSettingError as error:
|
|
113
|
+
print(f"[ERROR] {error}")
|
|
114
|
+
print(
|
|
115
|
+
"Supported settings: "
|
|
116
|
+
f"{self._supported_settings_text()}."
|
|
117
|
+
)
|
|
118
|
+
return 1
|
|
119
|
+
except ForgePyConfigError as error:
|
|
120
|
+
print(f"[ERROR] {error}")
|
|
121
|
+
return 1
|
|
122
|
+
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
def _show(self) -> None:
|
|
126
|
+
config = self._get_store().load()
|
|
127
|
+
|
|
128
|
+
print("=" * 40)
|
|
129
|
+
print(" ForgePy Configuration ")
|
|
130
|
+
print("=" * 40)
|
|
131
|
+
|
|
132
|
+
for setting, value in config.items():
|
|
133
|
+
print(
|
|
134
|
+
f"{setting} = "
|
|
135
|
+
f"{self._display_value(value)}"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
def _set(
|
|
139
|
+
self,
|
|
140
|
+
setting: str,
|
|
141
|
+
value: str,
|
|
142
|
+
) -> None:
|
|
143
|
+
updated = self._get_store().update(
|
|
144
|
+
setting,
|
|
145
|
+
value,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
print(
|
|
149
|
+
"[OK] ForgePy configuration updated: "
|
|
150
|
+
f"{setting} = {self._display_value(updated[setting])}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def _reset(self) -> None:
|
|
154
|
+
self._get_store().reset()
|
|
155
|
+
print("[OK] ForgePy configuration reset to defaults.")
|
|
156
|
+
|
|
157
|
+
def _get_store(self) -> ConfigStore:
|
|
158
|
+
if self._store is None:
|
|
159
|
+
self._store = ConfigStore()
|
|
160
|
+
|
|
161
|
+
return self._store
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def _display_value(value: str) -> str:
|
|
165
|
+
return json.dumps(
|
|
166
|
+
value,
|
|
167
|
+
ensure_ascii=False,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def _supported_settings_text() -> str:
|
|
172
|
+
return ", ".join(
|
|
173
|
+
ConfigStore.defaults()
|
|
174
|
+
)
|