click-extended 1.0.8__py3-none-any.whl → 1.0.9__py3-none-any.whl

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.
@@ -664,7 +664,33 @@ class RootNode(Node):
664
664
  context,
665
665
  )
666
666
 
667
- for validation_node in root.tree.validations:
667
+ catch_nodes = [
668
+ v
669
+ for v in root.tree.validations
670
+ if v.__class__.__name__ == "Catch"
671
+ ]
672
+ other_nodes = [
673
+ v
674
+ for v in root.tree.validations
675
+ if v.__class__.__name__ != "Catch"
676
+ ]
677
+ root.tree.validations = catch_nodes + other_nodes
678
+
679
+ for i, validation_node in enumerate(root.tree.validations):
680
+ if validation_node.__class__.__name__ == "Catch":
681
+ if hasattr(
682
+ validation_node, "remaining_validations"
683
+ ):
684
+ validation_node.remaining_validations = (
685
+ root.tree.validations[i + 1 :]
686
+ )
687
+ validation_node.on_finalize(
688
+ custom_context,
689
+ *validation_node.process_args,
690
+ **validation_node.process_kwargs,
691
+ )
692
+ break
693
+
668
694
  validation_node.on_finalize(
669
695
  custom_context,
670
696
  *validation_node.process_args,
@@ -2,6 +2,10 @@
2
2
  Validation node to catch and handle exceptions from command/group functions.
3
3
  """
4
4
 
5
+ # pylint: disable=wrong-import-position
6
+ # pylint: disable=wrong-import-order
7
+ # pylint: disable=ungrouped-imports
8
+
5
9
  import asyncio
6
10
  import inspect
7
11
  from functools import wraps
@@ -23,11 +27,16 @@ _catch_handlers: WeakKeyDictionary[
23
27
 
24
28
  class Catch(ValidationNode):
25
29
  """
26
- Catch and handle exceptions from command/group functions.
30
+ Catch and handle exceptions from command/group functions and
31
+ validators.
32
+
33
+ Wraps both the validation phase and function execution in a
34
+ try-except block. When an exception is caught, an optional handler
35
+ is invoked. Without a handler, exceptions are silently suppressed.
27
36
 
28
- Wraps the decorated function in a try-except block. When an exception
29
- is caught, an optional handler is invoked. Without a handler, exceptions
30
- are silently suppressed.
37
+ Catches exceptions from:
38
+ - Validation decorators (e.g., @exclusive, @requires)
39
+ - The command/group function itself
31
40
 
32
41
  Handler signatures supported:
33
42
  - `handler()` - No arguments, just execute code
@@ -36,15 +45,16 @@ class Catch(ValidationNode):
36
45
 
37
46
  Examples:
38
47
  ```py
39
- # Simple error logging
48
+ # Catch validation errors from @exclusive
40
49
  @command()
41
- @catch(ValueError, handler=lambda: print("Invalid value!"))
42
- def cmd():
43
- raise ValueError("Bad input")
50
+ @exclusive("--stock", "--query")
51
+ @catch(ValueError, handler=lambda e: print(f"Error: {e}"))
52
+ def search(stock, query):
53
+ pass
44
54
  ```
45
55
 
46
56
  ```py
47
- # Handle exception with details
57
+ # Catch errors from command function
48
58
  @command()
49
59
  @catch(ValueError, handler=lambda e: print(f"Error: {e}"))
50
60
  def cmd():
@@ -73,19 +83,52 @@ class Catch(ValidationNode):
73
83
  """Initialize Catch validation node with function to wrap."""
74
84
  super().__init__(name, process_args, process_kwargs, **kwargs)
75
85
  self.wrapped_func: Callable[..., Any] | None = None
86
+ self.remaining_validations: list[ValidationNode] = []
76
87
 
77
88
  def on_finalize(self, context: Context, *args: Any, **kwargs: Any) -> None:
78
89
  """
79
- Store exception handler configuration for later use.
90
+ Execute remaining validations wrapped in exception handling.
80
91
 
81
- The actual exception catching happens when the function is invoked,
82
- which is done by wrapping the function in the decorator.
92
+ This catches exceptions from all validators that run after @catch,
93
+ allowing it to catch validation errors like those from @exclusive.
83
94
 
84
95
  Args:
85
96
  context: The execution context
86
97
  *args: Contains exception types tuple at index 0
87
98
  **kwargs: Contains handler, reraise parameters
88
99
  """
100
+ exception_types: tuple[type[BaseException], ...] = args[0]
101
+ handler: Callable[..., Any] | None = kwargs.get("handler")
102
+ reraise: bool = kwargs.get("reraise", False)
103
+
104
+ for validation_node in self.remaining_validations:
105
+ try:
106
+ if asyncio.iscoroutinefunction(validation_node.on_finalize):
107
+ asyncio.run(
108
+ validation_node.on_finalize(
109
+ context,
110
+ *validation_node.process_args,
111
+ **validation_node.process_kwargs,
112
+ )
113
+ )
114
+ else:
115
+ validation_node.on_finalize(
116
+ context,
117
+ *validation_node.process_args,
118
+ **validation_node.process_kwargs,
119
+ )
120
+ except BaseException as exc:
121
+ if isinstance(exc, exception_types):
122
+ if handler is not None:
123
+ if asyncio.iscoroutinefunction(handler):
124
+ asyncio.run(_call_handler_async(handler, exc))
125
+ else:
126
+ _call_handler_sync(handler, exc)
127
+
128
+ if not reraise:
129
+ return
130
+
131
+ raise
89
132
 
90
133
 
91
134
  def catch(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: click_extended
3
- Version: 1.0.8
3
+ Version: 1.0.9
4
4
  Summary: An extension to Click with additional features like automatic async support, aliasing and a modular decorator system.
5
5
  Author-email: Marcus Fredriksson <marcus@marcusfredriksson.com>
6
6
  License: MIT License
@@ -14,7 +14,7 @@ click_extended/core/decorators/prompt.py,sha256=QwySQ6ZK0j0iL_jlwH3AU0U4G1X3MKAJ
14
14
  click_extended/core/decorators/selection.py,sha256=5cjWpAwReu8EY0mkziwpqhi_T5ucIlAPx1p-t3MkGJQ,5589
15
15
  click_extended/core/decorators/tag.py,sha256=395e6GN59QBj-L0NSBPynQOX3_2LAOIXkj8XKNXrRPs,3055
16
16
  click_extended/core/nodes/__init__.py,sha256=jSsKTiHPUpqXdk54cRXDotT0YMmVGezIIb3nQ0rcWko,737
17
- click_extended/core/nodes/_root_node.py,sha256=PitK6GT7LPYugxPFdVO6blG0-mZV9S2apUupgWCsYI0,43060
17
+ click_extended/core/nodes/_root_node.py,sha256=cNmTPtREVjNi6WZj3chtYFmn9_2iqJh4l32NEmO0Db4,44233
18
18
  click_extended/core/nodes/argument_node.py,sha256=gqG4HTDR40Xru_U_beKsbjVtLtwmPKfwwAUmcf5-1iQ,5284
19
19
  click_extended/core/nodes/child_node.py,sha256=yIuXmUzdXN0EToR9ohBRvHFBriSZ-xaLkv7P27PbTrA,17095
20
20
  click_extended/core/nodes/child_validation_node.py,sha256=Xcqid9EL3pHVrgv_43zz-ZL4W4yFm0Esj2qkk9qhWMc,3807
@@ -98,7 +98,7 @@ click_extended/decorators/math/sqrt.py,sha256=CWZoQpVw2lbP_LNeg-tWW8fz9VEHTX8eQN
98
98
  click_extended/decorators/math/subtract.py,sha256=A7TMByTUH3coSEe7iRN9NkHW2mv47kufb1H1adomG4A,806
99
99
  click_extended/decorators/math/to_percent.py,sha256=39vY13u7Z-pyob1vCoPBDy10FqJasJTVzHcrpTH6ERQ,1374
100
100
  click_extended/decorators/misc/__init__.py,sha256=Cl2u81f_dkvHoMoSb82L1KuNVM-3b1lzKHUMAuxhmH0,662
101
- click_extended/decorators/misc/catch.py,sha256=Ft8CbTlX6OgeNA8re_epKfwK4HyWQ1eUuCbhNqzpNbw,9343
101
+ click_extended/decorators/misc/catch.py,sha256=LkBxGHNo7_RrXjUP0LOTYOLvb9q1fTBIULJJIcYXo4Y,11095
102
102
  click_extended/decorators/misc/choice.py,sha256=RTVVjl0YUZ2Ti4j6yoHkUpJ0aqNwcnc3rTfmCVr0y0U,4354
103
103
  click_extended/decorators/misc/confirm_if.py,sha256=Rf3HcVVTRqz3sGSiPT02yz65_L351Fzkuw2V6pljlmY,4786
104
104
  click_extended/decorators/misc/default.py,sha256=TjZJKmHEqNThzyD6hOjQy2_QgHRenKILnAwfV1V5KAw,2473
@@ -144,9 +144,9 @@ click_extended/utils/naming.py,sha256=kNmzOqidgZZ1dE5aMYrxpWt4VwcLuEiFunpumpfHuZ
144
144
  click_extended/utils/process.py,sha256=sU3ZCMjBgjKcDnTRLKPdQ_TAjk0AT7Q8SatagD0JyHA,9729
145
145
  click_extended/utils/selection.py,sha256=_OQC88pGPUh29boxmS5ugXEi9jZGqAG180S27PeQaj0,9875
146
146
  click_extended/utils/time.py,sha256=H5m5caIEau_1GHkiYgKL_LcTtVdw2TkFVbkqJu7A9rQ,1067
147
- click_extended-1.0.8.dist-info/licenses/AUTHORS.md,sha256=NkShPinjqtnRDQVRyVnfJuOGM56sejauE3WRoYCcbtw,132
148
- click_extended-1.0.8.dist-info/licenses/LICENSE,sha256=gjO8hzM4mFSBXFikktaXVSgmXGcre91_GPJ-E_yP56E,1075
149
- click_extended-1.0.8.dist-info/METADATA,sha256=j42ol1yFZRyvuaiM_g7_-LVNJHFuMX6BDdcog4EjTC8,10841
150
- click_extended-1.0.8.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
151
- click_extended-1.0.8.dist-info/top_level.txt,sha256=2G3bm6tCNv80okRm773jKTk-_z1ElY-seaozZrn_TxA,15
152
- click_extended-1.0.8.dist-info/RECORD,,
147
+ click_extended-1.0.9.dist-info/licenses/AUTHORS.md,sha256=NkShPinjqtnRDQVRyVnfJuOGM56sejauE3WRoYCcbtw,132
148
+ click_extended-1.0.9.dist-info/licenses/LICENSE,sha256=gjO8hzM4mFSBXFikktaXVSgmXGcre91_GPJ-E_yP56E,1075
149
+ click_extended-1.0.9.dist-info/METADATA,sha256=nY4Le_lb7iE550nRKd8EoLDp1Jp5nsXqg2gAPgc6jy8,10841
150
+ click_extended-1.0.9.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
151
+ click_extended-1.0.9.dist-info/top_level.txt,sha256=2G3bm6tCNv80okRm773jKTk-_z1ElY-seaozZrn_TxA,15
152
+ click_extended-1.0.9.dist-info/RECORD,,