Skip to content

Commit e49754f

Browse files
author
chenjunjie
committed
✨ feat: archieve the config command
1 parent 95bdef5 commit e49754f

File tree

7 files changed

+129
-28
lines changed

7 files changed

+129
-28
lines changed

opendigger_pycli/cli/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@
33
from .commands.config_cmd import config
44
from .commands.display_cmd import display
55
from .commands.export_cmd import export
6-
from .commands.monitor_cmd import monitor
7-
from .commands.report_cmd import report
86

97
opendigger.add_command(config)
108

119
query.add_command(display)
1210
query.add_command(export)
13-
query.add_command(report)
14-
query.add_command(monitor)
Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,107 @@
1-
import typing as t # noqa: F401
1+
import typing as t
2+
from dataclasses import fields
23

34
import click
5+
from click.shell_completion import CompletionItem
46

7+
from opendigger_pycli.console import CONSOLE
58

6-
@click.command("config")
7-
def config():
8-
pass
9+
from ..base import pass_environment
10+
from ..config import ALL_CONFIGS
11+
12+
if t.TYPE_CHECKING:
13+
from click.core import Context, Parameter
14+
15+
from opendigger_pycli.datatypes.config import BaseConfig
16+
17+
from ..base import Environment
18+
19+
20+
def config_shell_completion(
21+
ctx: t.Optional["Context"], param: t.Optional["Parameter"], incomplete: str
22+
) -> t.List[CompletionItem]:
23+
incomplete_splited = incomplete.rsplit(".", 1)[0]
24+
if incomplete_splited == "":
25+
return [
26+
CompletionItem(config_dataclass_key) for config_dataclass_key in ALL_CONFIGS
27+
]
28+
is_key = False
29+
last_config_dataclass: t.Optional[BaseConfig] = None
30+
for config_dataclass_key in ALL_CONFIGS:
31+
if incomplete_splited.startswith(config_dataclass_key):
32+
if incomplete_splited != config_dataclass_key:
33+
return [CompletionItem(config_dataclass_key + ".")]
34+
else:
35+
is_key = True
36+
last_config_dataclass = ALL_CONFIGS[config_dataclass_key] # type: ignore
37+
break
38+
else:
39+
continue
40+
41+
if not is_key and last_config_dataclass is None:
42+
return []
43+
44+
if incomplete[:-1] == incomplete_splited:
45+
return [CompletionItem(f"{incomplete_splited}.{field.name}") for field in fields(last_config_dataclass)] # type: ignore
46+
47+
for field in fields(last_config_dataclass): # type: ignore
48+
if incomplete in f"{incomplete_splited}.{field.name}":
49+
return [CompletionItem(f"{incomplete_splited}.{field.name}")]
50+
51+
return []
52+
53+
54+
def parse_config_key(key: str) -> t.Tuple[str, str]:
55+
section_name, config_key = key.rsplit(".", 1)
56+
return section_name, config_key
57+
58+
59+
def check_config_setting(
60+
ctx: "Context", param: "Parameter", values: t.List[t.Tuple[str, str]]
61+
) -> t.List[t.Tuple[str, str]]:
62+
for value in values:
63+
try:
64+
section_name, config_key = parse_config_key(value[0])
65+
except Exception:
66+
raise click.BadParameter(f"{value[0]} is not a valid config key")
67+
else:
68+
if section_name not in ALL_CONFIGS or config_key not in [
69+
field.name for field in fields(ALL_CONFIGS[section_name])
70+
]:
71+
raise click.BadParameter(f"{value[0]} is not a valid config key")
72+
return values
73+
74+
75+
@click.command("config") # type: ignore
76+
@click.option(
77+
"--set",
78+
"-s",
79+
"config_settings",
80+
type=(str, str),
81+
multiple=True,
82+
callback=check_config_setting,
83+
shell_complete=config_shell_completion,
84+
help="Set config value",
85+
required=True,
86+
)
87+
@click.option(
88+
"--persist/--no-persist",
89+
"-p/-n",
90+
is_flag=True,
91+
default=True,
92+
help="Set config value persistently",
93+
)
94+
@pass_environment
95+
def config(
96+
env: "Environment", config_settings: t.List[t.Tuple[str, str]], persist: bool
97+
) -> None:
98+
"""Set config value"""
99+
for config_setting in config_settings:
100+
key, value = config_setting
101+
section_name, key = parse_config_key(key)
102+
env.dlog(f"set config: {section_name}.{key}={value}")
103+
env.set_config(section_name, key, value, is_persist=persist)
104+
env.dlog(f"finished to set config: {section_name}.{key}={value}")
105+
106+
CONSOLE.print("[green]Config set successfully[/]")
107+
CONSOLE.print(env.cli_config)

opendigger_pycli/cli/commands/monitor_cmd.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

opendigger_pycli/cli/commands/report_cmd.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

opendigger_pycli/cli/config.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ def __init__(self):
2424
def user_config_file_path(self) -> str:
2525
config_dir_str = click.get_app_dir("opendigger-pycli")
2626
config_dir = Path(config_dir_str)
27+
config_dir.mkdir(parents=True, exist_ok=True)
2728
user_config = config_dir / "config.ini"
29+
if not user_config.exists():
30+
user_config.touch()
2831
return str(user_config)
2932

3033
@property
@@ -40,7 +43,9 @@ def __load_config(self):
4043
parser = configparser.RawConfigParser()
4144
parser.read(self.config_file_paths)
4245

43-
for config_dataclass in ALL_CONFIGS:
46+
for config_dataclass_key in ALL_CONFIGS:
47+
config_dataclass = ALL_CONFIGS[config_dataclass_key]
48+
config_dataclass = ALL_CONFIGS[config_dataclass_key]
4449
if not is_dataclass(config_dataclass):
4550
raise TypeError(f"{config_dataclass} is not a dataclass")
4651

@@ -54,8 +59,10 @@ def __load_config(self):
5459

5560
def update_config(self):
5661
parser = configparser.RawConfigParser()
62+
parser.read(self.config_file_paths)
5763

58-
for config_dataclass in ALL_CONFIGS:
64+
for config_dataclass_key in ALL_CONFIGS:
65+
config_dataclass = ALL_CONFIGS[config_dataclass_key]
5966
if not is_dataclass(config_dataclass):
6067
raise TypeError(f"{config_dataclass} is not a dataclass")
6168

@@ -75,7 +82,8 @@ def __rich_console__(
7582
self, console: "Console", options: "ConsoleOptions"
7683
) -> "RenderResult":
7784
yield "[b]OpenDigger Python CLI Configs:[/b]"
78-
for config_dataclass in ALL_CONFIGS:
85+
for config_dataclass_key in ALL_CONFIGS:
86+
config_dataclass = ALL_CONFIGS[config_dataclass_key]
7987
if not is_dataclass(config_dataclass):
8088
raise TypeError(f"{config_dataclass} is not a dataclass")
8189

opendigger_pycli/cli/env.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,14 @@ def set_log_level(
107107
self.log("[bold green]verbose mode enabled")
108108

109109
self.load_configs()
110+
111+
def set_config(
112+
self, section_name: str, key: str, value: str, *, is_persist: bool = True
113+
) -> None:
114+
config = getattr(self.cli_config, section_name.replace(".", "_"))
115+
setattr(config, key, value)
116+
if is_persist:
117+
self.cli_config.update_config()
118+
self.dlog(
119+
f"[bold green]set config {section_name}.{key} to {value} successfully"
120+
)

opendigger_pycli/datatypes/config.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@ class UserInfoConfig(BaseConfig):
2121
email: str = ""
2222

2323

24-
ALL_CONFIGS: t.List[t.Type[BaseConfig]] = [AppKeyConfig, UserInfoConfig]
24+
ALL_CONFIGS: t.Dict[str, t.Type[BaseConfig]] = {
25+
"app_keys": AppKeyConfig,
26+
"user_info": UserInfoConfig,
27+
}

0 commit comments

Comments
 (0)