invoke-toolkit 0.0.63__tar.gz → 0.0.64__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.63 → invoke_toolkit-0.0.64}/.gitignore +1 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/PKG-INFO +1 -1
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/program/program.py +90 -18
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/tasks/tasks.py +17 -1
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/script/test_script.py +5 -4
- invoke_toolkit-0.0.64/tests/test_alias_self_reference.py +108 -0
- invoke_toolkit-0.0.64/tests/test_list_filter_flags.py +208 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_proctitle.py +16 -8
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/LICENSE.txt +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/README.md +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/pyproject.toml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/__main__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/collections.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/completion.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/config/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/config/config.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/config/registry.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/config/schema.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/config/status_helper.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/context/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/context/context.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/context/types.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/executor.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/config.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/create.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/dist.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/shell.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/loader/entrypoint.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/log/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/log/logger.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/output/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/output/console.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/output/utils.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/parser.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/program/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/program/main.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/py.typed +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/runners/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/runners/rich.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/scripts/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/scripts/loader.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/tasks/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/tasks/autocomplete.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/tasks/cache.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/tasks/types.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/testing.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/utils/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/utils/fzf.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/utils/inspection.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/utils/singleton.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/utils/text.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/templates/package-template/.gitignore.jinja +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/templates/package-template/README.md.jinja +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/templates/package-template/copier.yml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/templates/package-template/pyproject.toml.jinja +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/templates/package-template/src/{{package_slug}}/__init__.py.jinja +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/templates/package-template/src/{{package_slug}}/tasks.py.jinja +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/conftest.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/cached_completion/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/completion_with_config/.gitignore +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/completion_with_config/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/config_schema/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/enum_select_size/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/fzf_selector/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/literal_set_level/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/extensions/conftest.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/extensions/test_config_tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/extensions/test_create.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/extensions/test_package_template_entrypoint.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/extensions/test_shell_tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/all-four/invoke.json +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/all-four/invoke.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/all-four/invoke.yml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/collection.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/echo.yaml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/json/invoke.json +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/json-and-python/invoke.json +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/json-and-python/invoke.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/no-dedupe.yaml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/no-echo.yaml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/package/invoke.yml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/package/tasks/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/python/invoke.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/runtime.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/three-of-em/invoke.json +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/three-of-em/invoke.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/three-of-em/invoke.yml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/underscores/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/yaml/explicit.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/yaml/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/yml/explicit.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/yml/invoke.yml +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/yml/tasks.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/has_modules.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/conftest.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/test_config.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/program/main.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/program/tasks/__init__.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/program/tasks/coll1.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/tasks/test_cache.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/tasks/test_extensions_config.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_annotated_help.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_cofig_class.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_collection.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_collection_configure.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_completion_with_choices.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_config_helper.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_config_registry.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_config_schema.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_console.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_context_class.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_disable_status_cli.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_enum_arguments.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_executor.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_file_completion.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_global_context.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_help_flags.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_invoke_3_compat.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_invoke_compatibility.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_loader.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_parsing.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_program_with_collection.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/test_toplevel.py +0 -0
- {invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/utils/test_fzf.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.64
|
|
4
4
|
Summary: Extended APIs for PyInvoke: composable task plugins, typed CLI args, Rich output, shell completion, and PEP 723 script support
|
|
5
5
|
Project-URL: Documentation, https://github.com/D3f0/invoke-toolkit#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/D3f0/invoke-toolkit/issues
|
|
@@ -15,7 +15,7 @@ from importlib import metadata
|
|
|
15
15
|
from importlib.util import module_from_spec
|
|
16
16
|
from logging import getLogger
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import Dict, List, Optional, Sequence, Tuple, Union
|
|
18
|
+
from typing import Dict, List, Literal, Optional, Sequence, Tuple, Union
|
|
19
19
|
|
|
20
20
|
from rich.table import Table
|
|
21
21
|
|
|
@@ -81,6 +81,9 @@ class ToolkitProgram(Program):
|
|
|
81
81
|
config_class,
|
|
82
82
|
binary_names,
|
|
83
83
|
)
|
|
84
|
+
# Names of sub-collections that were loaded from entry-point plugins.
|
|
85
|
+
# Populated in load_collection(); used by --list-tasks / --list-plugins.
|
|
86
|
+
self._plugin_collection_names: set[str] = set()
|
|
84
87
|
|
|
85
88
|
def get_version(self) -> str:
|
|
86
89
|
"""Compute version
|
|
@@ -368,10 +371,92 @@ class ToolkitProgram(Program):
|
|
|
368
371
|
default=False,
|
|
369
372
|
help="Initializes shell for runner via CLI flag",
|
|
370
373
|
),
|
|
374
|
+
ToolkitArgument(
|
|
375
|
+
names=("list-tasks", "lt"),
|
|
376
|
+
kind=bool,
|
|
377
|
+
default=False,
|
|
378
|
+
help="List only the main tasks discovered in the current directory (excludes plugins)",
|
|
379
|
+
),
|
|
380
|
+
ToolkitArgument(
|
|
381
|
+
names=("list-plugins", "lp"),
|
|
382
|
+
kind=bool,
|
|
383
|
+
default=False,
|
|
384
|
+
help="List only the plugin-provided task collections (excludes main tasks)",
|
|
385
|
+
),
|
|
371
386
|
]
|
|
372
387
|
args.extend(toolkit_program_arguments)
|
|
373
388
|
return args
|
|
374
389
|
|
|
390
|
+
def _handle_list_flags(self) -> None:
|
|
391
|
+
"""
|
|
392
|
+
Handle --list, --list-tasks, and --list-plugins flags.
|
|
393
|
+
|
|
394
|
+
Sets ``list_format`` / ``list_depth`` / ``scoped_collection`` as needed
|
|
395
|
+
and raises `Exit` when any listing flag is active.
|
|
396
|
+
"""
|
|
397
|
+
self.list_format = self.args["list-format"].value
|
|
398
|
+
self.list_depth = self.args["list-depth"].value
|
|
399
|
+
|
|
400
|
+
# --list [root]: standard task listing (optionally scoped to a sub-collection)
|
|
401
|
+
list_root = self.args.list.value # True or a string namespace
|
|
402
|
+
if list_root:
|
|
403
|
+
if isinstance(list_root, str):
|
|
404
|
+
self.list_root = list_root
|
|
405
|
+
try:
|
|
406
|
+
sub = self.collection.subcollection_from_path(list_root)
|
|
407
|
+
self.scoped_collection = sub
|
|
408
|
+
except KeyError:
|
|
409
|
+
msg = "Sub-collection '{}' not found!"
|
|
410
|
+
raise Exit(msg.format(list_root))
|
|
411
|
+
self.list_tasks()
|
|
412
|
+
raise Exit
|
|
413
|
+
|
|
414
|
+
# --list-tasks: show only main (non-plugin) tasks
|
|
415
|
+
if self.args["list-tasks"].value:
|
|
416
|
+
debug("Saw --list-tasks, listing main tasks only & exiting")
|
|
417
|
+
self.scoped_collection = self._make_filtered_collection("tasks")
|
|
418
|
+
self.list_tasks()
|
|
419
|
+
raise Exit
|
|
420
|
+
|
|
421
|
+
# --list-plugins: show only plugin-provided collections
|
|
422
|
+
if self.args["list-plugins"].value:
|
|
423
|
+
debug("Saw --list-plugins, listing plugin collections only & exiting")
|
|
424
|
+
if not self._plugin_collection_names:
|
|
425
|
+
raise Exit("No plugin collections found.")
|
|
426
|
+
self.scoped_collection = self._make_filtered_collection("plugins")
|
|
427
|
+
self.list_tasks()
|
|
428
|
+
raise Exit
|
|
429
|
+
|
|
430
|
+
def _make_filtered_collection(
|
|
431
|
+
self, mode: "Literal['tasks', 'plugins']"
|
|
432
|
+
) -> "ToolkitCollection":
|
|
433
|
+
"""
|
|
434
|
+
Return a shallow copy of ``self.scoped_collection`` containing only
|
|
435
|
+
the subset of tasks and sub-collections appropriate for *mode*.
|
|
436
|
+
|
|
437
|
+
- ``'tasks'`` – direct tasks + non-plugin sub-collections only
|
|
438
|
+
- ``'plugins'`` – plugin sub-collections only (no direct tasks)
|
|
439
|
+
"""
|
|
440
|
+
source = self.scoped_collection
|
|
441
|
+
filtered = ToolkitCollection(source.name or "")
|
|
442
|
+
if mode == "tasks":
|
|
443
|
+
# Include all direct tasks on the scoped collection
|
|
444
|
+
for task_name, task in source.tasks.items():
|
|
445
|
+
filtered.add_task(task, name=task_name)
|
|
446
|
+
# Include sub-collections that are NOT from plugins
|
|
447
|
+
for coll_name, coll in source.collections.items():
|
|
448
|
+
if coll_name not in self._plugin_collection_names:
|
|
449
|
+
filtered.add_collection(coll, name=coll_name)
|
|
450
|
+
else: # mode == "plugins"
|
|
451
|
+
# Include only the plugin sub-collections
|
|
452
|
+
for coll_name, coll in source.collections.items():
|
|
453
|
+
if coll_name in self._plugin_collection_names:
|
|
454
|
+
filtered.add_collection(coll, name=coll_name)
|
|
455
|
+
# Preserve the default task pointer when it still makes sense
|
|
456
|
+
if source.default and source.default in filtered.tasks:
|
|
457
|
+
filtered.default = source.default
|
|
458
|
+
return filtered
|
|
459
|
+
|
|
375
460
|
def _load_entry_points_collection(self, start: str) -> "ToolkitCollection | None":
|
|
376
461
|
"""
|
|
377
462
|
Load task collections from entry points.
|
|
@@ -453,6 +538,7 @@ class ToolkitProgram(Program):
|
|
|
453
538
|
debug("Merging entry point collections with tasks.py")
|
|
454
539
|
for name, collection in ep_collection.collections.items():
|
|
455
540
|
self.collection.add_collection(collection, name=name)
|
|
541
|
+
self._plugin_collection_names.add(name)
|
|
456
542
|
except CollectionNotFound as e:
|
|
457
543
|
start = self.args["search-root"].value or "."
|
|
458
544
|
# First try to load entry points (for packages without tasks.py)
|
|
@@ -460,6 +546,7 @@ class ToolkitProgram(Program):
|
|
|
460
546
|
if ep_collection is not None:
|
|
461
547
|
debug("Loading collections from entry points")
|
|
462
548
|
self.collection = ep_collection
|
|
549
|
+
self._plugin_collection_names.update(ep_collection.collections.keys())
|
|
463
550
|
else:
|
|
464
551
|
# If entry points weren't found, try local_tasks or fail
|
|
465
552
|
if not (Path(start) / "local_tasks.py").exists():
|
|
@@ -571,23 +658,8 @@ class ToolkitProgram(Program):
|
|
|
571
658
|
# should?
|
|
572
659
|
raise ParseError("No idea what '{}' is!".format(halp))
|
|
573
660
|
|
|
574
|
-
#
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
self.list_format = self.args["list-format"].value
|
|
578
|
-
self.list_depth = self.args["list-depth"].value
|
|
579
|
-
if list_root:
|
|
580
|
-
# Not just --list, but --list some-root - do moar work
|
|
581
|
-
if isinstance(list_root, str):
|
|
582
|
-
self.list_root = list_root
|
|
583
|
-
try:
|
|
584
|
-
sub = self.collection.subcollection_from_path(list_root)
|
|
585
|
-
self.scoped_collection = sub
|
|
586
|
-
except KeyError:
|
|
587
|
-
msg = "Sub-collection '{}' not found!"
|
|
588
|
-
raise Exit(msg.format(list_root))
|
|
589
|
-
self.list_tasks()
|
|
590
|
-
raise Exit
|
|
661
|
+
# Handle --list / --list-tasks / --list-plugins (raises Exit if triggered)
|
|
662
|
+
self._handle_list_flags()
|
|
591
663
|
|
|
592
664
|
# Print completion helpers if necessary
|
|
593
665
|
if self.args.complete.value:
|
|
@@ -6,6 +6,7 @@ Type annotated tasks and and overrides over invoke
|
|
|
6
6
|
|
|
7
7
|
import inspect
|
|
8
8
|
import os
|
|
9
|
+
import warnings
|
|
9
10
|
import subprocess
|
|
10
11
|
import types
|
|
11
12
|
from enum import Enum
|
|
@@ -774,7 +775,22 @@ def task( # pylint: disable=too-many-arguments,too-many-branches
|
|
|
774
775
|
if name is not None:
|
|
775
776
|
task_kwargs["name"] = name
|
|
776
777
|
if aliases:
|
|
777
|
-
|
|
778
|
+
# Normalize a name to invoke's canonical form (underscores → hyphens).
|
|
779
|
+
effective_name = (name or getattr(f, "__name__", "")).replace("_", "-")
|
|
780
|
+
filtered_aliases = [
|
|
781
|
+
a for a in aliases if a.replace("_", "-") != effective_name
|
|
782
|
+
]
|
|
783
|
+
skipped = set(aliases) - set(filtered_aliases)
|
|
784
|
+
if skipped:
|
|
785
|
+
warnings.warn(
|
|
786
|
+
f"Task '{effective_name}': alias(es) {sorted(skipped)!r} "
|
|
787
|
+
"match the task name and will be ignored to prevent a "
|
|
788
|
+
"RecursionError at runtime.",
|
|
789
|
+
UserWarning,
|
|
790
|
+
stacklevel=2,
|
|
791
|
+
)
|
|
792
|
+
if filtered_aliases:
|
|
793
|
+
task_kwargs["aliases"] = filtered_aliases
|
|
778
794
|
if positional:
|
|
779
795
|
task_kwargs["positional"] = positional
|
|
780
796
|
if optional:
|
|
@@ -146,10 +146,12 @@ def test_script_loads_project_config(tmp_path: Path, capsys, monkeypatch):
|
|
|
146
146
|
# Create a temporary directory with a script and config file
|
|
147
147
|
monkeypatch.chdir(tmp_path)
|
|
148
148
|
|
|
149
|
-
# Create invoke.yml config file with echo=true
|
|
149
|
+
# Create invoke.yml config file with echo=true
|
|
150
|
+
# Note: pty=true is intentionally omitted — a PTY requires a real terminal
|
|
151
|
+
# and would cause pytest to raise OSError when output is captured.
|
|
150
152
|
config_file = tmp_path / "invoke.yml"
|
|
151
153
|
config_file.write_text(
|
|
152
|
-
"run:\n echo: true\n
|
|
154
|
+
"run:\n echo: true\n",
|
|
153
155
|
encoding="utf-8",
|
|
154
156
|
)
|
|
155
157
|
|
|
@@ -158,8 +160,7 @@ def test_script_loads_project_config(tmp_path: Path, capsys, monkeypatch):
|
|
|
158
160
|
def check_config(c):
|
|
159
161
|
# Check that config was loaded
|
|
160
162
|
assert c.config.run.echo is True, "echo should be True from invoke.yml"
|
|
161
|
-
|
|
162
|
-
c.run("echo 'Config loaded successfully'")
|
|
163
|
+
c.run("echo 'Config loaded successfully'", in_stream=False)
|
|
163
164
|
|
|
164
165
|
# Run script and verify config was loaded
|
|
165
166
|
# Note: need to pass script name as first arg for invoke
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Tests for self-referential alias guard in @task."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from invoke import Collection
|
|
7
|
+
|
|
8
|
+
from invoke_toolkit.tasks.tasks import task
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _make_collection(*tasks):
|
|
12
|
+
c = Collection()
|
|
13
|
+
for t in tasks:
|
|
14
|
+
c.add_task(t)
|
|
15
|
+
return c
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Warning is emitted
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_warns_when_alias_matches_function_name_underscore():
|
|
24
|
+
"""Alias equal to the function name (underscore form) triggers a UserWarning."""
|
|
25
|
+
with pytest.warns(UserWarning, match="match the task name"):
|
|
26
|
+
|
|
27
|
+
@task(aliases=["my_task"])
|
|
28
|
+
def my_task(ctx):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_warns_when_alias_matches_function_name_hyphen():
|
|
33
|
+
"""Alias equal to the hyphenated function name triggers a UserWarning."""
|
|
34
|
+
with pytest.warns(UserWarning, match="match the task name"):
|
|
35
|
+
|
|
36
|
+
@task(aliases=["my-task"])
|
|
37
|
+
def my_task(ctx):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_warns_when_alias_matches_explicit_name():
|
|
42
|
+
"""Alias equal to an explicit name= override triggers a UserWarning."""
|
|
43
|
+
with pytest.warns(UserWarning, match="match the task name"):
|
|
44
|
+
|
|
45
|
+
@task(name="deploy", aliases=["deploy"])
|
|
46
|
+
def run_deploy(ctx):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Self-referential alias is stripped (no RecursionError)
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_self_alias_stripped_task_is_reachable():
|
|
56
|
+
"""After stripping the self-alias the task is still reachable by its name."""
|
|
57
|
+
with warnings.catch_warnings():
|
|
58
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
59
|
+
|
|
60
|
+
@task(aliases=["my_task"])
|
|
61
|
+
def my_task(ctx):
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
c = _make_collection(my_task)
|
|
65
|
+
# Must not raise RecursionError
|
|
66
|
+
resolved = c["my-task"]
|
|
67
|
+
assert resolved is not None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_self_alias_stripped_valid_aliases_kept():
|
|
71
|
+
"""Valid aliases are preserved; only the self-referential one is removed."""
|
|
72
|
+
with warnings.catch_warnings():
|
|
73
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
74
|
+
|
|
75
|
+
@task(aliases=["my_task", "mt"])
|
|
76
|
+
def my_task(ctx):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
c = _make_collection(my_task)
|
|
80
|
+
assert c["mt"] is not None
|
|
81
|
+
assert c["my-task"] is not None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_all_aliases_self_referential_no_aliases_registered():
|
|
85
|
+
"""When every alias is self-referential the task has no aliases."""
|
|
86
|
+
with warnings.catch_warnings():
|
|
87
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
88
|
+
|
|
89
|
+
@task(aliases=["my_task3"])
|
|
90
|
+
def my_task3(ctx):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
c = _make_collection(my_task3)
|
|
94
|
+
# task_names maps canonical name → list of aliases; list should be empty
|
|
95
|
+
assert c.task_names["my-task3"] == []
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_no_warning_for_distinct_alias():
|
|
99
|
+
"""A genuinely distinct alias produces no warning."""
|
|
100
|
+
with warnings.catch_warnings():
|
|
101
|
+
warnings.simplefilter("error", UserWarning) # would raise if a warning fires
|
|
102
|
+
|
|
103
|
+
@task(aliases=["mt"])
|
|
104
|
+
def my_task(ctx):
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
c = _make_collection(my_task)
|
|
108
|
+
assert c["mt"] is not None
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Tests for --list-tasks and --list-plugins filter flags."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from invoke_toolkit import Context, task
|
|
7
|
+
from invoke_toolkit.collections import ToolkitCollection
|
|
8
|
+
from invoke_toolkit.testing import TestingToolkitProgram
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
# Shared task/collection factories
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@task()
|
|
17
|
+
def main_task_a(ctx: Context):
|
|
18
|
+
"""Main task A"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@task()
|
|
22
|
+
def main_task_b(ctx: Context):
|
|
23
|
+
"""Main task B"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@task()
|
|
27
|
+
def plugin_task_x(ctx: Context):
|
|
28
|
+
"""Plugin task X"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@task()
|
|
32
|
+
def plugin_task_y(ctx: Context):
|
|
33
|
+
"""Plugin task Y"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _make_collection_with_plugins() -> tuple[ToolkitCollection, set[str]]:
|
|
37
|
+
"""
|
|
38
|
+
Build a ToolkitCollection that looks like 'tasks.py + two plugin sub-collections'.
|
|
39
|
+
Returns the collection and the set of plugin sub-collection names.
|
|
40
|
+
"""
|
|
41
|
+
coll = ToolkitCollection("root")
|
|
42
|
+
coll.add_task(main_task_a)
|
|
43
|
+
coll.add_task(main_task_b)
|
|
44
|
+
|
|
45
|
+
plugin_coll = ToolkitCollection("myplugin")
|
|
46
|
+
plugin_coll.add_task(plugin_task_x)
|
|
47
|
+
plugin_coll.add_task(plugin_task_y)
|
|
48
|
+
coll.add_collection(plugin_coll, name="myplugin")
|
|
49
|
+
|
|
50
|
+
another_plugin = ToolkitCollection("otherplugin")
|
|
51
|
+
coll.add_collection(another_plugin, name="otherplugin")
|
|
52
|
+
|
|
53
|
+
return coll, {"myplugin", "otherplugin"}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _program_with_plugins(
|
|
57
|
+
plugin_names: set[str] | None = None,
|
|
58
|
+
) -> tuple[TestingToolkitProgram, ToolkitCollection]:
|
|
59
|
+
"""Return a configured TestingToolkitProgram with a plugin-aware collection."""
|
|
60
|
+
coll, plugin_set = _make_collection_with_plugins()
|
|
61
|
+
p = TestingToolkitProgram(namespace=coll)
|
|
62
|
+
if plugin_names is None:
|
|
63
|
+
p._plugin_collection_names = plugin_set
|
|
64
|
+
else:
|
|
65
|
+
p._plugin_collection_names = plugin_names
|
|
66
|
+
return p, coll
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# --list-tasks
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_list_tasks_shows_main_tasks(capsys, suppress_stderr_logging):
|
|
75
|
+
"""--list-tasks output contains the direct main tasks."""
|
|
76
|
+
p, _ = _program_with_plugins()
|
|
77
|
+
p.run(["", "--list-tasks", "--list-format", "json"])
|
|
78
|
+
out, _ = capsys.readouterr()
|
|
79
|
+
data = json.loads(out)
|
|
80
|
+
task_names = {t["name"] for t in data["tasks"]}
|
|
81
|
+
assert "main-task-a" in task_names
|
|
82
|
+
assert "main-task-b" in task_names
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_list_tasks_excludes_plugin_collections(capsys, suppress_stderr_logging):
|
|
86
|
+
"""--list-tasks must NOT include plugin sub-collections."""
|
|
87
|
+
p, _ = _program_with_plugins()
|
|
88
|
+
p.run(["", "--list-tasks", "--list-format", "json"])
|
|
89
|
+
out, _ = capsys.readouterr()
|
|
90
|
+
data = json.loads(out)
|
|
91
|
+
coll_names = {c["name"] for c in data.get("collections", [])}
|
|
92
|
+
assert "myplugin" not in coll_names
|
|
93
|
+
assert "otherplugin" not in coll_names
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_list_tasks_flat_format(capsys, suppress_stderr_logging):
|
|
97
|
+
"""--list-tasks works with the default flat format (no JSON)."""
|
|
98
|
+
p, _ = _program_with_plugins()
|
|
99
|
+
p.run(["", "--list-tasks"])
|
|
100
|
+
out, _ = capsys.readouterr()
|
|
101
|
+
assert "main-task-a" in out
|
|
102
|
+
assert "main-task-b" in out
|
|
103
|
+
assert "myplugin" not in out
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_list_tasks_no_plugins_registered(capsys, suppress_stderr_logging):
|
|
107
|
+
"""--list-tasks when there are no plugins still shows the main tasks."""
|
|
108
|
+
coll = ToolkitCollection("root")
|
|
109
|
+
coll.add_task(main_task_a)
|
|
110
|
+
p = TestingToolkitProgram(namespace=coll)
|
|
111
|
+
# _plugin_collection_names starts empty (no plugins)
|
|
112
|
+
p.run(["", "--list-tasks", "--list-format", "json"])
|
|
113
|
+
out, _ = capsys.readouterr()
|
|
114
|
+
data = json.loads(out)
|
|
115
|
+
task_names = {t["name"] for t in data["tasks"]}
|
|
116
|
+
assert "main-task-a" in task_names
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_list_tasks_preserves_non_plugin_subcollections(
|
|
120
|
+
capsys, suppress_stderr_logging
|
|
121
|
+
):
|
|
122
|
+
"""Non-plugin sub-collections (e.g. from tasks.py namespace) are kept by --list-tasks."""
|
|
123
|
+
coll = ToolkitCollection("root")
|
|
124
|
+
coll.add_task(main_task_a)
|
|
125
|
+
|
|
126
|
+
sub = ToolkitCollection("utils")
|
|
127
|
+
sub.add_task(main_task_b)
|
|
128
|
+
coll.add_collection(sub, name="utils")
|
|
129
|
+
|
|
130
|
+
plugin_coll = ToolkitCollection("myplugin")
|
|
131
|
+
plugin_coll.add_task(plugin_task_x)
|
|
132
|
+
coll.add_collection(plugin_coll, name="myplugin")
|
|
133
|
+
|
|
134
|
+
p = TestingToolkitProgram(namespace=coll)
|
|
135
|
+
p._plugin_collection_names = {"myplugin"}
|
|
136
|
+
|
|
137
|
+
p.run(["", "--list-tasks", "--list-format", "json"])
|
|
138
|
+
out, _ = capsys.readouterr()
|
|
139
|
+
data = json.loads(out)
|
|
140
|
+
coll_names = {c["name"] for c in data.get("collections", [])}
|
|
141
|
+
assert "utils" in coll_names
|
|
142
|
+
assert "myplugin" not in coll_names
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# --list-plugins
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_list_plugins_shows_plugin_collections(capsys, suppress_stderr_logging):
|
|
151
|
+
"""--list-plugins output contains the plugin sub-collections."""
|
|
152
|
+
p, _ = _program_with_plugins()
|
|
153
|
+
p.run(["", "--list-plugins", "--list-format", "json"])
|
|
154
|
+
out, _ = capsys.readouterr()
|
|
155
|
+
data = json.loads(out)
|
|
156
|
+
coll_names = {c["name"] for c in data.get("collections", [])}
|
|
157
|
+
assert "myplugin" in coll_names
|
|
158
|
+
assert "otherplugin" in coll_names
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_list_plugins_excludes_main_tasks(capsys, suppress_stderr_logging):
|
|
162
|
+
"""--list-plugins must NOT include the direct main tasks."""
|
|
163
|
+
p, _ = _program_with_plugins()
|
|
164
|
+
p.run(["", "--list-plugins", "--list-format", "json"])
|
|
165
|
+
out, _ = capsys.readouterr()
|
|
166
|
+
data = json.loads(out)
|
|
167
|
+
task_names = {t["name"] for t in data.get("tasks", [])}
|
|
168
|
+
assert "main-task-a" not in task_names
|
|
169
|
+
assert "main-task-b" not in task_names
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_list_plugins_flat_format(capsys, suppress_stderr_logging):
|
|
173
|
+
"""--list-plugins works with the default flat format."""
|
|
174
|
+
p, _ = _program_with_plugins()
|
|
175
|
+
p.run(["", "--list-plugins"])
|
|
176
|
+
out, _ = capsys.readouterr()
|
|
177
|
+
assert "myplugin" in out
|
|
178
|
+
assert "main-task-a" not in out
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_list_plugins_no_plugins_exits_with_message(capsys, suppress_stderr_logging):
|
|
182
|
+
"""--list-plugins when there are no plugins exits with a clear message."""
|
|
183
|
+
coll = ToolkitCollection("root")
|
|
184
|
+
coll.add_task(main_task_a)
|
|
185
|
+
p = TestingToolkitProgram(namespace=coll)
|
|
186
|
+
# _plugin_collection_names is empty — no plugins loaded
|
|
187
|
+
p.run(["", "--list-plugins"])
|
|
188
|
+
# run(..., exit=False) so no sys.exit — just check stderr for the message
|
|
189
|
+
_, err = capsys.readouterr()
|
|
190
|
+
assert "No plugin collections found" in err
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ---------------------------------------------------------------------------
|
|
194
|
+
# Mutual exclusion sanity (using both flags at once: first one wins)
|
|
195
|
+
# ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_list_tasks_takes_precedence_over_list_plugins(capsys, suppress_stderr_logging):
|
|
199
|
+
"""When both --list-tasks and --list-plugins are given, --list-tasks runs first."""
|
|
200
|
+
p, _ = _program_with_plugins()
|
|
201
|
+
p.run(["", "--list-tasks", "--list-plugins", "--list-format", "json"])
|
|
202
|
+
out, _ = capsys.readouterr()
|
|
203
|
+
data = json.loads(out)
|
|
204
|
+
# Should behave like --list-tasks
|
|
205
|
+
task_names = {t["name"] for t in data["tasks"]}
|
|
206
|
+
assert "main-task-a" in task_names
|
|
207
|
+
coll_names = {c["name"] for c in data.get("collections", [])}
|
|
208
|
+
assert "myplugin" not in coll_names
|
|
@@ -176,22 +176,25 @@ def test_proctitle_visible_in_ps(tmp_path: Path):
|
|
|
176
176
|
tasks_file.write_text(tasks_content)
|
|
177
177
|
|
|
178
178
|
# Start the task in a subprocess
|
|
179
|
-
|
|
179
|
+
proc = subprocess.Popen( # pylint: disable=consider-using-with
|
|
180
180
|
[sys.executable, "-m", "invoke_toolkit", "-r", str(tmp_path), "test-proctitle"],
|
|
181
181
|
stdout=subprocess.PIPE,
|
|
182
182
|
stderr=subprocess.PIPE,
|
|
183
|
-
)
|
|
183
|
+
)
|
|
184
|
+
try:
|
|
184
185
|
# Wait for the task to signal it's ready
|
|
185
|
-
|
|
186
|
+
startup_timeout = 10
|
|
186
187
|
start = time.time()
|
|
187
188
|
while not signal_file.exists():
|
|
188
|
-
if time.time() - start >
|
|
189
|
-
|
|
189
|
+
if time.time() - start > startup_timeout:
|
|
190
|
+
proc.kill()
|
|
191
|
+
proc.wait(timeout=5)
|
|
192
|
+
pytest.fail("Task did not start in time")
|
|
190
193
|
if proc.poll() is not None:
|
|
191
194
|
stdout, stderr = proc.communicate()
|
|
192
|
-
|
|
195
|
+
pytest.fail(
|
|
193
196
|
f"Process exited early: {proc.returncode}\n"
|
|
194
|
-
|
|
197
|
+
f"stdout: {stdout.decode()}\nstderr: {stderr.decode()}"
|
|
195
198
|
)
|
|
196
199
|
time.sleep(0.05)
|
|
197
200
|
|
|
@@ -210,7 +213,12 @@ def test_proctitle_visible_in_ps(tmp_path: Path):
|
|
|
210
213
|
|
|
211
214
|
# Signal the task to finish
|
|
212
215
|
done_file.write_text("done")
|
|
213
|
-
proc.wait(timeout=
|
|
216
|
+
proc.wait(timeout=10)
|
|
217
|
+
finally:
|
|
218
|
+
# Ensure the subprocess is always cleaned up
|
|
219
|
+
if proc.poll() is None:
|
|
220
|
+
proc.kill()
|
|
221
|
+
proc.wait(timeout=5)
|
|
214
222
|
|
|
215
223
|
|
|
216
224
|
@pytest.fixture
|
|
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
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/__init__.py
RENAMED
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/config.py
RENAMED
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/create.py
RENAMED
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/src/invoke_toolkit/extensions/tasks/shell.py
RENAMED
|
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
|
|
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
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/templates/package-template/pyproject.toml.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/completion_with_config/.gitignore
RENAMED
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/examples/completion_with_config/tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/extensions/test_package_template_entrypoint.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/collection.py
RENAMED
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/echo.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/no-echo.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/runtime.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/yaml/tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/configs/yml/tasks.py
RENAMED
|
File without changes
|
{invoke_toolkit-0.0.63 → invoke_toolkit-0.0.64}/tests/original_invoke/_support/has_modules.py
RENAMED
|
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
|
|
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
|