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.
- python_quantumflow-2.0.0.dist-info/METADATA +399 -0
- python_quantumflow-2.0.0.dist-info/RECORD +11 -0
- python_quantumflow-2.0.0.dist-info/WHEEL +5 -0
- python_quantumflow-2.0.0.dist-info/top_level.txt +1 -0
- quantumflow/__init__.py +47 -0
- quantumflow/cli.py +401 -0
- quantumflow/config.py +189 -0
- quantumflow/core.py +368 -0
- quantumflow/execution.py +116 -0
- quantumflow/metrics.py +168 -0
- quantumflow/validation.py +77 -0
quantumflow/cli.py
ADDED
@@ -0,0 +1,401 @@
|
|
1
|
+
"""
|
2
|
+
Command-line interface for QuantumFlow.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
import asyncio
|
7
|
+
import code
|
8
|
+
import importlib
|
9
|
+
import inspect
|
10
|
+
import json
|
11
|
+
import logging
|
12
|
+
import os
|
13
|
+
import sys
|
14
|
+
from typing import Any, Dict, List, Optional
|
15
|
+
|
16
|
+
from . import __version__, flow, TypeFlowContext, qflow, visualize_flow, fancy_print, is_fancy_output_available
|
17
|
+
from .core import configure_logging
|
18
|
+
|
19
|
+
logger = logging.getLogger("quantumflow")
|
20
|
+
|
21
|
+
class QuantumPlayground:
|
22
|
+
"""Interactive REPL for experimenting with QuantumFlow."""
|
23
|
+
|
24
|
+
def __init__(self):
|
25
|
+
self.context = {}
|
26
|
+
self.history = []
|
27
|
+
|
28
|
+
def start(self):
|
29
|
+
"""Start the playground."""
|
30
|
+
print("Welcome to QuantumFlow Playground!")
|
31
|
+
print("Type 'help' for assistance or 'exit' to quit.")
|
32
|
+
|
33
|
+
# Set up the context with QuantumFlow imports
|
34
|
+
self.context = {
|
35
|
+
"flow": flow,
|
36
|
+
"TypeFlowContext": TypeFlowContext,
|
37
|
+
"qflow": qflow,
|
38
|
+
"visualize_flow": visualize_flow,
|
39
|
+
}
|
40
|
+
|
41
|
+
# Create a console with the context
|
42
|
+
console = code.InteractiveConsole(self.context)
|
43
|
+
console.interact(banner="", exitmsg="Goodbye from QuantumFlow Playground!")
|
44
|
+
|
45
|
+
class FlowTutor:
|
46
|
+
"""Guided wizard for learning QuantumFlow concepts."""
|
47
|
+
|
48
|
+
def __init__(self):
|
49
|
+
self.lessons = [
|
50
|
+
{
|
51
|
+
"name": "Introduction to TypeFlow",
|
52
|
+
"description": "Learn the basics of TypeFlow and how to use flow() and TypeFlowContext.",
|
53
|
+
"steps": [
|
54
|
+
{
|
55
|
+
"instruction": "Let's start by importing the basic components:",
|
56
|
+
"code": "from quantumflow import flow, TypeFlowContext",
|
57
|
+
"expected_output": None
|
58
|
+
},
|
59
|
+
{
|
60
|
+
"instruction": "Now, let's try a simple type conversion:",
|
61
|
+
"code": "with TypeFlowContext():\n result = flow(42) + ' is the answer'\n print(result)",
|
62
|
+
"expected_output": "42 is the answer"
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"instruction": "Without TypeFlowContext, this would raise an error:",
|
66
|
+
"code": "try:\n result = 42 + ' is the answer'\n print(result)\nexcept TypeError as e:\n print(f'Error: {e}')",
|
67
|
+
"expected_output": "Error: unsupported operand type(s) for +: 'int' and 'str'"
|
68
|
+
}
|
69
|
+
]
|
70
|
+
},
|
71
|
+
{
|
72
|
+
"name": "Creating Flows with @qflow",
|
73
|
+
"description": "Learn how to create and visualize flows using the @qflow decorator.",
|
74
|
+
"steps": [
|
75
|
+
{
|
76
|
+
"instruction": "Let's import the necessary components:",
|
77
|
+
"code": "from quantumflow import qflow, visualize_flow",
|
78
|
+
"expected_output": None
|
79
|
+
},
|
80
|
+
{
|
81
|
+
"instruction": "Now, let's define a simple flow:",
|
82
|
+
"code": "@qflow\ndef process_data(data):\n return [item.upper() for item in data]\n\nresult = process_data(['hello', 'world'])\nprint(result)",
|
83
|
+
"expected_output": "['HELLO', 'WORLD']"
|
84
|
+
},
|
85
|
+
{
|
86
|
+
"instruction": "We can visualize the flow:",
|
87
|
+
"code": "# This would show a graph in a GUI window\n# visualize_flow(process_data)\nprint('Flow visualization would appear in a GUI window')",
|
88
|
+
"expected_output": "Flow visualization would appear in a GUI window"
|
89
|
+
}
|
90
|
+
]
|
91
|
+
},
|
92
|
+
{
|
93
|
+
"name": "Advanced Flow Features",
|
94
|
+
"description": "Learn about fancy output, async flows, and retry logic.",
|
95
|
+
"steps": [
|
96
|
+
{
|
97
|
+
"instruction": "Let's explore fancy terminal output:",
|
98
|
+
"code": "from quantumflow import fancy_print, create_progress\nimport time\n\nfancy_print('This is styled text!', style='bold magenta')\nfancy_print('Error messages stand out', style='bold red')\nfancy_print('Success messages too', style='bold green')",
|
99
|
+
"expected_output": None
|
100
|
+
},
|
101
|
+
{
|
102
|
+
"instruction": "Now let's try a progress bar:",
|
103
|
+
"code": "progress = create_progress('Processing')\nif progress:\n with progress:\n task = progress.add_task('Working...', total=10)\n for i in range(10):\n time.sleep(0.2) # Simulate work\n progress.update(task, advance=1)\n fancy_print('Processing complete!', style='green')\nelse:\n print('Rich library not available for fancy progress bars')",
|
104
|
+
"expected_output": None
|
105
|
+
},
|
106
|
+
{
|
107
|
+
"instruction": "Let's create an async flow with retry logic:",
|
108
|
+
"code": "import asyncio\nimport random\nfrom quantumflow import async_flow, retry\n\n@async_flow\n@retry(max_attempts=3, backoff_factor=0.5)\nasync def fetch_data(url):\n # Simulate network request\n await asyncio.sleep(0.2)\n # Randomly fail to demonstrate retry\n if random.random() < 0.5:\n raise ConnectionError('Network error')\n return f'Data from {url}'\n\n# We'll just define it, not run it since this is synchronous code\nfancy_print('Async flow with retry defined!', style='bold blue')",
|
109
|
+
"expected_output": None
|
110
|
+
}
|
111
|
+
]
|
112
|
+
},
|
113
|
+
{
|
114
|
+
"name": "Building Data Pipelines",
|
115
|
+
"description": "Learn how to chain flows to create data processing pipelines.",
|
116
|
+
"steps": [
|
117
|
+
{
|
118
|
+
"instruction": "Let's define multiple flows that work together:",
|
119
|
+
"code": "from quantumflow import qflow\nfrom typing import List, Dict\n\n@qflow\ndef extract(source: str) -> List[str]:\n \"\"\"Extract data from a source.\"\"\"\n # Simulate data extraction\n return [f'record-{i}' for i in range(1, 6)]\n\n@qflow\ndef transform(data: List[str]) -> List[Dict]:\n \"\"\"Transform raw data into structured format.\"\"\"\n return [{'id': item, 'processed': True, 'value': len(item)} for item in data]\n\n@qflow\ndef load(items: List[Dict]) -> str:\n \"\"\"Load processed data into a destination.\"\"\"\n # Simulate loading data\n return f'Successfully loaded {len(items)} items'\n\n@qflow\ndef etl_pipeline(source: str) -> str:\n \"\"\"Complete ETL pipeline.\"\"\"\n raw_data = extract(source)\n transformed_data = transform(raw_data)\n result = load(transformed_data)\n return result\n\n# Run the pipeline\nresult = etl_pipeline('sample-data')\nfancy_print(f'Pipeline result: {result}', style='bold green')",
|
120
|
+
"expected_output": None
|
121
|
+
}
|
122
|
+
]
|
123
|
+
}
|
124
|
+
]
|
125
|
+
self.current_lesson = 0
|
126
|
+
self.current_step = 0
|
127
|
+
# Flag to use fancy output if available
|
128
|
+
self.use_fancy_output = is_fancy_output_available()
|
129
|
+
|
130
|
+
def start(self):
|
131
|
+
"""Start the tutor."""
|
132
|
+
if self.use_fancy_output:
|
133
|
+
fancy_print("Welcome to QuantumFlow Tutor!", style="bold cyan")
|
134
|
+
fancy_print("This guided tutorial will help you learn QuantumFlow concepts.", style="cyan")
|
135
|
+
else:
|
136
|
+
print("Welcome to QuantumFlow Tutor!")
|
137
|
+
print("This guided tutorial will help you learn QuantumFlow concepts.")
|
138
|
+
|
139
|
+
while self.current_lesson < len(self.lessons):
|
140
|
+
lesson = self.lessons[self.current_lesson]
|
141
|
+
|
142
|
+
if self.use_fancy_output:
|
143
|
+
fancy_print(f"\nLesson {self.current_lesson + 1}: {lesson['name']}", style="bold magenta")
|
144
|
+
fancy_print(lesson['description'], style="magenta")
|
145
|
+
else:
|
146
|
+
print(f"\nLesson {self.current_lesson + 1}: {lesson['name']}")
|
147
|
+
print(lesson['description'])
|
148
|
+
|
149
|
+
self.current_step = 0
|
150
|
+
while self.current_step < len(lesson['steps']):
|
151
|
+
step = lesson['steps'][self.current_step]
|
152
|
+
|
153
|
+
if self.use_fancy_output:
|
154
|
+
fancy_print(f"\nStep {self.current_step + 1}:", style="bold blue")
|
155
|
+
fancy_print(step['instruction'], style="blue")
|
156
|
+
fancy_print("\nCode:", style="bold yellow")
|
157
|
+
|
158
|
+
# Try to use Rich syntax highlighting if available
|
159
|
+
try:
|
160
|
+
from rich.syntax import Syntax
|
161
|
+
from rich.console import Console
|
162
|
+
console = Console()
|
163
|
+
syntax = Syntax(step['code'], "python", theme="monokai", line_numbers=True)
|
164
|
+
console.print(syntax)
|
165
|
+
except ImportError:
|
166
|
+
fancy_print(step['code'], style="yellow")
|
167
|
+
else:
|
168
|
+
print(f"\nStep {self.current_step + 1}:")
|
169
|
+
print(step['instruction'])
|
170
|
+
print("\nCode:")
|
171
|
+
print(step['code'])
|
172
|
+
|
173
|
+
if self.use_fancy_output:
|
174
|
+
fancy_print("\nPress Enter to execute this code...", style="dim")
|
175
|
+
else:
|
176
|
+
input("\nPress Enter to execute this code...")
|
177
|
+
|
178
|
+
# Execute the code
|
179
|
+
try:
|
180
|
+
exec(step['code'])
|
181
|
+
if self.use_fancy_output:
|
182
|
+
fancy_print("Code executed successfully!", style="bold green")
|
183
|
+
except Exception as e:
|
184
|
+
if self.use_fancy_output:
|
185
|
+
fancy_print(f"Error: {e}", style="bold red")
|
186
|
+
else:
|
187
|
+
print(f"Error: {e}")
|
188
|
+
|
189
|
+
# Move to the next step
|
190
|
+
self.current_step += 1
|
191
|
+
|
192
|
+
# Move to the next lesson
|
193
|
+
self.current_lesson += 1
|
194
|
+
|
195
|
+
if self.current_lesson < len(self.lessons):
|
196
|
+
if self.use_fancy_output:
|
197
|
+
fancy_print("\nReady for the next lesson? (y/n): ", style="bold cyan", end="")
|
198
|
+
choice = input("\nReady for the next lesson? (y/n): " if not self.use_fancy_output else "")
|
199
|
+
if choice.lower() != 'y':
|
200
|
+
break
|
201
|
+
|
202
|
+
if self.use_fancy_output:
|
203
|
+
fancy_print("\nTutorial completed! You've learned the basics of QuantumFlow.", style="bold green")
|
204
|
+
else:
|
205
|
+
print("\nTutorial completed! You've learned the basics of QuantumFlow.")
|
206
|
+
|
207
|
+
class AutoDoc:
|
208
|
+
"""Automatic documentation generator for flows."""
|
209
|
+
|
210
|
+
def __init__(self, output_dir: str = "docs"):
|
211
|
+
self.output_dir = output_dir
|
212
|
+
|
213
|
+
def generate(self, module_paths: List[str]):
|
214
|
+
"""Generate documentation for flows in the specified modules."""
|
215
|
+
os.makedirs(self.output_dir, exist_ok=True)
|
216
|
+
|
217
|
+
for module_path in module_paths:
|
218
|
+
try:
|
219
|
+
module = importlib.import_module(module_path)
|
220
|
+
|
221
|
+
# Find all flow functions in the module
|
222
|
+
flows = []
|
223
|
+
for name, obj in inspect.getmembers(module):
|
224
|
+
if hasattr(obj, "flow"):
|
225
|
+
flows.append((name, obj))
|
226
|
+
|
227
|
+
if flows:
|
228
|
+
# Create a markdown file for the module
|
229
|
+
with open(os.path.join(self.output_dir, f"{module_path}.md"), "w") as f:
|
230
|
+
f.write(f"# {module_path}\n\n")
|
231
|
+
|
232
|
+
for name, flow_func in flows:
|
233
|
+
f.write(f"## {name}\n\n")
|
234
|
+
|
235
|
+
# Add docstring
|
236
|
+
if flow_func.__doc__:
|
237
|
+
f.write(f"{flow_func.__doc__.strip()}\n\n")
|
238
|
+
|
239
|
+
# Add signature
|
240
|
+
sig = inspect.signature(flow_func)
|
241
|
+
f.write(f"```python\n{name}{sig}\n```\n\n")
|
242
|
+
|
243
|
+
# Add type hints
|
244
|
+
if hasattr(flow_func, "flow") and hasattr(flow_func.flow, "type_hints"):
|
245
|
+
f.write("### Type Hints\n\n")
|
246
|
+
for param, type_hint in flow_func.flow.type_hints.items():
|
247
|
+
if param != "return":
|
248
|
+
f.write(f"- `{param}`: `{type_hint}`\n")
|
249
|
+
if "return" in flow_func.flow.type_hints:
|
250
|
+
f.write(f"- Returns: `{flow_func.flow.type_hints['return']}`\n")
|
251
|
+
f.write("\n")
|
252
|
+
|
253
|
+
# Generate a graph visualization
|
254
|
+
if hasattr(flow_func, "flow"):
|
255
|
+
graph_path = os.path.join(self.output_dir, f"{module_path}_{name}_graph.png")
|
256
|
+
try:
|
257
|
+
flow_func.flow.visualize(output=graph_path)
|
258
|
+
f.write(f"### Flow Graph\n\n")
|
259
|
+
f.write(f"})\n\n")
|
260
|
+
except Exception as e:
|
261
|
+
logger.warning(f"Failed to generate graph for {name}: {e}")
|
262
|
+
|
263
|
+
print(f"Documentation generated for {module_path} with {len(flows)} flows")
|
264
|
+
else:
|
265
|
+
print(f"No flows found in {module_path}")
|
266
|
+
|
267
|
+
except ImportError:
|
268
|
+
print(f"Could not import module {module_path}")
|
269
|
+
except Exception as e:
|
270
|
+
print(f"Error processing module {module_path}: {e}")
|
271
|
+
|
272
|
+
def main():
|
273
|
+
"""Main entry point for the QuantumFlow CLI."""
|
274
|
+
parser = argparse.ArgumentParser(description="QuantumFlow: Next-gen type-and-data-flow framework for Python")
|
275
|
+
parser.add_argument("--version", action="store_true", help="Show version information")
|
276
|
+
parser.add_argument("--verbose", action="store_true", help="Enable verbose output")
|
277
|
+
parser.add_argument("--no-fancy", action="store_true", help="Disable fancy terminal output")
|
278
|
+
|
279
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
280
|
+
|
281
|
+
# Play command
|
282
|
+
play_parser = subparsers.add_parser("play", help="Start the interactive playground")
|
283
|
+
|
284
|
+
# Tutor command
|
285
|
+
tutor_parser = subparsers.add_parser("tutor", help="Start the guided tutorial")
|
286
|
+
|
287
|
+
# AutoDoc command
|
288
|
+
autodoc_parser = subparsers.add_parser("autodoc", help="Generate documentation for flows")
|
289
|
+
autodoc_parser.add_argument("modules", nargs="+", help="Module paths to document")
|
290
|
+
autodoc_parser.add_argument("--output", default="docs", help="Output directory for documentation")
|
291
|
+
|
292
|
+
# Run command
|
293
|
+
run_parser = subparsers.add_parser("run", help="Run a flow")
|
294
|
+
run_parser.add_argument("flow", help="Flow to run (module.function)")
|
295
|
+
run_parser.add_argument("--input", help="Input data as JSON")
|
296
|
+
|
297
|
+
# Visualize command
|
298
|
+
viz_parser = subparsers.add_parser("visualize", help="Visualize a flow")
|
299
|
+
viz_parser.add_argument("flow", help="Flow to visualize (module.function)")
|
300
|
+
viz_parser.add_argument("--output", help="Output file path")
|
301
|
+
viz_parser.add_argument("--format", default="png", choices=["png", "svg", "pdf"], help="Output format")
|
302
|
+
|
303
|
+
args = parser.parse_args()
|
304
|
+
|
305
|
+
# Configure logging with fancy output unless disabled
|
306
|
+
log_level = logging.DEBUG if args.verbose else logging.INFO
|
307
|
+
fancy_output = not args.no_fancy
|
308
|
+
configure_logging(log_level, fancy=fancy_output)
|
309
|
+
|
310
|
+
if args.version:
|
311
|
+
if fancy_output and is_fancy_output_available():
|
312
|
+
fancy_print(f"QuantumFlow version {__version__}", style="bold cyan")
|
313
|
+
else:
|
314
|
+
print(f"QuantumFlow version {__version__}")
|
315
|
+
return 0
|
316
|
+
|
317
|
+
# Enhanced CLI output for commands
|
318
|
+
if args.command == "play":
|
319
|
+
if fancy_output and is_fancy_output_available():
|
320
|
+
fancy_print("Starting QuantumFlow Playground...", style="bold green")
|
321
|
+
playground = QuantumPlayground()
|
322
|
+
playground.start()
|
323
|
+
elif args.command == "tutor":
|
324
|
+
if fancy_output and is_fancy_output_available():
|
325
|
+
fancy_print("Starting QuantumFlow Tutorial...", style="bold green")
|
326
|
+
tutor = FlowTutor()
|
327
|
+
tutor.start()
|
328
|
+
elif args.command == "autodoc":
|
329
|
+
if fancy_output and is_fancy_output_available():
|
330
|
+
fancy_print(f"Generating documentation for modules: {', '.join(args.modules)}", style="bold green")
|
331
|
+
autodoc = AutoDoc(output_dir=args.output)
|
332
|
+
autodoc.generate(args.modules)
|
333
|
+
elif args.command == "run":
|
334
|
+
try:
|
335
|
+
if fancy_output and is_fancy_output_available():
|
336
|
+
fancy_print(f"Running flow: {args.flow}", style="bold green")
|
337
|
+
|
338
|
+
module_name, func_name = args.flow.rsplit(".", 1)
|
339
|
+
module = importlib.import_module(module_name)
|
340
|
+
flow_func = getattr(module, func_name)
|
341
|
+
|
342
|
+
input_data = None
|
343
|
+
if args.input:
|
344
|
+
if fancy_output and is_fancy_output_available():
|
345
|
+
fancy_print(f"Loading input data from: {args.input}", style="dim")
|
346
|
+
with open(args.input, "r") as f:
|
347
|
+
input_data = json.load(f)
|
348
|
+
|
349
|
+
if asyncio.iscoroutinefunction(flow_func):
|
350
|
+
result = asyncio.run(flow_func(input_data) if input_data else flow_func())
|
351
|
+
else:
|
352
|
+
result = flow_func(input_data) if input_data else flow_func()
|
353
|
+
|
354
|
+
if fancy_output and is_fancy_output_available():
|
355
|
+
fancy_print("Result:", style="bold green")
|
356
|
+
|
357
|
+
# Import rich.json only if available and needed
|
358
|
+
try:
|
359
|
+
from rich.json import JSON
|
360
|
+
from rich.console import Console
|
361
|
+
console = Console()
|
362
|
+
console.print(JSON(json.dumps(result)))
|
363
|
+
except ImportError:
|
364
|
+
print(json.dumps(result, indent=2))
|
365
|
+
else:
|
366
|
+
print(json.dumps(result, indent=2))
|
367
|
+
except Exception as e:
|
368
|
+
if fancy_output and is_fancy_output_available():
|
369
|
+
fancy_print(f"Error running flow: {e}", style="bold red")
|
370
|
+
else:
|
371
|
+
print(f"Error running flow: {e}")
|
372
|
+
return 1
|
373
|
+
elif args.command == "visualize":
|
374
|
+
try:
|
375
|
+
if fancy_output and is_fancy_output_available():
|
376
|
+
fancy_print(f"Visualizing flow: {args.flow}", style="bold green")
|
377
|
+
|
378
|
+
module_name, func_name = args.flow.rsplit(".", 1)
|
379
|
+
module = importlib.import_module(module_name)
|
380
|
+
flow_func = getattr(module, func_name)
|
381
|
+
|
382
|
+
visualize_flow(flow_func, format=args.format, output=args.output)
|
383
|
+
|
384
|
+
if args.output:
|
385
|
+
if fancy_output and is_fancy_output_available():
|
386
|
+
fancy_print(f"Flow visualization saved to: {args.output}", style="bold green")
|
387
|
+
else:
|
388
|
+
print(f"Flow visualization saved to {args.output}")
|
389
|
+
except Exception as e:
|
390
|
+
if fancy_output and is_fancy_output_available():
|
391
|
+
fancy_print(f"Error visualizing flow: {e}", style="bold red")
|
392
|
+
else:
|
393
|
+
print(f"Error visualizing flow: {e}")
|
394
|
+
return 1
|
395
|
+
else:
|
396
|
+
parser.print_help()
|
397
|
+
|
398
|
+
return 0
|
399
|
+
|
400
|
+
if __name__ == "__main__":
|
401
|
+
sys.exit(main())
|
quantumflow/config.py
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
"""
|
2
|
+
QuantumFlow Configuration Module
|
3
|
+
|
4
|
+
This module handles configuration settings for QuantumFlow, including
|
5
|
+
default effects and color themes.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
from enum import Enum
|
10
|
+
from typing import Dict, Any, Optional
|
11
|
+
import json
|
12
|
+
|
13
|
+
# ANSI color codes for terminal output
|
14
|
+
class Colors:
|
15
|
+
RESET = "\033[0m"
|
16
|
+
BOLD = "\033[1m"
|
17
|
+
UNDERLINE = "\033[4m"
|
18
|
+
|
19
|
+
# Foreground colors
|
20
|
+
BLACK = "\033[30m"
|
21
|
+
RED = "\033[31m"
|
22
|
+
GREEN = "\033[32m"
|
23
|
+
YELLOW = "\033[33m"
|
24
|
+
BLUE = "\033[34m"
|
25
|
+
MAGENTA = "\033[35m"
|
26
|
+
CYAN = "\033[36m"
|
27
|
+
WHITE = "\033[37m"
|
28
|
+
|
29
|
+
# Background colors
|
30
|
+
BG_BLACK = "\033[40m"
|
31
|
+
BG_RED = "\033[41m"
|
32
|
+
BG_GREEN = "\033[42m"
|
33
|
+
BG_YELLOW = "\033[43m"
|
34
|
+
BG_BLUE = "\033[44m"
|
35
|
+
BG_MAGENTA = "\033[45m"
|
36
|
+
BG_CYAN = "\033[46m"
|
37
|
+
BG_WHITE = "\033[47m"
|
38
|
+
|
39
|
+
# Bright variants
|
40
|
+
BRIGHT_BLACK = "\033[90m"
|
41
|
+
BRIGHT_RED = "\033[91m"
|
42
|
+
BRIGHT_GREEN = "\033[92m"
|
43
|
+
BRIGHT_YELLOW = "\033[93m"
|
44
|
+
BRIGHT_BLUE = "\033[94m"
|
45
|
+
BRIGHT_MAGENTA = "\033[95m"
|
46
|
+
BRIGHT_CYAN = "\033[96m"
|
47
|
+
BRIGHT_WHITE = "\033[97m"
|
48
|
+
|
49
|
+
|
50
|
+
class EffectType(Enum):
|
51
|
+
AUTO_CONVERSION = "auto_conversion"
|
52
|
+
TYPE_CHECKING = "type_checking"
|
53
|
+
ERROR_HANDLING = "error_handling"
|
54
|
+
LOGGING = "logging"
|
55
|
+
COLORIZE = "colorize"
|
56
|
+
|
57
|
+
|
58
|
+
# Default configuration
|
59
|
+
DEFAULT_CONFIG = {
|
60
|
+
"effects": {
|
61
|
+
EffectType.AUTO_CONVERSION.value: True,
|
62
|
+
EffectType.TYPE_CHECKING.value: True,
|
63
|
+
EffectType.ERROR_HANDLING.value: True,
|
64
|
+
EffectType.LOGGING.value: True,
|
65
|
+
EffectType.COLORIZE.value: True,
|
66
|
+
},
|
67
|
+
"colors": {
|
68
|
+
"success": Colors.GREEN,
|
69
|
+
"error": Colors.RED,
|
70
|
+
"warning": Colors.YELLOW,
|
71
|
+
"info": Colors.BLUE,
|
72
|
+
"highlight": Colors.MAGENTA,
|
73
|
+
"code": Colors.CYAN,
|
74
|
+
},
|
75
|
+
"terminal_output": True
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
class QuantumFlowConfig:
|
80
|
+
"""Configuration manager for QuantumFlow."""
|
81
|
+
|
82
|
+
_instance = None
|
83
|
+
_config: Dict[str, Any] = {}
|
84
|
+
|
85
|
+
def __new__(cls):
|
86
|
+
if cls._instance is None:
|
87
|
+
cls._instance = super(QuantumFlowConfig, cls).__new__(cls)
|
88
|
+
cls._instance._config = DEFAULT_CONFIG.copy()
|
89
|
+
cls._instance._load_user_config()
|
90
|
+
return cls._instance
|
91
|
+
|
92
|
+
def _load_user_config(self):
|
93
|
+
"""Load user configuration from file if it exists."""
|
94
|
+
config_path = os.path.expanduser("~/.quantumflow/config.json")
|
95
|
+
if os.path.exists(config_path):
|
96
|
+
try:
|
97
|
+
with open(config_path, 'r') as f:
|
98
|
+
user_config = json.load(f)
|
99
|
+
# Update only existing keys to prevent invalid settings
|
100
|
+
for section, settings in user_config.items():
|
101
|
+
if section in self._config:
|
102
|
+
if isinstance(settings, dict):
|
103
|
+
self._config[section].update(settings)
|
104
|
+
else:
|
105
|
+
self._config[section] = settings
|
106
|
+
except (json.JSONDecodeError, IOError):
|
107
|
+
# If there's an error, just use defaults
|
108
|
+
pass
|
109
|
+
|
110
|
+
def save_config(self):
|
111
|
+
"""Save current configuration to user config file."""
|
112
|
+
config_dir = os.path.expanduser("~/.quantumflow")
|
113
|
+
if not os.path.exists(config_dir):
|
114
|
+
os.makedirs(config_dir)
|
115
|
+
|
116
|
+
config_path = os.path.join(config_dir, "config.json")
|
117
|
+
with open(config_path, 'w') as f:
|
118
|
+
json.dump(self._config, f, indent=2)
|
119
|
+
|
120
|
+
def get_effect(self, effect_type: EffectType) -> bool:
|
121
|
+
"""Get whether an effect is enabled."""
|
122
|
+
return self._config["effects"].get(effect_type.value, False)
|
123
|
+
|
124
|
+
def set_effect(self, effect_type: EffectType, enabled: bool):
|
125
|
+
"""Enable or disable an effect."""
|
126
|
+
self._config["effects"][effect_type.value] = enabled
|
127
|
+
|
128
|
+
def get_color(self, color_name: str) -> str:
|
129
|
+
"""Get a color code by name."""
|
130
|
+
return self._config["colors"].get(color_name, Colors.RESET)
|
131
|
+
|
132
|
+
def set_color(self, color_name: str, color_code: str):
|
133
|
+
"""Set a color code by name."""
|
134
|
+
self._config["colors"][color_name] = color_code
|
135
|
+
|
136
|
+
def is_terminal_output_enabled(self) -> bool:
|
137
|
+
"""Check if terminal colored output is enabled."""
|
138
|
+
return self._config.get("terminal_output", True)
|
139
|
+
|
140
|
+
def set_terminal_output(self, enabled: bool):
|
141
|
+
"""Enable or disable terminal colored output."""
|
142
|
+
self._config["terminal_output"] = enabled
|
143
|
+
|
144
|
+
def reset_to_defaults(self):
|
145
|
+
"""Reset configuration to default values."""
|
146
|
+
self._config = DEFAULT_CONFIG.copy()
|
147
|
+
|
148
|
+
|
149
|
+
# Create a global configuration instance
|
150
|
+
config = QuantumFlowConfig()
|
151
|
+
|
152
|
+
|
153
|
+
def colorize(text: str, color_name: str) -> str:
|
154
|
+
"""Colorize text if terminal output is enabled."""
|
155
|
+
if not config.is_terminal_output_enabled():
|
156
|
+
return text
|
157
|
+
|
158
|
+
color = config.get_color(color_name)
|
159
|
+
return f"{color}{text}{Colors.RESET}"
|
160
|
+
|
161
|
+
|
162
|
+
def success(text: str) -> str:
|
163
|
+
"""Format text as success message."""
|
164
|
+
return colorize(text, "success")
|
165
|
+
|
166
|
+
|
167
|
+
def error(text: str) -> str:
|
168
|
+
"""Format text as error message."""
|
169
|
+
return colorize(text, "error")
|
170
|
+
|
171
|
+
|
172
|
+
def warning(text: str) -> str:
|
173
|
+
"""Format text as warning message."""
|
174
|
+
return colorize(text, "warning")
|
175
|
+
|
176
|
+
|
177
|
+
def info(text: str) -> str:
|
178
|
+
"""Format text as info message."""
|
179
|
+
return colorize(text, "info")
|
180
|
+
|
181
|
+
|
182
|
+
def highlight(text: str) -> str:
|
183
|
+
"""Format text as highlighted."""
|
184
|
+
return colorize(text, "highlight")
|
185
|
+
|
186
|
+
|
187
|
+
def code(text: str) -> str:
|
188
|
+
"""Format text as code."""
|
189
|
+
return colorize(text, "code")
|