Skip to content

Commit cb3dba3

Browse files
committed
setting
1 parent eee083b commit cb3dba3

File tree

17 files changed

+353
-52
lines changed

17 files changed

+353
-52
lines changed

main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from entari_cli import cli
22

3-
4-
cli.main("entari")
3+
cli.load_entry()
4+
cli.load_register("entari_cli.plugins")
5+
cli.main("entari setting --local abc.package_manager pdm")

src/entari_cli/commands/add.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from entari_cli import i18n_
77
from entari_cli.config import EntariConfig
8+
from entari_cli.project import get_project_root
89
from entari_cli.py_info import check_package_installed
910

1011

@@ -32,7 +33,7 @@ def dispatch(self, result: Arparma, next_: Next):
3233
name = result.query[str]("add.name")
3334
if not name:
3435
name = input(f"{Fore.BLUE}{i18n_.commands.add.prompts.name}{Fore.RESET}").strip()
35-
cfg = EntariConfig.load(result.query[str]("cfg_path.path", None))
36+
cfg = EntariConfig.load(result.query[str]("cfg_path.path", None), get_project_root())
3637
name_ = name.replace("::", "arclet.entari.builtins.")
3738
if check_package_installed(name_):
3839
pass

src/entari_cli/commands/config/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
YAML_PLUGIN_COMMON_TEMPLATE,
1818
YAML_PLUGIN_DEV_TEMPLATE,
1919
)
20+
from .path import ConfigPath # noqa: F401
21+
from ...project import get_project_root
2022

2123

2224
def check_env(file: Path):
@@ -59,10 +61,11 @@ def dispatch(self, result: Arparma, next_: Next):
5961
if result.find("config.new"):
6062
is_dev = result.find("config.new.dev")
6163
names = result.query[tuple[str, ...]]("config.new.plugins.names", ())
62-
if (path := result.query[str]("cfg_path.path", None)) is None:
63-
_path = Path.cwd() / "entari.yml"
64+
cwd = get_project_root()
65+
if cfg_path := result.query[str]("cfg_path.path", None):
66+
_path = Path(cfg_path)
6467
else:
65-
_path = Path(path)
68+
_path = cwd / "entari.yml"
6669
if _path.exists():
6770
return i18n_.commands.config.messages.exist(path=_path)
6871
if _path.suffix.startswith(".json"):
@@ -91,7 +94,7 @@ def dispatch(self, result: Arparma, next_: Next):
9194
return i18n_.commands.config.messages.created(path=_path)
9295
return i18n_.commands.config.messages.not_supported(suffix=_path.suffix)
9396
if result.find("config.current"):
94-
cfg = EntariConfig.load()
97+
cfg = EntariConfig.load(cwd=get_project_root())
9598
return i18n_.commands.config.messages.current(path=f"{Fore.BLUE}{cfg.path.resolve()!s}{Fore.RESET}")
9699
if result.find("config"):
97100
return self.command.get_help()

src/entari_cli/commands/init.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def init(self):
2020
return Alconna(
2121
"init",
2222
Option("-py|--python", Args["path/", str], help_text=i18n_.commands.init.options.python()),
23-
Option("--pip-args", Args["params/", MultiVar(str)], help_text=i18n_.commands.init.options.pip_args()),
23+
Option("--install-args", Args["params/", MultiVar(str)], help_text=i18n_.commands.init.options.install_args(), dest="install"),
2424
meta=CommandMeta(i18n_.commands.init.description()),
2525
)
2626

@@ -33,8 +33,8 @@ def meta(self) -> PluginMetadata:
3333

3434
def dispatch(self, result: Arparma, next_: Next):
3535
if result.find("init"):
36-
python = result.query[str]("new.python.path", "")
37-
args = result.query[tuple[str, ...]]("new.pip_args.params", ())
36+
python = result.query[str]("init.python.path", "")
37+
args = result.query[tuple[str, ...]]("init.install.params", ())
3838

3939
python_path = sys.executable
4040
if get_venv_like_prefix(sys.executable)[0] is None:

src/entari_cli/commands/new.py

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from entari_cli import i18n_
1010
from entari_cli.config import EntariConfig
11+
from entari_cli.consts import YES, NO
1112
from entari_cli.process import call_pip
1213
from entari_cli.project import (
1314
PYTHON_VERSION,
@@ -40,7 +41,7 @@ def init(self):
4041
Option("-O|--optional", help_text=i18n_.commands.new.options.optional()),
4142
Option("-p|--priority", Args["num/", int], help_text=i18n_.commands.new.options.priority()),
4243
Option("-py|--python", Args["path/", str], help_text=i18n_.commands.new.options.python()),
43-
Option("--pip-args", Args["params/", MultiVar(str)], help_text=i18n_.commands.new.options.pip_args()),
44+
Option("--install-args", Args["params/", MultiVar(str)], help_text=i18n_.commands.new.options.install_args(), dest="install"),
4445
meta=CommandMeta(i18n_.commands.new.description()),
4546
)
4647

@@ -58,27 +59,13 @@ def dispatch(self, result: Arparma, next_: Next):
5859
entari_version = "0.15.0"
5960
if not is_application:
6061
ans = ask(i18n_.commands.new.prompts.is_plugin_project(), "Y/n").strip().lower()
61-
is_application = ans in {"no", "false", "f", "0", "n", "n/a", "none", "nope", "nah"}
62+
is_application = ans in NO
6263
if not is_application:
63-
args = result.query[tuple[str, ...]]("new.pip_args.params", ())
64+
args = result.query[tuple[str, ...]]("new.install.params", ())
6465
python_path = sys.executable
6566
if get_venv_like_prefix(sys.executable)[0] is None:
6667
ans = ask(i18n_.venv.ask_create(), "Y/n").strip().lower()
67-
use_venv = ans in {
68-
"yes",
69-
"true",
70-
"t",
71-
"1",
72-
"y",
73-
"yea",
74-
"yeah",
75-
"yep",
76-
"sure",
77-
"ok",
78-
"okay",
79-
"",
80-
"y/n",
81-
}
68+
use_venv = ans in YES
8269
if use_venv:
8370
python_path = str(ensure_python(Path.cwd(), python).executable)
8471
if not check_package_installed("arclet.entari", python_path):
@@ -115,11 +102,11 @@ def dispatch(self, result: Arparma, next_: Next):
115102
is_file = result.find("new.file")
116103
if not is_file:
117104
ans = ask(i18n_.commands.new.prompts.is_single_file(), "Y/n").strip().lower()
118-
is_file = ans in {"yes", "true", "t", "1", "y", "yea", "yeah", "yep", "sure", "ok", "okay", "", "y/n"}
105+
is_file = ans in YES
119106
is_static = result.find("new.static")
120107
if not is_static:
121108
ans = ask(i18n_.commands.new.prompts.is_disposable(), "Y/n").strip().lower()
122-
is_static = ans in {"no", "false", "f", "0", "n", "n/a", "none", "nope", "nah"}
109+
is_static = ans in NO
123110
if proj_name.startswith("entari-plugin-") and check_package_installed(file_name):
124111
return f"{Fore.RED}{i18n_.commands.new.messages.installed(name=proj_name)}{Fore.RESET}"
125112
path = Path.cwd() / ("plugins" if is_application else "src")

src/entari_cli/commands/remove.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from entari_cli import i18n_
77
from entari_cli.config import EntariConfig
8+
from entari_cli.project import get_project_root
89

910

1011
@register("entari_cli.plugins")
@@ -24,7 +25,7 @@ def dispatch(self, result: Arparma, next_: Next):
2425
name = result.query[str]("remove.name")
2526
if not name:
2627
name = input(f"{Fore.BLUE}{i18n_.commands.remove.prompts.name()}{Fore.RESET}").strip()
27-
cfg = EntariConfig.load(result.query[str]("cfg_path.path", None))
28+
cfg = EntariConfig.load(result.query[str]("cfg_path.path", None), get_project_root())
2829
cfg.plugin.pop(name, None)
2930
cfg.save()
3031
return f"{Fore.GREEN}{i18n_.commands.remove.prompts.success(name=name)}{Fore.RESET}\n"

src/entari_cli/commands/setting.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import os
2+
import subprocess
3+
from typing import Any, Mapping
4+
5+
import tomlkit
6+
from arclet.alconna import Alconna, Arparma, CommandMeta, Option, Args
7+
from clilte import BasePlugin, PluginMetadata, register
8+
from clilte.core import Next
9+
from colorama.ansi import Fore, Style, code_to_chars
10+
from platformdirs import user_config_path
11+
12+
from entari_cli import i18n_
13+
from entari_cli.project import get_project_root
14+
from entari_cli.setting import DEFAULT, del_item, get_item, set_item, print_flattened
15+
16+
17+
ITALIC = code_to_chars(3)
18+
19+
20+
def get_editor() -> str:
21+
for key in "VISUAL", "EDITOR":
22+
rv = os.getenv(key)
23+
if rv:
24+
return rv
25+
if os.name == "nt":
26+
return "notepad"
27+
for editor in "sensible-editor", "vim", "nano":
28+
if os.system(f"which {editor} >/dev/null 2>&1") == 0:
29+
return editor
30+
return "vi"
31+
32+
33+
@register("entari_cli.plugins")
34+
class SelfSetting(BasePlugin):
35+
def init(self):
36+
return Alconna(
37+
"setting",
38+
Args["key/?#Config key", str]["value/?#Config value", str],
39+
Option("-l|--local", help_text="Set config in the local configuration file"),
40+
Option("-d|--delete", help_text="Unset a configuration key"),
41+
Option("-e|--edit", help_text="Open the configuration file in the editor(defined by EDITOR env var)"),
42+
meta=CommandMeta("Display the entari-cli configuration")
43+
)
44+
45+
def meta(self) -> PluginMetadata:
46+
return PluginMetadata(
47+
name="generate",
48+
description=i18n_.commands.generate.description(),
49+
version="0.1.0",
50+
priority=1,
51+
)
52+
53+
def get_setting(self, local: bool):
54+
setting_dir = get_project_root() if local else user_config_path("entari-cli", appauthor=False)
55+
setting_file = setting_dir / (".entari_cli.toml" if local else "config.toml")
56+
if not setting_file.exists():
57+
return None
58+
with setting_file.open("r", encoding="utf-8") as f:
59+
return tomlkit.load(f)
60+
61+
def get_config(self, key: str):
62+
value = None
63+
local_cfg = self.get_setting(True)
64+
global_cfg = self.get_setting(False)
65+
if local_cfg:
66+
value = get_item(local_cfg, key)
67+
if global_cfg and value is None:
68+
value = get_item(global_cfg, key)
69+
if value is None:
70+
return DEFAULT[key]
71+
return value
72+
73+
def dispatch(self, result: Arparma, next_: Next):
74+
if result.find("setting.edit"):
75+
if result.find("setting.args.key"):
76+
return f"{Fore.RED}Cannot specify an argument when `--edit` is given{Fore.RESET}"
77+
if result.find("setting.delete"):
78+
return f"{Fore.RED}`--delete` doesn't work when `--edit` is given{Fore.RESET}"
79+
setting_dir = get_project_root() if result.find("setting.local") else user_config_path("entari-cli", appauthor=False)
80+
setting_file = setting_dir / (".entari_cli.toml" if result.find("setting.local") else "config.toml")
81+
setting_dir.mkdir(parents=True, exist_ok=True)
82+
editor = get_editor()
83+
proc = subprocess.Popen(f"{editor} {setting_file!s}", shell=True)
84+
85+
if proc.wait() == 0:
86+
return f"{Fore.GREEN}Configuration file edited successfully.{Fore.RESET}"
87+
return f"{Fore.RED}Editor {editor} exited abnormally{Fore.RESET}"
88+
if result.find("setting.delete"):
89+
key = result.query[str]("setting.args.key")
90+
if not key:
91+
return f"{Fore.RED}Please specify the configuration key to unset{Fore.RESET}"
92+
setting_dir = get_project_root() if result.find("setting.local") else user_config_path("entari-cli", appauthor=False)
93+
setting_file = setting_dir / (".entari_cli.toml" if result.find("setting.local") else "config.toml")
94+
with setting_file.open("r", encoding="utf-8") as f:
95+
cfg = tomlkit.load(f)
96+
del_item(cfg, key)
97+
with setting_file.open("w", encoding="utf-8") as f:
98+
tomlkit.dump(cfg, f)
99+
return f"{Fore.GREEN}Configuration key '{key}' unset.{Fore.RESET}"
100+
if result.find("setting.args.value"):
101+
key = result.query[str]("setting.args.key")
102+
if not key:
103+
return f"{Fore.RED}Please specify the configuration key to unset{Fore.RESET}"
104+
value = result.query[str]("setting.args.value")
105+
setting_dir = get_project_root() if result.find("setting.local") else user_config_path("entari-cli", appauthor=False)
106+
setting_file = setting_dir / (".entari_cli.toml" if result.find("setting.local") else "config.toml")
107+
setting_dir.mkdir(parents=True, exist_ok=True)
108+
with setting_file.open("a+", encoding="utf-8") as f:
109+
f.seek(0)
110+
try:
111+
cfg = tomlkit.load(f)
112+
except Exception:
113+
cfg = tomlkit.document()
114+
set_item(cfg, key, value) # type: ignore
115+
f.truncate(0)
116+
tomlkit.dump(cfg, f)
117+
return f"{Fore.GREEN}Configuration key '{key}' set to '{value}'.{Fore.RESET}"
118+
if result.find("setting.args.key"):
119+
query = result.query[str]("setting.args.key", "")
120+
local_cfg = self.get_setting(True)
121+
global_cfg = self.get_setting(False)
122+
lines = []
123+
if local_cfg:
124+
for key, value in print_flattened(local_cfg):
125+
if not key.startswith(query):
126+
continue
127+
if key.endswith(".password") or key.endswith(".token") or key.endswith(".secret"):
128+
value = f"{ITALIC}<hidden>{Fore.RESET}"
129+
lines.append(f"{Fore.CYAN}{key}{Fore.RESET} = {value}")
130+
# value = get_item(local_cfg, key)
131+
if global_cfg:
132+
for key, value in print_flattened(global_cfg):
133+
if not key.startswith(query):
134+
continue
135+
if key.endswith(".password") or key.endswith(".token") or key.endswith(".secret"):
136+
value = f"{ITALIC}<hidden>{Fore.RESET}"
137+
lines.append(f"{Fore.CYAN}{key}{Fore.RESET} = {value}")
138+
if not lines:
139+
return f"{Fore.RED}Configuration key '{query}' not found.{Fore.RESET}"
140+
return "\n".join(lines)
141+
if result.find("setting"):
142+
local_cfg = self.get_setting(True)
143+
global_cfg = self.get_setting(False)
144+
print(f"{Style.BRIGHT}Site/default setting{Style.RESET_ALL}")
145+
self._show_config(DEFAULT, {
146+
**dict(print_flattened(global_cfg) if global_cfg else ()),
147+
**dict(print_flattened(local_cfg) if local_cfg else ()),
148+
})
149+
if global_cfg:
150+
print(f"\n{Style.BRIGHT}Global configuration file ({Fore.GREEN}{user_config_path('entari-cli', appauthor=False) / 'config.toml'}{Fore.RESET}){Style.RESET_ALL}")
151+
self._show_config(dict(print_flattened(global_cfg)),{})
152+
if local_cfg:
153+
print(f"\n{Style.BRIGHT}Local configuration file ({Fore.GREEN}{get_project_root() / '.entari_cli.toml'}{Fore.RESET}){Style.RESET_ALL}")
154+
self._show_config(dict(print_flattened(local_cfg)),{})
155+
# lines = []
156+
# if local_cfg:
157+
# lines.append(f"# Local Configuration File ({Fore.GREEN}{get_project_root() / '.entari_cli.toml'}{Fore.RESET})")
158+
# for key, value in print_flattened(local_cfg):
159+
# if key.endswith(".password") or key.endswith(".token") or key.endswith(".secret"):
160+
# value = f"{ITALIC}<hidden>{Fore.RESET}"
161+
# lines.append(f"{Fore.CYAN}{key}{Fore.RESET} = {value}")
162+
# lines.append("")
163+
# if global_cfg:
164+
# lines.append(f"# Global Configuration File ({Fore.GREEN}{user_config_path('entari-cli', appauthor=False) / 'config.toml'}{Fore.RESET})")
165+
# for key, value in print_flattened(local_cfg):
166+
# if key.endswith(".password") or key.endswith(".token") or key.endswith(".secret"):
167+
# value = f"{ITALIC}<hidden>{Fore.RESET}"
168+
# lines.append(f"{Fore.CYAN}{key}{Fore.RESET} = {value}")
169+
# lines.append("")
170+
# return "\n".join(lines) if lines else f"{Fore.YELLOW}No configuration found.{Fore.RESET}"
171+
return next_(None)
172+
173+
def _show_config(self, config: Mapping[str, Any], supersedes: Mapping[str, Any]):
174+
for key in sorted(config):
175+
superseded = key in supersedes
176+
if key.endswith(".password") or key.endswith(".token") or key.endswith(".secret"):
177+
value = f"{ITALIC}<hidden>"
178+
else:
179+
value = config[key]
180+
print(
181+
f"{Style.DIM if superseded else ''}{Fore.CYAN}{key}{Fore.RESET} = {value}{Style.RESET_ALL}"
182+
)

src/entari_cli/config/file.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,21 +157,22 @@ def save(self, path: Union[str, os.PathLike[str], None] = None, indent: int = 2)
157157
self.dumper(self.path, Path(path or self.path), self.dump(indent), indent)
158158

159159
@classmethod
160-
def load(cls, path: Union[str, os.PathLike[str], None] = None) -> "EntariConfig":
160+
def load(cls, path: Union[str, os.PathLike[str], None] = None, cwd: Union[Path, None] = None) -> "EntariConfig":
161161
try:
162162
import dotenv
163163

164164
dotenv.load_dotenv()
165165
except ImportError:
166166
dotenv = None # noqa
167167
pass
168+
cwd = cwd or Path.cwd()
168169
if not path:
169170
if "ENTARI_CONFIG_FILE" in os.environ:
170171
_path = Path(os.environ["ENTARI_CONFIG_FILE"])
171-
elif (Path.cwd() / ".entari.json").exists():
172-
_path = Path.cwd() / ".entari.json"
172+
elif (cwd / ".entari.json").exists():
173+
_path = cwd / ".entari.json"
173174
else:
174-
_path = Path.cwd() / "entari.yml"
175+
_path = cwd / "entari.yml"
175176
else:
176177
_path = Path(path)
177178
if "ENTARI_CONFIG_EXTENSION" in os.environ:
@@ -191,9 +192,6 @@ def load(cls, path: Union[str, os.PathLike[str], None] = None) -> "EntariConfig"
191192
return cls(_path)
192193

193194

194-
load_config = EntariConfig.load
195-
196-
197195
def register_loader(*ext: str):
198196
"""Register a loader for a specific file extension."""
199197

src/entari_cli/consts.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
66
DEFAULT_PYTHON = ("python3", "python")
77
WINDOWS_DEFAULT_PYTHON = ("python",)
8+
9+
YES = {"yes", "true", "t", "1", "y", "yea", "yeah", "yep", "sure", "ok", "okay", "", "y/n"}
10+
NO = {"no", "false", "f", "0", "n", "n/a", "none", "nope", "nah"}

0 commit comments

Comments
 (0)