invoke-toolkit 0.0.56__tar.gz → 0.0.58__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.
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/PKG-INFO +1 -1
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/completion.py +4 -4
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/config/config.py +24 -26
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/context/context.py +86 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/tasks/config.py +2 -4
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/tasks/create.py +6 -5
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/templates/package-template/pyproject.toml.jinja +3 -5
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/extensions/test_create.py +139 -1
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/collection.py +10 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/echo.yaml +2 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/no-dedupe.yaml +2 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/no-echo.yaml +2 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/package/invoke.yml +3 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/package/tasks/__init__.py +6 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/runtime.py +6 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/underscores/tasks.py +6 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/yaml/explicit.py +9 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/yaml/tasks.py +6 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/yml/explicit.py +9 -0
- invoke_toolkit-0.0.58/tests/original_invoke/_support/configs/yml/tasks.py +6 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_completion_with_choices.py +44 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_config_helper.py +76 -42
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/utils/test_fzf.py +12 -7
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/.gitignore +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/LICENSE.txt +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/README.md +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/pyproject.toml +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/__main__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/collections.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/config/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/config/registry.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/config/schema.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/config/status_helper.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/context/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/context/types.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/executor.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/tasks/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/tasks/dist.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/tasks/shell.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/loader/entrypoint.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/log/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/log/logger.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/output/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/output/console.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/output/utils.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/parser.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/program/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/program/main.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/program/program.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/runners/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/runners/rich.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/scripts/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/scripts/loader.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/tasks/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/tasks/autocomplete.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/tasks/cache.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/tasks/tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/tasks/types.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/testing.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/utils/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/utils/fzf.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/utils/inspection.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/utils/singleton.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/utils/text.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/templates/package-template/.gitignore.jinja +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/templates/package-template/README.md.jinja +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/templates/package-template/copier.yml +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/templates/package-template/src/{{package_slug}}/__init__.py.jinja +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/templates/package-template/src/{{package_slug}}/tasks.py.jinja +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/conftest.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/examples/cached_completion/tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/examples/config_schema/tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/examples/enum_select_size/tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/examples/fzf_selector/tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/examples/literal_set_level/tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/extensions/conftest.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/extensions/test_config_tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/extensions/test_package_template_entrypoint.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/extensions/test_shell_tasks.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/all-four/invoke.json +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/all-four/invoke.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/all-four/invoke.yml +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/json/invoke.json +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/json-and-python/invoke.json +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/json-and-python/invoke.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/python/invoke.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/three-of-em/invoke.json +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/three-of-em/invoke.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/three-of-em/invoke.yml +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/configs/yml/invoke.yml +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/_support/has_modules.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/conftest.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/original_invoke/test_config.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/program/main.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/program/tasks/__init__.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/program/tasks/coll1.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/script/test_script.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/tasks/test_cache.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/tasks/test_extensions_config.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_annotated_help.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_cofig_class.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_collection.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_collection_configure.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_config_registry.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_config_schema.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_console.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_context_class.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_disable_status_cli.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_enum_arguments.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_executor.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_file_completion.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_global_context.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_help_flags.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_invoke_compatibility.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_loader.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_parsing.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_proctitle.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_program_with_collection.py +0 -0
- {invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/tests/test_toplevel.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invoke-toolkit
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.58
|
|
4
4
|
Summary: A set of extended APIs for PyInvoke for composable scripts, plugins and richer output
|
|
5
5
|
Project-URL: Documentation, https://github.com/D3f0/invoke-toolkit#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/D3f0/invoke-toolkit/issues
|
|
@@ -20,7 +20,7 @@ from invoke.completion.complete import (
|
|
|
20
20
|
from invoke.exceptions import Exit, ParseError
|
|
21
21
|
from invoke.parser import Parser, ParserContext
|
|
22
22
|
|
|
23
|
-
from invoke_toolkit.config import
|
|
23
|
+
from invoke_toolkit.config import ToolkitConfig
|
|
24
24
|
from invoke_toolkit.context import ToolkitContext
|
|
25
25
|
from invoke_toolkit.tasks.tasks import (
|
|
26
26
|
_extract_enum_params,
|
|
@@ -176,11 +176,11 @@ def get_choices_for_argument(
|
|
|
176
176
|
if arg_name in callbacks:
|
|
177
177
|
try:
|
|
178
178
|
# Try to call the callback with context and incomplete
|
|
179
|
-
ctx = ToolkitContext()
|
|
179
|
+
ctx = ToolkitContext(config=ToolkitConfig())
|
|
180
180
|
|
|
181
181
|
# Get timeout from config (default: 10 seconds)
|
|
182
|
-
timeout = get_config_value(
|
|
183
|
-
|
|
182
|
+
timeout = ctx.get_config_value(
|
|
183
|
+
"completion.callback_timeout", default=10.0
|
|
184
184
|
)
|
|
185
185
|
|
|
186
186
|
# Execute callback with timeout
|
|
@@ -391,32 +391,30 @@ def get_config_value( # pylint: disable=inconsistent-return-statements
|
|
|
391
391
|
SystemExit: Raised via ctx.rich_exit() if required value is missing.
|
|
392
392
|
|
|
393
393
|
Examples:
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
>>> exit_code=1 # Auto-detects 'secret' parameter name
|
|
419
|
-
>>> )
|
|
394
|
+
Preferred: Use the context method (no import needed)::
|
|
395
|
+
|
|
396
|
+
@task()
|
|
397
|
+
def my_task(ctx: Context) -> None:
|
|
398
|
+
# Simple usage with default
|
|
399
|
+
db_host = ctx.get_config_value("database.host", default="localhost")
|
|
400
|
+
|
|
401
|
+
# Required value - exits if missing
|
|
402
|
+
api_key = ctx.get_config_value("api.key", required=True)
|
|
403
|
+
|
|
404
|
+
# Required with custom exit code and message
|
|
405
|
+
secret = ctx.get_config_value(
|
|
406
|
+
"secrets.token",
|
|
407
|
+
exit_code=2,
|
|
408
|
+
exit_message="Token must be configured"
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
Alternative: Use the standalone function (backward compatible)::
|
|
412
|
+
|
|
413
|
+
from invoke_toolkit.config import get_config_value
|
|
414
|
+
|
|
415
|
+
@task()
|
|
416
|
+
def my_task(ctx: Context) -> None:
|
|
417
|
+
db_host = get_config_value(ctx, "database.host", default="localhost")
|
|
420
418
|
"""
|
|
421
419
|
# Mark as required if exit parameters are provided or if required=True
|
|
422
420
|
has_exit_params = (exit_message is not None) or (exit_code is not None) or required
|
|
@@ -7,6 +7,7 @@ from contextlib import _GeneratorContextManager, contextmanager
|
|
|
7
7
|
from os import PathLike
|
|
8
8
|
from typing import (
|
|
9
9
|
TYPE_CHECKING,
|
|
10
|
+
Any,
|
|
10
11
|
Callable,
|
|
11
12
|
Generator,
|
|
12
13
|
Iterator,
|
|
@@ -16,6 +17,7 @@ from typing import (
|
|
|
16
17
|
Protocol,
|
|
17
18
|
TypeVar,
|
|
18
19
|
Union,
|
|
20
|
+
overload,
|
|
19
21
|
)
|
|
20
22
|
|
|
21
23
|
import setproctitle
|
|
@@ -329,3 +331,87 @@ class ToolkitContext(Context, ConfigProtocol):
|
|
|
329
331
|
["tmux", "rename-window", previous_tmux_title],
|
|
330
332
|
check=False,
|
|
331
333
|
)
|
|
334
|
+
|
|
335
|
+
# Type overloads for get_config_value - returns T when no exit params
|
|
336
|
+
@overload
|
|
337
|
+
def get_config_value(
|
|
338
|
+
self,
|
|
339
|
+
path: str,
|
|
340
|
+
default: T = ..., # type: ignore[assignment]
|
|
341
|
+
exit_message: None = None,
|
|
342
|
+
exit_code: None = None,
|
|
343
|
+
required: bool = False,
|
|
344
|
+
) -> Any | T: ...
|
|
345
|
+
|
|
346
|
+
# Type hints NoReturn when exit_message provided
|
|
347
|
+
@overload
|
|
348
|
+
def get_config_value(
|
|
349
|
+
self,
|
|
350
|
+
path: str,
|
|
351
|
+
default: Any = ...,
|
|
352
|
+
exit_message: str = ...,
|
|
353
|
+
exit_code: Optional[int] = None,
|
|
354
|
+
required: bool = False,
|
|
355
|
+
) -> Any | NoReturn: ...
|
|
356
|
+
|
|
357
|
+
# Type hints NoReturn when exit_code provided
|
|
358
|
+
@overload
|
|
359
|
+
def get_config_value(
|
|
360
|
+
self,
|
|
361
|
+
path: str,
|
|
362
|
+
default: Any = ...,
|
|
363
|
+
exit_message: None = None,
|
|
364
|
+
exit_code: int = ...,
|
|
365
|
+
required: bool = False,
|
|
366
|
+
) -> Any | NoReturn: ...
|
|
367
|
+
|
|
368
|
+
# Type hints NoReturn when required=True
|
|
369
|
+
@overload
|
|
370
|
+
def get_config_value(
|
|
371
|
+
self,
|
|
372
|
+
path: str,
|
|
373
|
+
default: Any = ...,
|
|
374
|
+
exit_message: Optional[str] = None,
|
|
375
|
+
exit_code: Optional[int] = None,
|
|
376
|
+
required: bool = True,
|
|
377
|
+
) -> Any | NoReturn: ...
|
|
378
|
+
|
|
379
|
+
def get_config_value(
|
|
380
|
+
self,
|
|
381
|
+
path: str,
|
|
382
|
+
default: Any = ...,
|
|
383
|
+
exit_message: Optional[str] = None,
|
|
384
|
+
exit_code: Optional[int] = None,
|
|
385
|
+
required: bool = False,
|
|
386
|
+
) -> Any:
|
|
387
|
+
"""Get a configuration value from config with dot notation support.
|
|
388
|
+
|
|
389
|
+
This is a convenience method that wraps invoke_toolkit.config.get_config_value.
|
|
390
|
+
See that function for full documentation.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
path: Dot-separated path to the config value (e.g., 'database.host')
|
|
394
|
+
default: Default value if path not found
|
|
395
|
+
exit_message: Custom message when value required but missing
|
|
396
|
+
exit_code: Exit code for ctx.rich_exit() when missing
|
|
397
|
+
required: Whether value is required (exits if missing)
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
The config value if found, otherwise default value.
|
|
401
|
+
|
|
402
|
+
Example:
|
|
403
|
+
@task()
|
|
404
|
+
def my_task(ctx: Context) -> None:
|
|
405
|
+
db_host = ctx.get_config_value("database.host", default="localhost")
|
|
406
|
+
api_key = ctx.get_config_value("api.key", required=True)
|
|
407
|
+
"""
|
|
408
|
+
# Import here to avoid circular imports
|
|
409
|
+
from invoke_toolkit.config.config import ( # pylint: disable=import-outside-toplevel
|
|
410
|
+
_UNDEFINED_DEFAULT,
|
|
411
|
+
get_config_value as _get_config_value,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Handle the default sentinel - ellipsis means "use undefined default"
|
|
415
|
+
if default is ...:
|
|
416
|
+
default = _UNDEFINED_DEFAULT
|
|
417
|
+
return _get_config_value(self, path, default, exit_message, exit_code, required)
|
{invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/tasks/config.py
RENAMED
|
@@ -404,10 +404,8 @@ def _complete_config_path(ctx: Context, incomplete: str) -> list[str]:
|
|
|
404
404
|
|
|
405
405
|
def _complete_my_items(ctx: Context, incomplete: str) -> list[str]:
|
|
406
406
|
'''Completion callback for my custom items.'''
|
|
407
|
-
from
|
|
408
|
-
|
|
409
|
-
# Get data from context or config
|
|
410
|
-
items = get_config_value(ctx, "my.items", default=[])
|
|
407
|
+
# Get data from config using context method (no import needed)
|
|
408
|
+
items = ctx.get_config_value("my.items", default=[])
|
|
411
409
|
|
|
412
410
|
# Filter by incomplete prefix
|
|
413
411
|
matching = [i for i in items if i.startswith(incomplete)]
|
{invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/src/invoke_toolkit/extensions/tasks/create.py
RENAMED
|
@@ -65,7 +65,7 @@ def _get_template() -> str:
|
|
|
65
65
|
script_template = dedent(rf"""
|
|
66
66
|
#!/usr/bin/env -S uv run --script
|
|
67
67
|
# /// script
|
|
68
|
-
# requires-python = ">=3.
|
|
68
|
+
# requires-python = ">=3.11"
|
|
69
69
|
# dependencies = [
|
|
70
70
|
# "invoke-toolkit{version_for_template}",
|
|
71
71
|
# ]
|
|
@@ -322,9 +322,9 @@ def package(
|
|
|
322
322
|
template_data = {
|
|
323
323
|
"package_name": actual_name,
|
|
324
324
|
"package_slug": package_slug,
|
|
325
|
-
"collection_name":
|
|
325
|
+
"collection_name": extension_short_name,
|
|
326
326
|
"extension_short_name": extension_short_name,
|
|
327
|
-
"python_version": "3.
|
|
327
|
+
"python_version": "3.11",
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
run_copy(
|
|
@@ -342,9 +342,10 @@ def package(
|
|
|
342
342
|
dedent(
|
|
343
343
|
f"""
|
|
344
344
|
[yellow]Next steps:[/yellow]
|
|
345
|
-
cd {
|
|
345
|
+
cd {target_path}
|
|
346
346
|
uv sync
|
|
347
|
-
|
|
347
|
+
# Test your package
|
|
348
|
+
uv run --directory {target_path} -m invoke-toolkit -l
|
|
348
349
|
"""
|
|
349
350
|
).strip()
|
|
350
351
|
)
|
{invoke_toolkit-0.0.56 → invoke_toolkit-0.0.58}/templates/package-template/pyproject.toml.jinja
RENAMED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "{{ package_name }}"
|
|
7
|
-
|
|
7
|
+
version = "0.1.0"
|
|
8
8
|
description = "{{ project_description }}"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">={{ python_version }}"
|
|
@@ -17,9 +17,10 @@ classifiers = [
|
|
|
17
17
|
"Intended Audience :: Developers",
|
|
18
18
|
"License :: OSI Approved :: MIT License",
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
|
-
"Programming Language :: Python :: 3.10",
|
|
21
20
|
"Programming Language :: Python :: 3.11",
|
|
22
21
|
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3.14",
|
|
23
24
|
]
|
|
24
25
|
dependencies = [
|
|
25
26
|
"invoke-toolkit", # Requires invoke-toolkit, any version
|
|
@@ -28,8 +29,5 @@ dependencies = [
|
|
|
28
29
|
[project.entry-points."invoke_toolkit.collection"]
|
|
29
30
|
"{{ extension_short_name }}" = "{{ package_slug }}:collection"
|
|
30
31
|
|
|
31
|
-
[tool.hatch.version]
|
|
32
|
-
path = "src/{{ package_slug }}/__init__.py"
|
|
33
|
-
|
|
34
32
|
[tool.hatch.build.targets.wheel]
|
|
35
33
|
packages = ["src/{{ package_slug }}"]
|
|
@@ -274,7 +274,7 @@ def _verify_python_requirement(lines):
|
|
|
274
274
|
"""Verify requires-python field exists and has correct value."""
|
|
275
275
|
for line in lines:
|
|
276
276
|
if "requires-python" in line:
|
|
277
|
-
assert ">=3.
|
|
277
|
+
assert ">=3.11" in line, f"Expected requires-python >=3.11, got: '{line}'"
|
|
278
278
|
return
|
|
279
279
|
|
|
280
280
|
raise AssertionError("requires-python field not found in metadata")
|
|
@@ -658,6 +658,144 @@ def test_package_uses_git_config_template(
|
|
|
658
658
|
assert (pkg_dir / "pyproject.toml").exists(), "pyproject.toml should exist"
|
|
659
659
|
|
|
660
660
|
|
|
661
|
+
def _assert_cli_examples_use_name(
|
|
662
|
+
short_name: str, pkg_dir: Path, package_slug: str
|
|
663
|
+
) -> None:
|
|
664
|
+
"""
|
|
665
|
+
Helper: asserts that the rendered README.md and __init__.py use ``short_name``
|
|
666
|
+
(the intk-visible collection name) in all CLI usage examples, and that no
|
|
667
|
+
raw Jinja placeholders leaked through.
|
|
668
|
+
"""
|
|
669
|
+
# --- README.md checks ---
|
|
670
|
+
readme_path = pkg_dir / "README.md"
|
|
671
|
+
assert readme_path.exists(), "README.md was not generated"
|
|
672
|
+
readme = readme_path.read_text(encoding="utf-8")
|
|
673
|
+
|
|
674
|
+
assert f"collection named `{short_name}`" in readme, (
|
|
675
|
+
f"README should reference '{short_name}' in the collection listing sentence, "
|
|
676
|
+
f"but got:\n{readme}"
|
|
677
|
+
)
|
|
678
|
+
assert f"`{short_name}.hello`" in readme, (
|
|
679
|
+
f"README should reference '{short_name}' in the task list entry, "
|
|
680
|
+
f"but got:\n{readme}"
|
|
681
|
+
)
|
|
682
|
+
assert f"intk {short_name}.hello" in readme, (
|
|
683
|
+
f"README should reference '{short_name}' in the example command, "
|
|
684
|
+
f"but got:\n{readme}"
|
|
685
|
+
)
|
|
686
|
+
assert "{{ collection_name }}" not in readme, (
|
|
687
|
+
"Jinja variable {{ collection_name }} was not rendered in README.md"
|
|
688
|
+
)
|
|
689
|
+
assert "{{ package_name }}" not in readme, (
|
|
690
|
+
"Jinja variable {{ package_name }} was not rendered in README.md"
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
# --- __init__.py comment checks ---
|
|
694
|
+
init_path = pkg_dir / "src" / package_slug / "__init__.py"
|
|
695
|
+
assert init_path.exists(), "__init__.py was not generated"
|
|
696
|
+
init = init_path.read_text(encoding="utf-8")
|
|
697
|
+
|
|
698
|
+
assert f"intk {short_name}.hello" in init, (
|
|
699
|
+
f"__init__.py example comment should reference '{short_name}' for 'hello', "
|
|
700
|
+
f"but got:\n{init}"
|
|
701
|
+
)
|
|
702
|
+
assert f"intk {short_name}.my_task" in init, (
|
|
703
|
+
f"__init__.py example comment should reference '{short_name}' for 'my_task', "
|
|
704
|
+
f"but got:\n{init}"
|
|
705
|
+
)
|
|
706
|
+
assert f"intk {short_name}.tasks.hello" in init, (
|
|
707
|
+
f"__init__.py 'NOT' comment should reference '{short_name}' for nested 'tasks.hello', "
|
|
708
|
+
f"but got:\n{init}"
|
|
709
|
+
)
|
|
710
|
+
assert f"intk {short_name}.utils.my_task" in init, (
|
|
711
|
+
f"__init__.py 'NOT' comment should reference '{short_name}' for nested 'utils.my_task', "
|
|
712
|
+
f"but got:\n{init}"
|
|
713
|
+
)
|
|
714
|
+
assert "{{ collection_name }}" not in init, (
|
|
715
|
+
"Jinja variable {{ collection_name }} was not rendered in __init__.py"
|
|
716
|
+
)
|
|
717
|
+
assert "{{ package_name }}" not in init, (
|
|
718
|
+
"Jinja variable {{ package_name }} was not rendered in __init__.py"
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def test_template_cli_examples_use_collection_name_unprefixed(
|
|
723
|
+
tmp_path: Path,
|
|
724
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
725
|
+
):
|
|
726
|
+
"""
|
|
727
|
+
Regression test (non-prefixed package): verifies that the CLI usage examples
|
|
728
|
+
in README.md and __init__.py use the package slug as the collection name.
|
|
729
|
+
|
|
730
|
+
For a package named ``my-readme-pkg`` (no ``invoke-toolkit-`` prefix) the
|
|
731
|
+
intk-visible name is the full slug ``my_readme_pkg``.
|
|
732
|
+
"""
|
|
733
|
+
import invoke_toolkit
|
|
734
|
+
|
|
735
|
+
invoke_toolkit_path = Path(invoke_toolkit.__file__).parent
|
|
736
|
+
default_template = (
|
|
737
|
+
invoke_toolkit_path.parent.parent / "templates" / "package-template"
|
|
738
|
+
)
|
|
739
|
+
if not default_template.exists():
|
|
740
|
+
pytest.skip("Default template not found in development setup")
|
|
741
|
+
|
|
742
|
+
monkeypatch.chdir(tmp_path)
|
|
743
|
+
|
|
744
|
+
package_name = "my-readme-pkg"
|
|
745
|
+
package_slug = package_name.replace("-", "_")
|
|
746
|
+
# Non-prefixed: collection_name == package_slug
|
|
747
|
+
expected_short_name = package_slug
|
|
748
|
+
|
|
749
|
+
x = TestingToolkitProgram()
|
|
750
|
+
x.run(["", "-x", "create.package", "--name", package_name])
|
|
751
|
+
|
|
752
|
+
_assert_cli_examples_use_name(
|
|
753
|
+
expected_short_name, tmp_path / package_name, package_slug
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def test_template_cli_examples_use_collection_name_prefixed(
|
|
758
|
+
tmp_path: Path,
|
|
759
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
760
|
+
):
|
|
761
|
+
"""
|
|
762
|
+
Regression test (invoke-toolkit-prefixed package): verifies that the CLI
|
|
763
|
+
usage examples use only the short name after stripping the ``invoke-toolkit-``
|
|
764
|
+
prefix, not the full package name.
|
|
765
|
+
|
|
766
|
+
For ``--name my-pkg --ext-name myext`` the full package name becomes
|
|
767
|
+
``invoke-toolkit-myext``, but intk exposes it as ``myext``.
|
|
768
|
+
"""
|
|
769
|
+
import invoke_toolkit
|
|
770
|
+
|
|
771
|
+
invoke_toolkit_path = Path(invoke_toolkit.__file__).parent
|
|
772
|
+
default_template = (
|
|
773
|
+
invoke_toolkit_path.parent.parent / "templates" / "package-template"
|
|
774
|
+
)
|
|
775
|
+
if not default_template.exists():
|
|
776
|
+
pytest.skip("Default template not found in development setup")
|
|
777
|
+
|
|
778
|
+
monkeypatch.chdir(tmp_path)
|
|
779
|
+
|
|
780
|
+
ext_name = "myext"
|
|
781
|
+
full_package_name = f"invoke-toolkit-{ext_name}"
|
|
782
|
+
package_slug = full_package_name.replace("-", "_")
|
|
783
|
+
# Prefixed: collection_name == ext_name (the part after "invoke-toolkit-")
|
|
784
|
+
expected_short_name = ext_name
|
|
785
|
+
|
|
786
|
+
x = TestingToolkitProgram()
|
|
787
|
+
x.run(["", "-x", "create.package", "--name", "ignored", "--ext-name", ext_name])
|
|
788
|
+
|
|
789
|
+
pkg_dir = tmp_path / full_package_name
|
|
790
|
+
_assert_cli_examples_use_name(expected_short_name, pkg_dir, package_slug)
|
|
791
|
+
|
|
792
|
+
# Extra guard: the full package name must NOT appear as the collection name
|
|
793
|
+
readme = (pkg_dir / "README.md").read_text(encoding="utf-8")
|
|
794
|
+
assert f"intk {full_package_name}.hello" not in readme, (
|
|
795
|
+
"README must not use the full prefixed package name as the collection name"
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
|
|
661
799
|
def test_package_template_priority_explicit_over_git_config(
|
|
662
800
|
tmp_path: Path,
|
|
663
801
|
monkeypatch: pytest.MonkeyPatch,
|
|
@@ -1126,3 +1126,47 @@ def test_completion_callback_cached_decorator_without_diskcache():
|
|
|
1126
1126
|
# Check cache info shows no backend
|
|
1127
1127
|
cache_info = no_cache_callback.cache_info()
|
|
1128
1128
|
assert cache_info["backend"] == "none"
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
def test_completion_callback_has_access_to_config():
|
|
1132
|
+
"""Test that completion callbacks have access to ToolkitConfig.
|
|
1133
|
+
|
|
1134
|
+
This test demonstrates that the fix for instantiating ToolkitConfig in completion
|
|
1135
|
+
callbacks allows them to access the config object and read config values like
|
|
1136
|
+
completion.callback_timeout with the correct defaults.
|
|
1137
|
+
|
|
1138
|
+
Before the fix, ctx.get_config_value() would only return default values because
|
|
1139
|
+
ToolkitContext was instantiated without any config, causing it to use the
|
|
1140
|
+
base invoke.config.Config instead of ToolkitConfig.
|
|
1141
|
+
"""
|
|
1142
|
+
|
|
1143
|
+
def config_aware_callback(ctx: Context, incomplete: str) -> list[str]:
|
|
1144
|
+
"""A callback that reads config values."""
|
|
1145
|
+
# Uses ctx.get_config_value() method - no import needed
|
|
1146
|
+
# This works because ctx has ToolkitConfig (not base Config)
|
|
1147
|
+
timeout = ctx.get_config_value("completion.callback_timeout", default=999.0)
|
|
1148
|
+
|
|
1149
|
+
# With the fix, this should be 10.0 (the ToolkitConfig default)
|
|
1150
|
+
# Without the fix, it would fall back to the default value (999.0)
|
|
1151
|
+
return [f"timeout={timeout}"]
|
|
1152
|
+
|
|
1153
|
+
coll = ToolkitCollection()
|
|
1154
|
+
|
|
1155
|
+
@task
|
|
1156
|
+
def config_task(
|
|
1157
|
+
ctx: Context,
|
|
1158
|
+
option: Annotated[str, config_aware_callback],
|
|
1159
|
+
) -> None:
|
|
1160
|
+
"""Task that uses config in completion callback."""
|
|
1161
|
+
|
|
1162
|
+
coll.add_task(config_task) # type: ignore[arg-type]
|
|
1163
|
+
|
|
1164
|
+
# Get completion choices
|
|
1165
|
+
choices = get_choices_for_argument(coll, "config-task", "option", "")
|
|
1166
|
+
|
|
1167
|
+
# Verify the callback could access the ToolkitConfig default value
|
|
1168
|
+
assert len(choices) == 1, f"Expected 1 choice, got {len(choices)}: {choices}"
|
|
1169
|
+
# Should be the ToolkitConfig default (10.0), not the fallback default (999.0)
|
|
1170
|
+
assert "timeout=10.0" in choices, (
|
|
1171
|
+
f"Expected default timeout value (10.0) from ToolkitConfig, got: {choices}"
|
|
1172
|
+
)
|