chatenv 0.2.1__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {chatenv-0.2.1/src/chatenv.egg-info → chatenv-0.2.3}/PKG-INFO +3 -3
- {chatenv-0.2.1 → chatenv-0.2.3}/pyproject.toml +2 -2
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/__init__.py +1 -1
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/cli.py +27 -3
- {chatenv-0.2.1 → chatenv-0.2.3/src/chatenv.egg-info}/PKG-INFO +3 -3
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv.egg-info/requires.txt +2 -2
- {chatenv-0.2.1 → chatenv-0.2.3}/tests/test_chatenv_core.py +79 -1
- {chatenv-0.2.1 → chatenv-0.2.3}/tests/test_version.py +1 -1
- {chatenv-0.2.1 → chatenv-0.2.3}/LICENSE +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/README.md +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/setup.cfg +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/configs.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/discovery.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/fields.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/paste.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/paths.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/presets/__init__.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/registry.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/source_chain.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/store.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv/utils.py +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv.egg-info/SOURCES.txt +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv.egg-info/dependency_links.txt +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv.egg-info/entry_points.txt +0 -0
- {chatenv-0.2.1 → chatenv-0.2.3}/src/chatenv.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chatenv
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: ChatArch typed environment profile manager
|
|
5
5
|
Author-email: rexwzh <1073853456@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -11,8 +11,8 @@ Requires-Python: >=3.10
|
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Requires-Dist: chatstyle<0.2.0,>=0.1.0
|
|
14
|
-
Requires-Dist: click
|
|
15
|
-
Requires-Dist: python-dotenv
|
|
14
|
+
Requires-Dist: click<9.0,>=8.4.2
|
|
15
|
+
Requires-Dist: python-dotenv<2.0,>=1.2.2
|
|
16
16
|
Provides-Extra: dev
|
|
17
17
|
Requires-Dist: build>=1.0; extra == "dev"
|
|
18
18
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
@@ -13,8 +13,8 @@ authors = [{name = "rexwzh", email = "1073853456@qq.com"}]
|
|
|
13
13
|
keywords = ["chatenv", "chatarch", "env"]
|
|
14
14
|
dependencies = [
|
|
15
15
|
"chatstyle>=0.1.0,<0.2.0",
|
|
16
|
-
"click>=8.0",
|
|
17
|
-
"python-dotenv>=1.0",
|
|
16
|
+
"click>=8.4.2,<9.0",
|
|
17
|
+
"python-dotenv>=1.2.2,<2.0",
|
|
18
18
|
]
|
|
19
19
|
classifiers = [
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
@@ -277,7 +277,9 @@ def _resolve_config_types_or_prompt(
|
|
|
277
277
|
return [_select_config_interactive(action=action, configs=selectable)]
|
|
278
278
|
|
|
279
279
|
if not config_types and allow_multi:
|
|
280
|
-
|
|
280
|
+
raise click.ClickException(
|
|
281
|
+
f"{action} requires --type/-t outside interactive mode.\n{_available_config_types_message()}"
|
|
282
|
+
)
|
|
281
283
|
if not config_types:
|
|
282
284
|
raise click.ClickException(
|
|
283
285
|
f"{action} requires --type/-t outside interactive mode.\n{_available_config_types_message()}"
|
|
@@ -377,11 +379,28 @@ def init_env(ctx: click.Context, config_types: tuple[str, ...], interactive: boo
|
|
|
377
379
|
click.echo(f"Configuration saved to {_envs_dir(ctx)}")
|
|
378
380
|
|
|
379
381
|
|
|
382
|
+
def _has_configured_values(config_cls: type[BaseEnvConfig], values: dict[str, str]) -> bool:
|
|
383
|
+
"""Return true when a profile has values beyond blanks and schema defaults."""
|
|
384
|
+
fields_by_key = {field.env_key: field for field in config_cls.get_fields().values()}
|
|
385
|
+
for key, value in values.items():
|
|
386
|
+
field = fields_by_key.get(key)
|
|
387
|
+
if field is None:
|
|
388
|
+
if value:
|
|
389
|
+
return True
|
|
390
|
+
continue
|
|
391
|
+
if value == "":
|
|
392
|
+
continue
|
|
393
|
+
if field.default is not None and str(field.default) == str(value):
|
|
394
|
+
continue
|
|
395
|
+
return True
|
|
396
|
+
return False
|
|
397
|
+
|
|
398
|
+
|
|
380
399
|
@cli.command(name="list")
|
|
381
400
|
@click.option("--type", "config_types", "-t", multiple=True, help="Filter config types.")
|
|
382
401
|
@click.pass_context
|
|
383
402
|
def list_env(ctx: click.Context, config_types: tuple[str, ...]):
|
|
384
|
-
"""List
|
|
403
|
+
"""List active default and named profiles grouped by config type."""
|
|
385
404
|
store = _store(ctx)
|
|
386
405
|
configs = _matched_or_all(config_types)
|
|
387
406
|
_ensure_registered()
|
|
@@ -390,10 +409,15 @@ def list_env(ctx: click.Context, config_types: tuple[str, ...]):
|
|
|
390
409
|
found = False
|
|
391
410
|
for config_cls in configs:
|
|
392
411
|
profiles = store.list_profiles(config_cls)
|
|
393
|
-
|
|
412
|
+
active_path = store.active_path(config_cls)
|
|
413
|
+
has_default = active_path.exists()
|
|
414
|
+
show_default = has_default and _has_configured_values(config_cls, store.load_active(config_cls))
|
|
415
|
+
if not show_default and not profiles:
|
|
394
416
|
continue
|
|
395
417
|
found = True
|
|
396
418
|
click.echo(f"[{config_cls.get_storage_name()}]")
|
|
419
|
+
if show_default:
|
|
420
|
+
click.echo("- .env [default]")
|
|
397
421
|
for profile in profiles:
|
|
398
422
|
click.echo(f"- {profile}.env")
|
|
399
423
|
if not found:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chatenv
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: ChatArch typed environment profile manager
|
|
5
5
|
Author-email: rexwzh <1073853456@qq.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -11,8 +11,8 @@ Requires-Python: >=3.10
|
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Requires-Dist: chatstyle<0.2.0,>=0.1.0
|
|
14
|
-
Requires-Dist: click
|
|
15
|
-
Requires-Dist: python-dotenv
|
|
14
|
+
Requires-Dist: click<9.0,>=8.4.2
|
|
15
|
+
Requires-Dist: python-dotenv<2.0,>=1.2.2
|
|
16
16
|
Provides-Extra: dev
|
|
17
17
|
Requires-Dist: build>=1.0; extra == "dev"
|
|
18
18
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
@@ -92,13 +92,17 @@ def test_duplicate_provider_config_does_not_pollute_registry(monkeypatch):
|
|
|
92
92
|
discovery_module._provider_configs.update(provider_configs_before)
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
def
|
|
95
|
+
def test_runtime_dependencies_use_reviewed_latest_windows():
|
|
96
96
|
pyproject_text = (Path(__file__).resolve().parents[1] / "pyproject.toml").read_text(
|
|
97
97
|
encoding="utf-8"
|
|
98
98
|
)
|
|
99
99
|
|
|
100
100
|
assert '"chatstyle>=0.1.0,<0.2.0"' in pyproject_text
|
|
101
|
+
assert '"click>=8.4.2,<9.0"' in pyproject_text
|
|
102
|
+
assert '"python-dotenv>=1.2.2,<2.0"' in pyproject_text
|
|
101
103
|
assert '"chatstyle>=0.1.0"' not in pyproject_text
|
|
104
|
+
assert '"click>=8.0"' not in pyproject_text
|
|
105
|
+
assert '"python-dotenv>=1.0"' not in pyproject_text
|
|
102
106
|
|
|
103
107
|
|
|
104
108
|
def test_status_lists_registered_platforms_and_provider_detail(tmp_path):
|
|
@@ -175,6 +179,62 @@ def test_cli_paste_active_merges_file_values_without_system_env(monkeypatch, tmp
|
|
|
175
179
|
assert "OPENAI_API_KEY" not in env_text
|
|
176
180
|
|
|
177
181
|
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_list_marks_active_default_profile(tmp_path):
|
|
185
|
+
runner = CliRunner()
|
|
186
|
+
home = tmp_path / "arch"
|
|
187
|
+
store = EnvStore(home / "envs")
|
|
188
|
+
store.save_active(UnitConfig, {"UNIT_VALUE": "active"})
|
|
189
|
+
store.save_profile(UnitConfig, "work", {"UNIT_VALUE": "named"})
|
|
190
|
+
|
|
191
|
+
result = runner.invoke(cli, ["--home", str(home), "list", "--type", "unit"])
|
|
192
|
+
|
|
193
|
+
assert result.exit_code == 0, result.output
|
|
194
|
+
assert "[Unit]" in result.output
|
|
195
|
+
assert "- .env [default]" in result.output
|
|
196
|
+
assert "- work.env" in result.output
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_list_keeps_named_only_profiles_without_default_marker(tmp_path):
|
|
201
|
+
runner = CliRunner()
|
|
202
|
+
home = tmp_path / "arch"
|
|
203
|
+
store = EnvStore(home / "envs")
|
|
204
|
+
store.save_profile(UnitConfig, "work", {"UNIT_VALUE": "named"})
|
|
205
|
+
|
|
206
|
+
result = runner.invoke(cli, ["--home", str(home), "list", "--type", "unit"])
|
|
207
|
+
|
|
208
|
+
assert result.exit_code == 0, result.output
|
|
209
|
+
assert "[Unit]" in result.output
|
|
210
|
+
assert "- .env [default]" not in result.output
|
|
211
|
+
assert "- work.env" in result.output
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def test_list_hides_default_only_active_profile_by_default(tmp_path):
|
|
215
|
+
runner = CliRunner()
|
|
216
|
+
home = tmp_path / "arch"
|
|
217
|
+
store = EnvStore(home / "envs")
|
|
218
|
+
store.save_active(UnitConfig, {"UNIT_KEY": "", "UNIT_VALUE": "default"})
|
|
219
|
+
|
|
220
|
+
result = runner.invoke(cli, ["--home", str(home), "list", "--type", "unit"])
|
|
221
|
+
|
|
222
|
+
assert result.exit_code == 0, result.output
|
|
223
|
+
assert "No profiles found under" in result.output
|
|
224
|
+
assert "[Unit]" not in result.output
|
|
225
|
+
assert "- .env [default]" not in result.output
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_list_reports_no_profiles_when_active_and_named_profiles_absent(tmp_path):
|
|
229
|
+
runner = CliRunner()
|
|
230
|
+
home = tmp_path / "arch"
|
|
231
|
+
(home / "envs" / "Unit").mkdir(parents=True)
|
|
232
|
+
|
|
233
|
+
result = runner.invoke(cli, ["--home", str(home), "list", "--type", "unit"])
|
|
234
|
+
|
|
235
|
+
assert result.exit_code == 0, result.output
|
|
236
|
+
assert "No profiles found under" in result.output
|
|
237
|
+
|
|
178
238
|
def test_paths_only_use_chatarch_home(monkeypatch, tmp_path):
|
|
179
239
|
monkeypatch.setenv("CHATARCH_HOME", str(tmp_path / "arch"))
|
|
180
240
|
paths = get_paths()
|
|
@@ -485,6 +545,24 @@ def test_cli_init_with_type_still_prompts_fields_when_auto_prompt_disabled(
|
|
|
485
545
|
assert "UNIT_VALUE='from-init'" in env_text
|
|
486
546
|
|
|
487
547
|
|
|
548
|
+
def test_cli_init_without_type_fails_outside_interactive_without_writing_all(
|
|
549
|
+
tmp_path, monkeypatch
|
|
550
|
+
):
|
|
551
|
+
runner = CliRunner()
|
|
552
|
+
home = tmp_path / "arch"
|
|
553
|
+
monkeypatch.setenv("CHATARCH_AUTO_PROMPT", "false")
|
|
554
|
+
monkeypatch.setattr(
|
|
555
|
+
"chatenv.cli._chatstyle_resolve_interactive_mode",
|
|
556
|
+
_tty_resolution,
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
result = runner.invoke(cli, ["--home", str(home), "init", "-I"])
|
|
560
|
+
|
|
561
|
+
assert result.exit_code != 0
|
|
562
|
+
assert "init requires --type/-t outside interactive mode" in result.output
|
|
563
|
+
assert not (home / "envs").exists()
|
|
564
|
+
|
|
565
|
+
|
|
488
566
|
def test_cli_new_prompts_for_config_type_when_type_missing(
|
|
489
567
|
tmp_path, monkeypatch
|
|
490
568
|
):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|