python-quantumflow 2.0.0__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.
quantumflow/core.py ADDED
@@ -0,0 +1,368 @@
1
+ """
2
+ Python QuantumFlow Core Module
3
+
4
+ This module provides the core functionality of Python QuantumFlow,
5
+ including type conversion and flow control.
6
+ """
7
+
8
+ import functools
9
+ import logging
10
+ import time
11
+ import random
12
+ import asyncio
13
+ import atexit
14
+ import sys
15
+ import inspect
16
+ from typing import Any, Callable, Type, TypeVar, Union, get_type_hints
17
+
18
+ # ANSI color codes for terminal output
19
+ RESET = "\033[0m"
20
+ BOLD = "\033[1m"
21
+ MAGENTA = "\033[35m"
22
+ BLUE = "\033[34m"
23
+ CYAN = "\033[36m"
24
+
25
+ # Signature string
26
+ SIGNATURE = f"\n{BOLD}{MAGENTA}────────────────────────────────────────{RESET}\n{CYAN}Created with Python QuantumFlow by Magi Sharma{RESET}"
27
+
28
+ from .config import config, EffectType, success, error, warning, info, highlight, code
29
+
30
+ T = TypeVar('T')
31
+
32
+ def flow(target_type: Type[T]) -> Callable[[Any], T]:
33
+ """
34
+ Create a flow function that converts the input to the specified target type.
35
+
36
+ Args:
37
+ target_type: The type to convert to.
38
+
39
+ Returns:
40
+ A function that takes an input and returns it converted to the target type.
41
+ """
42
+ def converter(value: Any) -> T:
43
+ # Apply colorization if enabled
44
+ if config.get_effect(EffectType.COLORIZE):
45
+ print(info(f"Converting {highlight(str(type(value).__name__))} to {highlight(str(target_type.__name__))}"))
46
+
47
+ # Apply conversion logic
48
+ try:
49
+ # Simple case: already the correct type
50
+ if isinstance(value, target_type):
51
+ return value
52
+
53
+ # Try to convert using the target type constructor
54
+ result = target_type(value)
55
+
56
+ # Show success message if colorize is enabled
57
+ if config.get_effect(EffectType.COLORIZE):
58
+ print(success(f"Successfully converted to {highlight(str(target_type.__name__))}"))
59
+
60
+ return result
61
+ except Exception as e:
62
+ # Apply error handling if enabled
63
+ if config.get_effect(EffectType.ERROR_HANDLING):
64
+ print(error(f"Conversion error: {str(e)}"))
65
+ # Could implement retry logic or fallback here
66
+ raise
67
+
68
+ return converter
69
+
70
+ def qflow(func: Callable) -> Callable:
71
+ """
72
+ Decorator that applies Python QuantumFlow effects to a function.
73
+
74
+ This decorator enables automatic type conversion based on type hints,
75
+ error handling, and colorized output.
76
+
77
+ Args:
78
+ func: The function to decorate.
79
+
80
+ Returns:
81
+ The decorated function.
82
+ """
83
+ @functools.wraps(func)
84
+ def wrapper(*args, **kwargs):
85
+ # Apply auto-conversion effect if enabled
86
+ if config.get_effect(EffectType.AUTO_CONVERSION):
87
+ # Get type hints for the function
88
+ hints = get_type_hints(func)
89
+
90
+ # Get the parameter names
91
+ sig = inspect.signature(func)
92
+ param_names = list(sig.parameters.keys())
93
+
94
+ # Convert positional arguments based on type hints
95
+ converted_args = list(args)
96
+ for i, arg in enumerate(args):
97
+ if i < len(param_names):
98
+ param_name = param_names[i]
99
+ if param_name in hints:
100
+ target_type = hints[param_name]
101
+ # Apply the flow conversion
102
+ try:
103
+ converted_args[i] = flow(target_type)(arg)
104
+ except:
105
+ # If conversion fails, keep original value
106
+ pass
107
+
108
+ # Convert keyword arguments based on type hints
109
+ converted_kwargs = {}
110
+ for key, value in kwargs.items():
111
+ if key in hints:
112
+ target_type = hints[key]
113
+ try:
114
+ converted_kwargs[key] = flow(target_type)(value)
115
+ except:
116
+ # If conversion fails, keep original value
117
+ converted_kwargs[key] = value
118
+ else:
119
+ converted_kwargs[key] = value
120
+
121
+ # Call the function with converted arguments
122
+ result = func(*converted_args, **converted_kwargs)
123
+ else:
124
+ # If auto-conversion is disabled, just call the function normally
125
+ result = func(*args, **kwargs)
126
+
127
+ # Show signature if colorize effect is enabled
128
+ if config.get_effect(EffectType.COLORIZE):
129
+ print(SIGNATURE)
130
+
131
+ return result
132
+
133
+ return wrapper
134
+
135
+ def with_typeflow():
136
+ """
137
+ Context manager for QuantumFlow operations.
138
+
139
+ This enables automatic type conversion within the context block.
140
+
141
+ Returns:
142
+ A context manager object.
143
+ """
144
+ class TypeFlowContext:
145
+ def __enter__(self):
146
+ # Enable all effects when entering the context
147
+ for effect in EffectType:
148
+ config.set_effect(effect, True)
149
+ return self
150
+
151
+ def __exit__(self, exc_type, exc_val, exc_tb):
152
+ # Keep effects enabled when exiting
153
+ return False
154
+
155
+ return TypeFlowContext()
156
+
157
+ def async_flow(func):
158
+ """
159
+ Decorator for async functions to make them part of a flow.
160
+ """
161
+ @functools.wraps(func)
162
+ async def wrapper(*args, **kwargs):
163
+ return await func(*args, **kwargs)
164
+ return wrapper
165
+
166
+ def retry(max_attempts=3, backoff_factor=0.5, exceptions=(Exception,)):
167
+ """
168
+ Decorator to retry a function if it raises specified exceptions.
169
+
170
+ Args:
171
+ max_attempts: Maximum number of retry attempts
172
+ backoff_factor: Exponential backoff factor
173
+ exceptions: Tuple of exceptions to catch and retry
174
+ """
175
+ def decorator(func):
176
+ @functools.wraps(func)
177
+ async def async_wrapper(*args, **kwargs):
178
+ last_exception = None
179
+ for attempt in range(max_attempts):
180
+ try:
181
+ return await func(*args, **kwargs)
182
+ except exceptions as e:
183
+ last_exception = e
184
+ if attempt < max_attempts - 1:
185
+ sleep_time = backoff_factor * (2 ** attempt) + random.uniform(0, 0.1)
186
+ await asyncio.sleep(sleep_time)
187
+ else:
188
+ raise last_exception
189
+
190
+ @functools.wraps(func)
191
+ def sync_wrapper(*args, **kwargs):
192
+ last_exception = None
193
+ for attempt in range(max_attempts):
194
+ try:
195
+ return func(*args, **kwargs)
196
+ except exceptions as e:
197
+ last_exception = e
198
+ if attempt < max_attempts - 1:
199
+ sleep_time = backoff_factor * (2 ** attempt) + random.uniform(0, 0.1)
200
+ time.sleep(sleep_time)
201
+ else:
202
+ raise last_exception
203
+
204
+ # Choose the appropriate wrapper based on whether the function is async
205
+ if inspect.iscoroutinefunction(func):
206
+ return async_wrapper
207
+ return sync_wrapper
208
+
209
+ return decorator
210
+
211
+ def configure_logging(level="INFO", fancy=False):
212
+ """Configure basic logging."""
213
+ numeric_level = getattr(logging, level.upper(), None)
214
+ if not isinstance(numeric_level, int):
215
+ raise ValueError(f"Invalid log level: {level}")
216
+ logging.basicConfig(level=numeric_level)
217
+
218
+ def fancy_print(message, style=None, end="\n"):
219
+ """Print with styling using ANSI color codes for terminal output.
220
+
221
+ If style is not provided, will attempt to apply default styling based on message content.
222
+ """
223
+ # Define ANSI color codes
224
+ COLORS = {
225
+ "black": "\033[30m",
226
+ "red": "\033[31m",
227
+ "green": "\033[32m",
228
+ "yellow": "\033[33m",
229
+ "blue": "\033[34m",
230
+ "magenta": "\033[35m",
231
+ "cyan": "\033[36m",
232
+ "white": "\033[37m",
233
+ "bold": "\033[1m",
234
+ "dim": "\033[2m",
235
+ "italic": "\033[3m",
236
+ "underline": "\033[4m",
237
+ "reset": "\033[0m"
238
+ }
239
+
240
+ # Auto-detect style if none provided
241
+ if style is None:
242
+ message_lower = message.lower()
243
+
244
+ # Apply default styling based on message content
245
+ if any(kw in message_lower for kw in ["error", "failed", "exception", "crash"]):
246
+ style = "bold red"
247
+ elif any(kw in message_lower for kw in ["warning", "caution", "attention"]):
248
+ style = "bold yellow"
249
+ elif any(kw in message_lower for kw in ["success", "completed", "done", "finished"]):
250
+ style = "bold green"
251
+ elif any(kw in message_lower for kw in ["info", "note", "notice"]):
252
+ style = "cyan"
253
+ elif any(kw in message_lower for kw in ["debug", "trace"]):
254
+ style = "dim"
255
+ elif any(kw in message_lower for kw in ["important", "critical"]):
256
+ style = "bold magenta"
257
+ elif message.isupper(): # ALL CAPS text gets bold styling
258
+ style = "bold"
259
+ elif message.startswith(("=>", "->", ">>", "-->")): # Arrow indicators
260
+ style = "bold blue"
261
+ elif message.startswith(("# ", "## ", "### ")): # Heading-like text
262
+ heading_level = message.count('#')
263
+ if heading_level == 1:
264
+ style = "bold magenta"
265
+ elif heading_level == 2:
266
+ style = "bold blue"
267
+ else:
268
+ style = "bold cyan"
269
+ else:
270
+ # Default effects for normal text - subtle gradient effect across the text
271
+ return _gradient_print(message, end=end)
272
+
273
+ if style:
274
+ # Parse multiple styles (e.g., "bold red")
275
+ style_parts = style.split()
276
+ codes = ""
277
+ for part in style_parts:
278
+ if part in COLORS:
279
+ codes += COLORS[part]
280
+
281
+ # Apply style and reset after the message
282
+ print(f"{codes}{message}{COLORS['reset']}", end=end)
283
+ else:
284
+ print(message, end=end)
285
+
286
+ def _gradient_print(text, start_color=(70, 130, 180), end_color=(138, 43, 226), end="\n"):
287
+ """Print text with a color gradient using ANSI RGB color codes."""
288
+ start_r, start_g, start_b = start_color
289
+ end_r, end_g, end_b = end_color
290
+ result = ""
291
+
292
+ for i, char in enumerate(text):
293
+ # Calculate the gradient position
294
+ ratio = i / (len(text) - 1) if len(text) > 1 else 0
295
+
296
+ # Interpolate between start and end colors
297
+ r = int(start_r + (end_r - start_r) * ratio)
298
+ g = int(start_g + (end_g - start_g) * ratio)
299
+ b = int(start_b + (end_b - start_b) * ratio)
300
+
301
+ # Add the colored character
302
+ result += f"\033[38;2;{r};{g};{b}m{char}"
303
+
304
+ # Reset color at the end
305
+ print(f"{result}\033[0m", end=end)
306
+ return True
307
+
308
+ def print_author_credit(small=True):
309
+ """Print a small credit line with the author's name.
310
+
311
+ This function can be called at the end of scripts to provide attribution.
312
+ """
313
+ if small:
314
+ # Subtle, small attribution
315
+ print("\n\033[38;2;180;180;180m" + "─" * 40 + "\033[0m")
316
+ print("\033[38;2;180;180;180mCreated with QuantumFlow by Magi Sharma\033[0m")
317
+ else:
318
+ # More elaborate attribution with gradient
319
+ author_text = "Created by Magi Sharma"
320
+ framework_text = "QUANTUMFLOW FRAMEWORK"
321
+ version_text = "v0.6.1"
322
+
323
+ print("\n" + "─" * 50)
324
+
325
+ # Print author with gradient
326
+ result = ""
327
+ start_r, start_g, start_b = 255, 215, 0 # Gold
328
+ end_r, end_g, end_b = 255, 140, 0 # Dark orange
329
+
330
+ for i, char in enumerate(author_text):
331
+ ratio = i / (len(author_text) - 1) if len(author_text) > 1 else 0
332
+ r = int(start_r + (end_r - start_r) * ratio)
333
+ g = int(start_g + (end_g - start_g) * ratio)
334
+ b = int(start_b + (end_b - start_b) * ratio)
335
+ result += f"\033[38;2;{r};{g};{b}m{char}"
336
+
337
+ print(f"{result}\033[0m")
338
+
339
+ # Print framework name with different gradient
340
+ result = ""
341
+ start_r, start_g, start_b = 0, 191, 255 # Deep sky blue
342
+ end_r, end_g, end_b = 138, 43, 226 # Purple
343
+
344
+ for i, char in enumerate(framework_text):
345
+ ratio = i / (len(framework_text) - 1) if len(framework_text) > 1 else 0
346
+ r = int(start_r + (end_r - start_r) * ratio)
347
+ g = int(start_g + (end_g - start_g) * ratio)
348
+ b = int(start_b + (end_b - start_b) * ratio)
349
+ result += f"\033[38;2;{r};{g};{b}m{char}"
350
+
351
+ print(f"{result} \033[38;2;150;150;150m{version_text}\033[0m")
352
+ print("─" * 50)
353
+
354
+ def _show_credits_on_exit():
355
+ """Show small author credits when the program exits."""
356
+ # Only show on regular exits, not errors or interrupts
357
+ if sys.exc_info()[0] is None:
358
+ print_author_credit(small=True)
359
+
360
+ # Register the exit handler
361
+ atexit.register(_show_credits_on_exit)
362
+
363
+ def create_progress(description=None):
364
+ """Create a simple progress indicator."""
365
+ return None # Fallback to simple printing
366
+
367
+ # Add console object for compatibility
368
+ console = None
@@ -0,0 +1,116 @@
1
+ """
2
+ Execution backends for QuantumFlow.
3
+ """
4
+
5
+ import asyncio
6
+ import concurrent.futures
7
+ import logging
8
+ import multiprocessing
9
+ from typing import Any, Callable, Dict, List, Optional, Union
10
+
11
+ logger = logging.getLogger("quantumflow")
12
+
13
+ class Executor:
14
+ """Base class for flow executors."""
15
+
16
+ def execute(self, flow, *args, **kwargs):
17
+ """Execute a flow."""
18
+ raise NotImplementedError("Subclasses must implement this method")
19
+
20
+ class SyncExecutor(Executor):
21
+ """Executor that runs flows synchronously."""
22
+
23
+ def execute(self, flow, *args, **kwargs):
24
+ """Execute a flow synchronously."""
25
+ return flow(*args, **kwargs)
26
+
27
+ class AsyncExecutor(Executor):
28
+ """Executor that runs flows asynchronously."""
29
+
30
+ async def execute(self, flow, *args, **kwargs):
31
+ """Execute a flow asynchronously."""
32
+ if asyncio.iscoroutinefunction(flow):
33
+ return await flow(*args, **kwargs)
34
+ else:
35
+ loop = asyncio.get_event_loop()
36
+ return await loop.run_in_executor(None, lambda: flow(*args, **kwargs))
37
+
38
+ class ThreadPoolExecutor(Executor):
39
+ """Executor that runs flows in a thread pool."""
40
+
41
+ def __init__(self, max_workers: Optional[int] = None):
42
+ self.max_workers = max_workers
43
+
44
+ def execute(self, flow, *args, **kwargs):
45
+ """Execute a flow in a thread pool."""
46
+ with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
47
+ future = executor.submit(flow, *args, **kwargs)
48
+ return future.result()
49
+
50
+ class ProcessPoolExecutor(Executor):
51
+ """Executor that runs flows in a process pool."""
52
+
53
+ def __init__(self, max_workers: Optional[int] = None):
54
+ self.max_workers = max_workers
55
+
56
+ def execute(self, flow, *args, **kwargs):
57
+ """Execute a flow in a process pool."""
58
+ with concurrent.futures.ProcessPoolExecutor(max_workers=self.max_workers) as executor:
59
+ future = executor.submit(flow, *args, **kwargs)
60
+ return future.result()
61
+
62
+ class DaskExecutor(Executor):
63
+ """Executor that runs flows using Dask."""
64
+
65
+ def __init__(self, address: Optional[str] = None):
66
+ self.address = address
67
+ self._client = None
68
+
69
+ def _get_client(self):
70
+ """Get or create a Dask client."""
71
+ if self._client is None:
72
+ try:
73
+ from dask.distributed import Client
74
+ self._client = Client(self.address)
75
+ except ImportError:
76
+ raise ImportError("Dask not installed. Install with 'pip install dask[distributed]'")
77
+ return self._client
78
+
79
+ def execute(self, flow, *args, **kwargs):
80
+ """Execute a flow using Dask."""
81
+ client = self._get_client()
82
+ future = client.submit(flow, *args, **kwargs)
83
+ return future.result()
84
+
85
+ class RayExecutor(Executor):
86
+ """Executor that runs flows using Ray."""
87
+
88
+ def __init__(self, address: Optional[str] = None):
89
+ self.address = address
90
+ self._initialized = False
91
+
92
+ def _initialize(self):
93
+ """Initialize Ray."""
94
+ if not self._initialized:
95
+ try:
96
+ import ray
97
+ if self.address:
98
+ ray.init(address=self.address)
99
+ else:
100
+ ray.init()
101
+ self._initialized = True
102
+ except ImportError:
103
+ raise ImportError("Ray not installed. Install with 'pip install ray'")
104
+
105
+ def execute(self, flow, *args, **kwargs):
106
+ """Execute a flow using Ray."""
107
+ self._initialize()
108
+
109
+ import ray
110
+
111
+ @ray.remote
112
+ def ray_wrapper(f, *args, **kwargs):
113
+ return f(*args, **kwargs)
114
+
115
+ future = ray_wrapper.remote(flow, *args, **kwargs)
116
+ return ray.get(future)
quantumflow/metrics.py ADDED
@@ -0,0 +1,168 @@
1
+ """
2
+ Metrics and observability for QuantumFlow.
3
+ """
4
+
5
+ import asyncio
6
+ import logging
7
+ import threading
8
+ import time
9
+ from typing import Any, Dict, List, Optional, Union
10
+
11
+ logger = logging.getLogger("quantumflow")
12
+
13
+ class Metric:
14
+ """Base class for metrics."""
15
+
16
+ def __init__(self, name: str, description: str):
17
+ self.name = name
18
+ self.description = description
19
+
20
+ def get_value(self) -> Any:
21
+ """Get the current value of the metric."""
22
+ raise NotImplementedError("Subclasses must implement this method")
23
+
24
+ class Counter(Metric):
25
+ """Counter metric that only increases."""
26
+
27
+ def __init__(self, name: str, description: str):
28
+ super().__init__(name, description)
29
+ self._value = 0
30
+
31
+ def inc(self, value: int = 1):
32
+ """Increment the counter."""
33
+ if value < 0:
34
+ raise ValueError("Counter can only be incremented by non-negative values")
35
+ self._value += value
36
+
37
+ def get_value(self) -> int:
38
+ """Get the current value of the counter."""
39
+ return self._value
40
+
41
+ class Gauge(Metric):
42
+ """Gauge metric that can go up and down."""
43
+
44
+ def __init__(self, name: str, description: str):
45
+ super().__init__(name, description)
46
+ self._value = 0
47
+
48
+ def set(self, value: float):
49
+ """Set the gauge value."""
50
+ self._value = value
51
+
52
+ def inc(self, value: float = 1):
53
+ """Increment the gauge."""
54
+ self._value += value
55
+
56
+ def dec(self, value: float = 1):
57
+ """Decrement the gauge."""
58
+ self._value -= value
59
+
60
+ def get_value(self) -> float:
61
+ """Get the current value of the gauge."""
62
+ return self._value
63
+
64
+ class Histogram(Metric):
65
+ """Histogram metric for measuring distributions."""
66
+
67
+ def __init__(self, name: str, description: str, buckets: List[float] = None):
68
+ super().__init__(name, description)
69
+ self.buckets = buckets or [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
70
+ self._values = []
71
+
72
+ def observe(self, value: float):
73
+ """Observe a value."""
74
+ self._values.append(value)
75
+
76
+ def get_value(self) -> Dict[str, Any]:
77
+ """Get the current value of the histogram."""
78
+ result = {"count": len(self._values), "sum": sum(self._values), "buckets": {}}
79
+
80
+ for bucket in self.buckets:
81
+ result["buckets"][bucket] = sum(1 for v in self._values if v <= bucket)
82
+
83
+ return result
84
+
85
+ # Global registry for metrics
86
+ _metrics_registry = {}
87
+
88
+ def counter(name: str, description: str) -> Counter:
89
+ """Create or get a counter metric."""
90
+ if name not in _metrics_registry:
91
+ _metrics_registry[name] = Counter(name, description)
92
+ return _metrics_registry[name]
93
+
94
+ def gauge(name: str, description: str) -> Gauge:
95
+ """Create or get a gauge metric."""
96
+ if name not in _metrics_registry:
97
+ _metrics_registry[name] = Gauge(name, description)
98
+ return _metrics_registry[name]
99
+
100
+ def histogram(name: str, description: str, buckets: List[float] = None) -> Histogram:
101
+ """Create or get a histogram metric."""
102
+ if name not in _metrics_registry:
103
+ _metrics_registry[name] = Histogram(name, description, buckets)
104
+ return _metrics_registry[name]
105
+
106
+ class MetricsServer:
107
+ """Server for exposing metrics."""
108
+
109
+ def __init__(self, host: str = "localhost", port: int = 8000):
110
+ self.host = host
111
+ self.port = port
112
+ self._server = None
113
+ self._running = False
114
+
115
+ async def start(self):
116
+ """Start the metrics server."""
117
+ from aiohttp import web
118
+
119
+ async def metrics_handler(request):
120
+ """Handle metrics requests."""
121
+ output = []
122
+
123
+ for name, metric in _metrics_registry.items():
124
+ output.append(f"# HELP {name} {metric.description}")
125
+
126
+ if isinstance(metric, Counter):
127
+ output.append(f"# TYPE {name} counter")
128
+ output.append(f"{name} {metric.get_value()}")
129
+ elif isinstance(metric, Gauge):
130
+ output.append(f"# TYPE {name} gauge")
131
+ output.append(f"{name} {metric.get_value()}")
132
+ elif isinstance(metric, Histogram):
133
+ output.append(f"# TYPE {name} histogram")
134
+ value = metric.get_value()
135
+
136
+ for bucket, count in value["buckets"].items():
137
+ output.append(f"{name}_bucket{{le=\"{bucket}\"}} {count}")
138
+
139
+ output.append(f"{name}_count {value['count']}")
140
+ output.append(f"{name}_sum {value['sum']}")
141
+
142
+ return web.Response(text="\n".join(output))
143
+
144
+ app = web.Application()
145
+ app.router.add_get("/metrics", metrics_handler)
146
+
147
+ runner = web.AppRunner(app)
148
+ await runner.setup()
149
+ site = web.TCPSite(runner, self.host, self.port)
150
+ await site.start()
151
+
152
+ self._server = runner
153
+ self._running = True
154
+
155
+ logger.info(f"Metrics server started at http://{self.host}:{self.port}/metrics")
156
+ return self
157
+
158
+ async def stop(self):
159
+ """Stop the metrics server."""
160
+ if self._server and self._running:
161
+ await self._server.cleanup()
162
+ self._running = False
163
+ logger.info("Metrics server stopped")
164
+
165
+ async def serve_metrics(host: str = "localhost", port: int = 8000) -> MetricsServer:
166
+ """Start a metrics server."""
167
+ server = MetricsServer(host, port)
168
+ return await server.start()