wexample-cli 1.1.0__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.
Files changed (37) hide show
  1. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/PKG-INFO +8 -8
  2. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/README.md +4 -4
  3. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/pyproject.toml +4 -4
  4. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/command/extended_command.py +12 -13
  5. wexample_cli-1.2.0/tests/helpers/__init__.py +0 -0
  6. wexample_cli-1.2.0/tests/helpers/test_extra_args.py +38 -0
  7. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/__init__.py +0 -0
  8. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/command/__init__.py +0 -0
  9. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/common/__init__.py +0 -0
  10. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/common/command_method_wrapper.py +0 -0
  11. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/const/__init__.py +0 -0
  12. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/const/middleware.py +0 -0
  13. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/const/tags.py +0 -0
  14. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/const/types.py +0 -0
  15. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/context/__init__.py +0 -0
  16. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/context/execution_context.py +0 -0
  17. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/__init__.py +0 -0
  18. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/alias.py +0 -0
  19. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/as_sudo.py +0 -0
  20. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/command.py +0 -0
  21. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/middleware.py +0 -0
  22. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/option.py +0 -0
  23. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/option_stop_on_failure.py +0 -0
  24. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/screenable.py +0 -0
  25. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/decorator/webhook.py +0 -0
  26. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/exception/__init__.py +0 -0
  27. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/exception/abstract_command_option_exception.py +0 -0
  28. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/exception/command_option_missing_exception.py +0 -0
  29. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/exception/command_option_validation_exception.py +0 -0
  30. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/helpers/__init__.py +0 -0
  31. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/helpers/extra_args.py +0 -0
  32. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/middleware/__init__.py +0 -0
  33. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/middleware/abstract_middleware.py +0 -0
  34. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/py.typed +0 -0
  35. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/testing/__init__.py +0 -0
  36. {wexample_cli-1.1.0 → wexample_cli-1.2.0}/src/wexample_cli/testing/kernel.py +0 -0
  37. {wexample_cli-1.1.0 → 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.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.0.0
14
- Requires-Dist: wexample-helpers>=16.0.0
15
- Requires-Dist: wexample-prompt>=13.1.0
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.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.0.0
104
- - wexample-helpers: >=16.0.0
105
- - wexample-prompt: >=13.1.0
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.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.0.0
84
- - wexample-helpers: >=16.0.0
85
- - wexample-prompt: >=13.1.0
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.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.0.0",
23
- "wexample-helpers>=16.0.0",
24
- "wexample-prompt>=13.1.0",
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(**middlewares_attributes[name])
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 option.name in parsed_args:
92
- value = parsed_args[option.name]
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=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[option.name] = value
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 len(self.command_wrapper.middlewares) > 0:
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
- for execution_context in execution_contexts:
324
- task = asyncio.create_task(execute_single_pass(execution_context))
325
- tasks.append(task)
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)
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"