provide-foundation 0.0.0.dev0__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.
Files changed (149) hide show
  1. provide/__init__.py +15 -0
  2. provide/foundation/__init__.py +155 -0
  3. provide/foundation/_version.py +58 -0
  4. provide/foundation/cli/__init__.py +67 -0
  5. provide/foundation/cli/commands/__init__.py +3 -0
  6. provide/foundation/cli/commands/deps.py +71 -0
  7. provide/foundation/cli/commands/logs/__init__.py +63 -0
  8. provide/foundation/cli/commands/logs/generate.py +357 -0
  9. provide/foundation/cli/commands/logs/generate_old.py +569 -0
  10. provide/foundation/cli/commands/logs/query.py +174 -0
  11. provide/foundation/cli/commands/logs/send.py +166 -0
  12. provide/foundation/cli/commands/logs/tail.py +112 -0
  13. provide/foundation/cli/decorators.py +262 -0
  14. provide/foundation/cli/main.py +65 -0
  15. provide/foundation/cli/testing.py +220 -0
  16. provide/foundation/cli/utils.py +210 -0
  17. provide/foundation/config/__init__.py +106 -0
  18. provide/foundation/config/base.py +295 -0
  19. provide/foundation/config/env.py +369 -0
  20. provide/foundation/config/loader.py +311 -0
  21. provide/foundation/config/manager.py +387 -0
  22. provide/foundation/config/schema.py +284 -0
  23. provide/foundation/config/sync.py +281 -0
  24. provide/foundation/config/types.py +78 -0
  25. provide/foundation/config/validators.py +80 -0
  26. provide/foundation/console/__init__.py +29 -0
  27. provide/foundation/console/input.py +364 -0
  28. provide/foundation/console/output.py +178 -0
  29. provide/foundation/context/__init__.py +12 -0
  30. provide/foundation/context/core.py +356 -0
  31. provide/foundation/core.py +20 -0
  32. provide/foundation/crypto/__init__.py +182 -0
  33. provide/foundation/crypto/algorithms.py +111 -0
  34. provide/foundation/crypto/certificates.py +896 -0
  35. provide/foundation/crypto/checksums.py +301 -0
  36. provide/foundation/crypto/constants.py +57 -0
  37. provide/foundation/crypto/hashing.py +265 -0
  38. provide/foundation/crypto/keys.py +188 -0
  39. provide/foundation/crypto/signatures.py +144 -0
  40. provide/foundation/crypto/utils.py +164 -0
  41. provide/foundation/errors/__init__.py +96 -0
  42. provide/foundation/errors/auth.py +73 -0
  43. provide/foundation/errors/base.py +81 -0
  44. provide/foundation/errors/config.py +103 -0
  45. provide/foundation/errors/context.py +299 -0
  46. provide/foundation/errors/decorators.py +484 -0
  47. provide/foundation/errors/handlers.py +360 -0
  48. provide/foundation/errors/integration.py +105 -0
  49. provide/foundation/errors/platform.py +37 -0
  50. provide/foundation/errors/process.py +140 -0
  51. provide/foundation/errors/resources.py +133 -0
  52. provide/foundation/errors/runtime.py +160 -0
  53. provide/foundation/errors/safe_decorators.py +133 -0
  54. provide/foundation/errors/types.py +276 -0
  55. provide/foundation/file/__init__.py +79 -0
  56. provide/foundation/file/atomic.py +157 -0
  57. provide/foundation/file/directory.py +134 -0
  58. provide/foundation/file/formats.py +236 -0
  59. provide/foundation/file/lock.py +175 -0
  60. provide/foundation/file/safe.py +179 -0
  61. provide/foundation/file/utils.py +170 -0
  62. provide/foundation/hub/__init__.py +88 -0
  63. provide/foundation/hub/click_builder.py +310 -0
  64. provide/foundation/hub/commands.py +42 -0
  65. provide/foundation/hub/components.py +640 -0
  66. provide/foundation/hub/decorators.py +244 -0
  67. provide/foundation/hub/info.py +32 -0
  68. provide/foundation/hub/manager.py +446 -0
  69. provide/foundation/hub/registry.py +279 -0
  70. provide/foundation/hub/type_mapping.py +54 -0
  71. provide/foundation/hub/types.py +28 -0
  72. provide/foundation/logger/__init__.py +41 -0
  73. provide/foundation/logger/base.py +22 -0
  74. provide/foundation/logger/config/__init__.py +16 -0
  75. provide/foundation/logger/config/base.py +40 -0
  76. provide/foundation/logger/config/logging.py +394 -0
  77. provide/foundation/logger/config/telemetry.py +188 -0
  78. provide/foundation/logger/core.py +239 -0
  79. provide/foundation/logger/custom_processors.py +172 -0
  80. provide/foundation/logger/emoji/__init__.py +44 -0
  81. provide/foundation/logger/emoji/matrix.py +209 -0
  82. provide/foundation/logger/emoji/sets.py +458 -0
  83. provide/foundation/logger/emoji/types.py +56 -0
  84. provide/foundation/logger/factories.py +56 -0
  85. provide/foundation/logger/processors/__init__.py +13 -0
  86. provide/foundation/logger/processors/main.py +254 -0
  87. provide/foundation/logger/processors/trace.py +113 -0
  88. provide/foundation/logger/ratelimit/__init__.py +31 -0
  89. provide/foundation/logger/ratelimit/limiters.py +294 -0
  90. provide/foundation/logger/ratelimit/processor.py +203 -0
  91. provide/foundation/logger/ratelimit/queue_limiter.py +305 -0
  92. provide/foundation/logger/setup/__init__.py +29 -0
  93. provide/foundation/logger/setup/coordinator.py +138 -0
  94. provide/foundation/logger/setup/emoji_resolver.py +64 -0
  95. provide/foundation/logger/setup/processors.py +85 -0
  96. provide/foundation/logger/setup/testing.py +39 -0
  97. provide/foundation/logger/trace.py +38 -0
  98. provide/foundation/metrics/__init__.py +119 -0
  99. provide/foundation/metrics/otel.py +122 -0
  100. provide/foundation/metrics/simple.py +165 -0
  101. provide/foundation/observability/__init__.py +53 -0
  102. provide/foundation/observability/openobserve/__init__.py +79 -0
  103. provide/foundation/observability/openobserve/auth.py +72 -0
  104. provide/foundation/observability/openobserve/client.py +307 -0
  105. provide/foundation/observability/openobserve/commands.py +357 -0
  106. provide/foundation/observability/openobserve/exceptions.py +41 -0
  107. provide/foundation/observability/openobserve/formatters.py +298 -0
  108. provide/foundation/observability/openobserve/models.py +134 -0
  109. provide/foundation/observability/openobserve/otlp.py +320 -0
  110. provide/foundation/observability/openobserve/search.py +222 -0
  111. provide/foundation/observability/openobserve/streaming.py +235 -0
  112. provide/foundation/platform/__init__.py +44 -0
  113. provide/foundation/platform/detection.py +193 -0
  114. provide/foundation/platform/info.py +157 -0
  115. provide/foundation/process/__init__.py +39 -0
  116. provide/foundation/process/async_runner.py +373 -0
  117. provide/foundation/process/lifecycle.py +406 -0
  118. provide/foundation/process/runner.py +390 -0
  119. provide/foundation/setup/__init__.py +101 -0
  120. provide/foundation/streams/__init__.py +44 -0
  121. provide/foundation/streams/console.py +57 -0
  122. provide/foundation/streams/core.py +65 -0
  123. provide/foundation/streams/file.py +104 -0
  124. provide/foundation/testing/__init__.py +166 -0
  125. provide/foundation/testing/cli.py +227 -0
  126. provide/foundation/testing/crypto.py +163 -0
  127. provide/foundation/testing/fixtures.py +49 -0
  128. provide/foundation/testing/hub.py +23 -0
  129. provide/foundation/testing/logger.py +106 -0
  130. provide/foundation/testing/streams.py +54 -0
  131. provide/foundation/tracer/__init__.py +49 -0
  132. provide/foundation/tracer/context.py +115 -0
  133. provide/foundation/tracer/otel.py +135 -0
  134. provide/foundation/tracer/spans.py +174 -0
  135. provide/foundation/types.py +32 -0
  136. provide/foundation/utils/__init__.py +97 -0
  137. provide/foundation/utils/deps.py +195 -0
  138. provide/foundation/utils/env.py +491 -0
  139. provide/foundation/utils/formatting.py +483 -0
  140. provide/foundation/utils/parsing.py +235 -0
  141. provide/foundation/utils/rate_limiting.py +112 -0
  142. provide/foundation/utils/streams.py +67 -0
  143. provide/foundation/utils/timing.py +93 -0
  144. provide_foundation-0.0.0.dev0.dist-info/METADATA +469 -0
  145. provide_foundation-0.0.0.dev0.dist-info/RECORD +149 -0
  146. provide_foundation-0.0.0.dev0.dist-info/WHEEL +5 -0
  147. provide_foundation-0.0.0.dev0.dist-info/entry_points.txt +2 -0
  148. provide_foundation-0.0.0.dev0.dist-info/licenses/LICENSE +201 -0
  149. provide_foundation-0.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,310 @@
1
+ """Click command and group building functions."""
2
+
3
+ import inspect
4
+ from typing import Any
5
+
6
+ import click
7
+
8
+ from provide.foundation.hub.info import CommandInfo
9
+ from provide.foundation.hub.registry import Registry, get_command_registry
10
+ from provide.foundation.hub.type_mapping import extract_click_type
11
+ from provide.foundation.logger import get_logger
12
+
13
+ log = get_logger(__name__)
14
+
15
+
16
+ def ensure_parent_groups(parent_path: str, registry: Registry) -> None:
17
+ """Ensure all parent groups in the path exist, creating them if needed."""
18
+ parts = parent_path.split(".")
19
+
20
+ # Build up the path progressively
21
+ for i in range(len(parts)):
22
+ group_path = ".".join(parts[: i + 1])
23
+ registry_key = group_path
24
+
25
+ # Check if this group already exists
26
+ if not registry.get_entry(registry_key, dimension="command"):
27
+ # Create a placeholder group
28
+ def group_func() -> None:
29
+ """Auto-generated command group."""
30
+ pass
31
+
32
+ # Set the function name for better debugging
33
+ group_func.__name__ = f"{parts[i]}_group"
34
+
35
+ # Register the group
36
+ parent = ".".join(parts[:i]) if i > 0 else None
37
+
38
+ info = CommandInfo(
39
+ name=parts[i],
40
+ func=group_func,
41
+ description=f"{parts[i].capitalize()} commands",
42
+ metadata={"is_group": True, "auto_created": True},
43
+ parent=parent,
44
+ )
45
+
46
+ registry.register(
47
+ name=registry_key,
48
+ value=group_func,
49
+ dimension="command",
50
+ metadata={
51
+ "info": info,
52
+ "description": info.description,
53
+ "parent": parent,
54
+ "is_group": True,
55
+ "auto_created": True,
56
+ },
57
+ )
58
+
59
+ log.debug(f"Auto-created group: {group_path}")
60
+
61
+
62
+ def build_click_command(
63
+ name: str,
64
+ registry: Registry | None = None,
65
+ ) -> click.Command | None:
66
+ """
67
+ Build a Click command from a registered function.
68
+
69
+ This function takes a registered command and converts it to a
70
+ Click command with proper options and arguments based on the
71
+ function signature.
72
+
73
+ Args:
74
+ name: Command name in registry
75
+ registry: Custom registry (defaults to global)
76
+
77
+ Returns:
78
+ Click Command or None if not found
79
+
80
+ Example:
81
+ >>> @register_command("greet")
82
+ >>> def greet(name: str = "World"):
83
+ >>> print(f"Hello, {name}!")
84
+ >>>
85
+ >>> click_cmd = build_click_command("greet")
86
+ >>> # Now click_cmd can be added to a Click group
87
+ """
88
+ reg = registry or get_command_registry()
89
+ entry = reg.get_entry(name, dimension="command")
90
+
91
+ if not entry:
92
+ return None
93
+
94
+ info = entry.metadata.get("info")
95
+ if not info:
96
+ return None
97
+
98
+ # If it's already a Click command, return it
99
+ if info.click_command:
100
+ return info.click_command
101
+
102
+ func = info.func
103
+ if not callable(func):
104
+ return None
105
+
106
+ # Build Click command from function signature
107
+ sig = inspect.signature(func)
108
+
109
+ # Process parameters - separate arguments and options
110
+ params = list(sig.parameters.items())
111
+ arguments = []
112
+ options = []
113
+
114
+ for param_name, param in params:
115
+ if param_name in ("self", "cls", "ctx"):
116
+ continue
117
+
118
+ has_default = param.default != inspect.Parameter.empty
119
+ if has_default:
120
+ options.append((param_name, param))
121
+ else:
122
+ arguments.append((param_name, param))
123
+
124
+ # Start with the base function
125
+ decorated_func = func
126
+
127
+ # Process options in reverse order (for decorator stacking)
128
+ for param_name, param in reversed(options):
129
+ # Create option
130
+ option_name = f"--{param_name.replace('_', '-')}"
131
+ if param.annotation != inspect.Parameter.empty:
132
+ # Extract the actual type from unions/optionals
133
+ param_type = extract_click_type(param.annotation)
134
+
135
+ # Use type annotation
136
+ if param_type == bool:
137
+ decorated_func = click.option(
138
+ option_name,
139
+ is_flag=True,
140
+ default=param.default,
141
+ help=f"{param_name} flag",
142
+ )(decorated_func)
143
+ else:
144
+ decorated_func = click.option(
145
+ option_name,
146
+ type=param_type,
147
+ default=param.default,
148
+ help=f"{param_name} option",
149
+ )(decorated_func)
150
+ else:
151
+ decorated_func = click.option(
152
+ option_name,
153
+ default=param.default,
154
+ help=f"{param_name} option",
155
+ )(decorated_func)
156
+
157
+ # Process arguments in reverse order
158
+ # When we apply decorators programmatically, the last one applied
159
+ # becomes the outermost decorator, which Click sees first
160
+ for param_name, param in reversed(arguments):
161
+ # Create argument
162
+ if param.annotation != inspect.Parameter.empty:
163
+ # Extract the actual type from unions/optionals
164
+ param_type = extract_click_type(param.annotation)
165
+ decorated_func = click.argument(
166
+ param_name,
167
+ type=param_type,
168
+ )(decorated_func)
169
+ else:
170
+ decorated_func = click.argument(param_name)(decorated_func)
171
+
172
+ # Create the Click command with the decorated function
173
+ cmd = click.Command(
174
+ name=info.name,
175
+ callback=decorated_func,
176
+ help=info.description,
177
+ hidden=info.hidden,
178
+ )
179
+
180
+ # Copy over the params from the decorated function (Click stores them there)
181
+ # Note: Click params are in reverse order of decoration, but for the Command
182
+ # we need them in the correct positional order
183
+ if hasattr(decorated_func, "__click_params__"):
184
+ cmd.params = list(reversed(decorated_func.__click_params__))
185
+
186
+ return cmd
187
+
188
+
189
+ def create_command_group(
190
+ name: str = "cli",
191
+ commands: list[str] | None = None,
192
+ registry: Registry | None = None,
193
+ **kwargs: Any,
194
+ ) -> click.Group:
195
+ """
196
+ Create a Click group with registered commands.
197
+
198
+ Args:
199
+ name: Name for the CLI group
200
+ commands: List of command names to include (None = all)
201
+ registry: Custom registry (defaults to global)
202
+ **kwargs: Additional Click Group options
203
+
204
+ Returns:
205
+ Click Group with registered commands
206
+
207
+ Example:
208
+ >>> # Register some commands
209
+ >>> @register_command("init")
210
+ >>> def init_cmd():
211
+ >>> pass
212
+ >>>
213
+ >>> # Create CLI group
214
+ >>> cli = create_command_group("myapp")
215
+ >>>
216
+ >>> # Run the CLI
217
+ >>> if __name__ == "__main__":
218
+ >>> cli()
219
+ """
220
+ reg = registry or get_command_registry()
221
+ group = click.Group(name=name, **kwargs)
222
+
223
+ # Build nested command structure
224
+ groups: dict[str, click.Group] = {}
225
+
226
+ # Get commands to include
227
+ if commands is None:
228
+ commands = reg.list_dimension("command")
229
+
230
+ # Sort commands to ensure parents are created before children
231
+ sorted_commands = sorted(commands, key=lambda x: x.count("."))
232
+
233
+ # First pass: create all groups
234
+ for cmd_name in sorted_commands:
235
+ entry = reg.get_entry(cmd_name, dimension="command")
236
+ if not entry:
237
+ continue
238
+
239
+ info = entry.metadata.get("info")
240
+ if not info:
241
+ continue
242
+
243
+ # Check if this is a group
244
+ if entry.metadata.get("is_group"):
245
+ parent = entry.metadata.get("parent")
246
+ # Extract the actual group name (without parent prefix)
247
+ actual_name = cmd_name.split(".")[-1] if parent else cmd_name
248
+
249
+ subgroup = click.Group(
250
+ name=actual_name,
251
+ help=info.description,
252
+ hidden=info.hidden,
253
+ )
254
+ groups[cmd_name] = subgroup
255
+
256
+ # Add to parent or root
257
+ if parent:
258
+ # Handle multi-level parents with dot notation
259
+ parent_key = parent
260
+ if parent_key in groups:
261
+ groups[parent_key].add_command(subgroup)
262
+ else:
263
+ # Parent should have been created, add to root as fallback
264
+ group.add_command(subgroup)
265
+ else:
266
+ group.add_command(subgroup)
267
+
268
+ # Second pass: add commands to groups
269
+ for cmd_name in sorted_commands:
270
+ entry = reg.get_entry(cmd_name, dimension="command")
271
+ if not entry:
272
+ continue
273
+
274
+ info = entry.metadata.get("info")
275
+ if not info or info.hidden or entry.metadata.get("is_group"):
276
+ continue
277
+
278
+ # Build Click command
279
+ click_cmd = build_click_command(cmd_name, registry=reg)
280
+ if click_cmd:
281
+ parent = entry.metadata.get("parent")
282
+
283
+ # Update command name if it has a parent
284
+ if parent:
285
+ # Extract the actual command name (without parent prefix)
286
+ parts = cmd_name.split(".")
287
+ parent_parts = parent.split(".")
288
+ # Remove parent parts from command name
289
+ cmd_parts = parts[len(parent_parts) :]
290
+ click_cmd.name = cmd_parts[0] if cmd_parts else parts[-1]
291
+
292
+ # Add to parent group or root
293
+ if parent:
294
+ parent_key = parent
295
+ if parent_key in groups:
296
+ groups[parent_key].add_command(click_cmd)
297
+ else:
298
+ # Parent not found, add to root
299
+ group.add_command(click_cmd)
300
+ else:
301
+ group.add_command(click_cmd)
302
+
303
+ return group
304
+
305
+
306
+ __all__ = [
307
+ "build_click_command",
308
+ "create_command_group",
309
+ "ensure_parent_groups",
310
+ ]
@@ -0,0 +1,42 @@
1
+ """
2
+ Command registration and management for the hub.
3
+
4
+ This module now re-exports from the split modules for backward compatibility.
5
+ """
6
+
7
+ # Core hub features (always available)
8
+ from provide.foundation.hub.decorators import register_command
9
+ from provide.foundation.hub.info import CommandInfo
10
+ from provide.foundation.hub.registry import get_command_registry
11
+
12
+
13
+ # CLI features (require click) - lazy loaded
14
+ def __getattr__(name: str):
15
+ """Support lazy loading of CLI-dependent features."""
16
+ if name in ("build_click_command", "create_command_group"):
17
+ try:
18
+ from provide.foundation.hub.click_builder import (
19
+ build_click_command,
20
+ create_command_group,
21
+ )
22
+
23
+ if name == "build_click_command":
24
+ return build_click_command
25
+ elif name == "create_command_group":
26
+ return create_command_group
27
+ except ImportError as e:
28
+ if "click" in str(e):
29
+ raise ImportError(
30
+ f"CLI feature '{name}' requires: pip install 'provide-foundation[cli]'"
31
+ ) from e
32
+ raise
33
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
34
+
35
+
36
+ __all__ = [
37
+ "CommandInfo",
38
+ "build_click_command",
39
+ "create_command_group",
40
+ "get_command_registry",
41
+ "register_command",
42
+ ]