wexample-cli 1.0.2__tar.gz → 1.2.0__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.
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/PKG-INFO +8 -8
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/README.md +4 -4
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/pyproject.toml +4 -4
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/command/extended_command.py +12 -13
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/screenable.py +8 -1
- wexample_cli-1.2.0/tests/helpers/__init__.py +0 -0
- wexample_cli-1.2.0/tests/helpers/test_extra_args.py +38 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/command/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/common/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/common/command_method_wrapper.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/const/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/const/middleware.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/const/tags.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/const/types.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/context/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/context/execution_context.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/alias.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/as_sudo.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/command.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/middleware.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/option.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/option_stop_on_failure.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/webhook.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/exception/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/exception/abstract_command_option_exception.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/exception/command_option_missing_exception.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/exception/command_option_validation_exception.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/helpers/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/helpers/extra_args.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/middleware/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/middleware/abstract_middleware.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/py.typed +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/testing/__init__.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/testing/kernel.py +0 -0
- {wexample_cli-1.0.2 → wexample_cli-1.2.0}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: wexample-cli
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Reusable CLI primitives — command decorators, options, middlewares, and the enriched command runner — extracted from wex-core so any kernel built on wexample-app can opt in without depending on the full wex framework.
|
|
5
5
|
Author-Email: weeger <contact@wexample.com>
|
|
6
6
|
License: MIT
|
|
@@ -10,9 +10,9 @@ Classifier: Operating System :: OS Independent
|
|
|
10
10
|
Project-URL: homepage, https://github.com/wexample/python-cli
|
|
11
11
|
Requires-Python: >=3.10
|
|
12
12
|
Requires-Dist: attrs>=23.1.0
|
|
13
|
-
Requires-Dist: wexample-app>=18.
|
|
14
|
-
Requires-Dist: wexample-helpers>=16.
|
|
15
|
-
Requires-Dist: wexample-prompt>=13.
|
|
13
|
+
Requires-Dist: wexample-app>=18.1.0
|
|
14
|
+
Requires-Dist: wexample-helpers>=16.2.0
|
|
15
|
+
Requires-Dist: wexample-prompt>=13.2.0
|
|
16
16
|
Provides-Extra: dev
|
|
17
17
|
Requires-Dist: pytest; extra == "dev"
|
|
18
18
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -20,7 +20,7 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
|
|
21
21
|
# cli
|
|
22
22
|
|
|
23
|
-
Version: 1.0
|
|
23
|
+
Version: 1.2.0
|
|
24
24
|
|
|
25
25
|
Reusable CLI primitives — command decorators, options, middlewares, and the enriched command runner — extracted from wex-core so any kernel built on wexample-app can opt in without depending on the full wex framework.
|
|
26
26
|
|
|
@@ -100,9 +100,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
100
100
|
## Dependencies
|
|
101
101
|
|
|
102
102
|
- attrs: >=23.1.0
|
|
103
|
-
- wexample-app: >=18.
|
|
104
|
-
- wexample-helpers: >=16.
|
|
105
|
-
- wexample-prompt: >=13.
|
|
103
|
+
- wexample-app: >=18.1.0
|
|
104
|
+
- wexample-helpers: >=16.2.0
|
|
105
|
+
- wexample-prompt: >=13.2.0
|
|
106
106
|
|
|
107
107
|
## Versioning & Compatibility Policy
|
|
108
108
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# cli
|
|
2
2
|
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
|
|
5
5
|
Reusable CLI primitives — command decorators, options, middlewares, and the enriched command runner — extracted from wex-core so any kernel built on wexample-app can opt in without depending on the full wex framework.
|
|
6
6
|
|
|
@@ -80,9 +80,9 @@ Visit the [Wexample Suite documentation](https://docs.wexample.com) for the comp
|
|
|
80
80
|
## Dependencies
|
|
81
81
|
|
|
82
82
|
- attrs: >=23.1.0
|
|
83
|
-
- wexample-app: >=18.
|
|
84
|
-
- wexample-helpers: >=16.
|
|
85
|
-
- wexample-prompt: >=13.
|
|
83
|
+
- wexample-app: >=18.1.0
|
|
84
|
+
- wexample-helpers: >=16.2.0
|
|
85
|
+
- wexample-prompt: >=13.2.0
|
|
86
86
|
|
|
87
87
|
## Versioning & Compatibility Policy
|
|
88
88
|
|
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "wexample-cli"
|
|
9
|
-
version = "1.0
|
|
9
|
+
version = "1.2.0"
|
|
10
10
|
description = "Reusable CLI primitives — command decorators, options, middlewares, and the enriched command runner — extracted from wex-core so any kernel built on wexample-app can opt in without depending on the full wex framework."
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "weeger", email = "contact@wexample.com" },
|
|
@@ -19,9 +19,9 @@ classifiers = [
|
|
|
19
19
|
]
|
|
20
20
|
dependencies = [
|
|
21
21
|
"attrs>=23.1.0",
|
|
22
|
-
"wexample-app>=18.
|
|
23
|
-
"wexample-helpers>=16.
|
|
24
|
-
"wexample-prompt>=13.
|
|
22
|
+
"wexample-app>=18.1.0",
|
|
23
|
+
"wexample-helpers>=16.2.0",
|
|
24
|
+
"wexample-prompt>=13.2.0",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[project.readme]
|
|
@@ -38,9 +38,9 @@ class ExtendedCommand(Command):
|
|
|
38
38
|
middlewares_attributes = self.command_wrapper.middlewares_attributes
|
|
39
39
|
middlewares_registry = self.kernel.get_registry("middlewares")
|
|
40
40
|
|
|
41
|
-
for name in middlewares_attributes:
|
|
41
|
+
for name, attributes in middlewares_attributes.items():
|
|
42
42
|
middleware_class = middlewares_registry.get_class(name)
|
|
43
|
-
middleware = middleware_class(**
|
|
43
|
+
middleware = middleware_class(**attributes)
|
|
44
44
|
self.command_wrapper.set_middleware(middleware)
|
|
45
45
|
|
|
46
46
|
# Universal --help / -h handling — render the command's options instead
|
|
@@ -85,17 +85,18 @@ class ExtendedCommand(Command):
|
|
|
85
85
|
|
|
86
86
|
# Process all declared options
|
|
87
87
|
for option in self.command_wrapper.options:
|
|
88
|
+
name = option.name
|
|
88
89
|
value = None
|
|
89
90
|
|
|
90
91
|
# If the option is in parsed args, use that value
|
|
91
|
-
if
|
|
92
|
-
value = parsed_args[
|
|
92
|
+
if name in parsed_args:
|
|
93
|
+
value = parsed_args[name]
|
|
93
94
|
# Otherwise, use the default value if available
|
|
94
95
|
elif option.default is not None:
|
|
95
96
|
value = option.default
|
|
96
97
|
# If the option is required but not provided, raise an error
|
|
97
98
|
elif option.required:
|
|
98
|
-
raise CommandOptionMissingException(option_name=
|
|
99
|
+
raise CommandOptionMissingException(option_name=name)
|
|
99
100
|
|
|
100
101
|
# Validate the value if validators are defined and value is not None
|
|
101
102
|
if value is not None and option.validators:
|
|
@@ -121,7 +122,7 @@ class ExtendedCommand(Command):
|
|
|
121
122
|
|
|
122
123
|
# Assign the validated value
|
|
123
124
|
if value is not None:
|
|
124
|
-
option.value = function_kwargs[
|
|
125
|
+
option.value = function_kwargs[name] = value
|
|
125
126
|
|
|
126
127
|
# `--` passthrough: if the user supplied extra args after `--`, the
|
|
127
128
|
# parser put them in parsed_args["__extra_args__"]. Forward them as
|
|
@@ -158,7 +159,7 @@ class ExtendedCommand(Command):
|
|
|
158
159
|
MIDDLEWARE_OPTION_VALUE_OPTIONAL,
|
|
159
160
|
)
|
|
160
161
|
|
|
161
|
-
if
|
|
162
|
+
if self.command_wrapper.middlewares:
|
|
162
163
|
output = MultipleResponse(kernel=self.kernel)
|
|
163
164
|
|
|
164
165
|
for middleware in self.command_wrapper.middlewares:
|
|
@@ -286,9 +287,6 @@ class ExtendedCommand(Command):
|
|
|
286
287
|
from wexample_app.helpers.response import response_normalize
|
|
287
288
|
from wexample_app.response.abstract_response import AbstractResponse
|
|
288
289
|
|
|
289
|
-
# Create a list to store all tasks
|
|
290
|
-
tasks = []
|
|
291
|
-
|
|
292
290
|
# Create an executor for running CPU-bound functions in a thread pool
|
|
293
291
|
executor = ThreadPoolExecutor(max_workers=min(32, len(execution_contexts)))
|
|
294
292
|
|
|
@@ -320,9 +318,10 @@ class ExtendedCommand(Command):
|
|
|
320
318
|
return response_normalize(kernel=self.kernel, response=result)
|
|
321
319
|
|
|
322
320
|
# Create a task for each pass
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
321
|
+
tasks = [
|
|
322
|
+
asyncio.create_task(execute_single_pass(execution_context))
|
|
323
|
+
for execution_context in execution_contexts
|
|
324
|
+
]
|
|
326
325
|
|
|
327
326
|
# Wait for all tasks to complete
|
|
328
327
|
responses = await asyncio.gather(*tasks)
|
|
@@ -103,10 +103,17 @@ def _build_screen_wrapper(default_interval: float, height: int) -> AnyCallable:
|
|
|
103
103
|
saved_output = kernel_io.output
|
|
104
104
|
kernel_io.output = response.io.output
|
|
105
105
|
try:
|
|
106
|
-
next_dispatch(function_kwargs)
|
|
106
|
+
inner_response = next_dispatch(function_kwargs)
|
|
107
107
|
finally:
|
|
108
108
|
kernel_io.output = saved_output
|
|
109
109
|
|
|
110
|
+
if inner_response is not None:
|
|
111
|
+
formatted = inner_response.get_formatted(
|
|
112
|
+
output_format=request.output_format
|
|
113
|
+
)
|
|
114
|
+
if formatted:
|
|
115
|
+
response.print(formatted)
|
|
116
|
+
|
|
110
117
|
response.reload()
|
|
111
118
|
|
|
112
119
|
kernel_io.screen(callback=_frame, height=height)
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import types
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_resolve_shell_command_joins_extra_args_with_quoting() -> None:
|
|
7
|
+
from wexample_cli.helpers.extra_args import resolve_shell_command
|
|
8
|
+
|
|
9
|
+
result = resolve_shell_command(
|
|
10
|
+
context=None, command=None, extra_args=["echo", "hi there"]
|
|
11
|
+
)
|
|
12
|
+
assert result == "echo 'hi there'"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_resolve_shell_command_returns_none_when_nothing_given() -> None:
|
|
16
|
+
from wexample_cli.helpers.extra_args import resolve_shell_command
|
|
17
|
+
|
|
18
|
+
result = resolve_shell_command(context=None, command=None, extra_args=None)
|
|
19
|
+
assert result is None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_resolve_shell_command_uses_command_when_no_extra_args() -> None:
|
|
23
|
+
from wexample_cli.helpers.extra_args import resolve_shell_command
|
|
24
|
+
|
|
25
|
+
result = resolve_shell_command(context=None, command="ls -la", extra_args=None)
|
|
26
|
+
assert result == "ls -la"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_resolve_shell_command_warns_when_both_given() -> None:
|
|
30
|
+
from wexample_prompt.common.io_manager import IoManager
|
|
31
|
+
|
|
32
|
+
from wexample_cli.helpers.extra_args import resolve_shell_command
|
|
33
|
+
|
|
34
|
+
context = types.SimpleNamespace(io=IoManager())
|
|
35
|
+
result = resolve_shell_command(
|
|
36
|
+
context=context, command="legacy", extra_args=["run"]
|
|
37
|
+
)
|
|
38
|
+
assert result == "run"
|
|
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
|
{wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/decorator/option_stop_on_failure.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
|
{wexample_cli-1.0.2 → wexample_cli-1.2.0}/src/wexample_cli/middleware/abstract_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|