qyro 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.
Files changed (45) hide show
  1. qyro/__init__.py +17 -0
  2. qyro/adapters/__init__.py +4 -0
  3. qyro/adapters/language_adapters/__init__.py +4 -0
  4. qyro/adapters/language_adapters/c/__init__.py +4 -0
  5. qyro/adapters/language_adapters/python/__init__.py +4 -0
  6. qyro/adapters/language_adapters/python/python_adapter.py +584 -0
  7. qyro/cli/__init__.py +8 -0
  8. qyro/cli/__main__.py +5 -0
  9. qyro/cli/cli.py +392 -0
  10. qyro/cli/interactive.py +297 -0
  11. qyro/common/__init__.py +37 -0
  12. qyro/common/animation.py +82 -0
  13. qyro/common/builder.py +434 -0
  14. qyro/common/compiler.py +895 -0
  15. qyro/common/config.py +93 -0
  16. qyro/common/constants.py +99 -0
  17. qyro/common/errors.py +176 -0
  18. qyro/common/frontend.py +74 -0
  19. qyro/common/health.py +358 -0
  20. qyro/common/kafka_manager.py +192 -0
  21. qyro/common/logging.py +149 -0
  22. qyro/common/memory.py +147 -0
  23. qyro/common/metrics.py +301 -0
  24. qyro/common/monitoring.py +468 -0
  25. qyro/common/parser.py +91 -0
  26. qyro/common/platform.py +609 -0
  27. qyro/common/redis_memory.py +1108 -0
  28. qyro/common/rpc.py +287 -0
  29. qyro/common/sandbox.py +191 -0
  30. qyro/common/schema_loader.py +33 -0
  31. qyro/common/secure_sandbox.py +490 -0
  32. qyro/common/toolchain_validator.py +617 -0
  33. qyro/common/type_generator.py +176 -0
  34. qyro/common/validation.py +401 -0
  35. qyro/common/validator.py +204 -0
  36. qyro/gateway/__init__.py +8 -0
  37. qyro/gateway/gateway.py +303 -0
  38. qyro/orchestrator/__init__.py +8 -0
  39. qyro/orchestrator/orchestrator.py +1223 -0
  40. qyro-2.0.0.dist-info/METADATA +244 -0
  41. qyro-2.0.0.dist-info/RECORD +45 -0
  42. qyro-2.0.0.dist-info/WHEEL +5 -0
  43. qyro-2.0.0.dist-info/entry_points.txt +2 -0
  44. qyro-2.0.0.dist-info/licenses/LICENSE +21 -0
  45. qyro-2.0.0.dist-info/top_level.txt +1 -0
qyro/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+ """
2
+ Qyro - Universal Polyglot Runtime
3
+ The Universal Polyglot Runtime - Write Python, C, Rust, Java in one file with shared state and modern microservices architecture.
4
+ """
5
+
6
+ __version__ = "2.0.0"
7
+ __author__ = "Qyro Team"
8
+ __email__ = "team@qyro.dev"
9
+
10
+ # Define the package exports
11
+ __all__ = [
12
+ 'orchestrator',
13
+ 'common',
14
+ 'cli',
15
+ 'gateway',
16
+ 'adapters'
17
+ ]
@@ -0,0 +1,4 @@
1
+ """
2
+ Qyro Language Adapters
3
+ Native bindings for different programming languages
4
+ """
@@ -0,0 +1,4 @@
1
+ """
2
+ Nexus Language Adapters
3
+ Native bindings for different programming languages
4
+ """
@@ -0,0 +1,4 @@
1
+ """
2
+ Nexus C Language Adapter
3
+ Native C bindings for the polyglot runtime
4
+ """
@@ -0,0 +1,4 @@
1
+ """
2
+ Nexus Python Language Adapter
3
+ Python bindings for the polyglot runtime
4
+ """
@@ -0,0 +1,584 @@
1
+ """
2
+ Nexus Python Adapter - Redis Pub/Sub Implementation
3
+ Thread-safe Redis-based communication for cross-language integration.
4
+
5
+ This adapter uses Redis pub/sub for real-time communication instead of
6
+ file-based shared memory. It reads Redis connection info from environment
7
+ variables provided by the orchestrator.
8
+
9
+ Environment Variables:
10
+ NEXUS_REDIS_HOST - Redis server host (default: localhost)
11
+ NEXUS_REDIS_PORT - Redis server port (default: 6379)
12
+ NEXUS_REDIS_DB - Redis database number (default: 0)
13
+ NEXUS_REDIS_PASSWORD - Redis password (optional)
14
+ NEXUS_MODULE_NAME - Module name for this adapter (required)
15
+
16
+ Redis Channels:
17
+ nexus:state - Read/write state
18
+ nexus:state:changed - Subscribe to state changes
19
+ nexus:events - Publish/subscribe to events
20
+ nexus:broadcast - Broadcast messages
21
+ """
22
+
23
+ import os
24
+ import json
25
+ import time
26
+ import threading
27
+ import uuid
28
+ from typing import Dict, Any, Optional, Callable, List
29
+ from dataclasses import dataclass
30
+
31
+ try:
32
+ import redis
33
+ except ImportError:
34
+ raise ImportError(
35
+ "Redis library is required. Install with: pip install redis"
36
+ )
37
+
38
+
39
+ # Error codes matching Python constants.py
40
+ class NexusError(Exception):
41
+ """Base exception for Nexus errors."""
42
+ pass
43
+
44
+
45
+ class ErrorCode:
46
+ OK = 0
47
+ REDIS_CONNECTION_FAILED = 1001
48
+ REDIS_SUBSCRIPTION_FAILED = 1002
49
+ STATE_READ_FAILED = 1003
50
+ STATE_WRITE_FAILED = 1004
51
+ EVENT_PUBLISH_FAILED = 1005
52
+ JSON_PARSE_ERROR = 2001
53
+
54
+
55
+ @dataclass
56
+ class RedisConfig:
57
+ """Redis connection configuration."""
58
+ host: str
59
+ port: int
60
+ db: int
61
+ password: Optional[str]
62
+ module_name: str
63
+
64
+
65
+ class NexusModule:
66
+ """
67
+ Nexus Python Adapter using Redis pub/sub for communication.
68
+
69
+ This class provides a Redis-based interface for cross-language
70
+ communication, replacing file-based shared memory with Redis pub/sub.
71
+ """
72
+
73
+ # Redis channel names
74
+ CHANNEL_STATE = "nexus:state"
75
+ CHANNEL_STATE_CHANGED = "nexus:state:changed"
76
+ CHANNEL_EVENTS = "nexus:events"
77
+ CHANNEL_BROADCAST = "nexus:broadcast"
78
+
79
+ def __init__(self, module_name: Optional[str] = None):
80
+ """
81
+ Initialize the Nexus module with Redis connection.
82
+
83
+ Args:
84
+ module_name: Module name (defaults to NEXUS_MODULE_NAME env var)
85
+ """
86
+ self.config = self._load_config(module_name)
87
+ self.redis_client: Optional[redis.Redis] = None
88
+ self.pubsub: Optional[redis.client.PubSub] = None
89
+ self._subscribed = False
90
+ self._event_handlers: Dict[str, List[Callable]] = {}
91
+ self._state_change_handlers: List[Callable] = []
92
+ self._broadcast_handlers: List[Callable] = []
93
+ self._listener_thread: Optional[threading.Thread] = None
94
+ self._running = False
95
+ self._process_id = uuid.uuid4().hex[:8]
96
+
97
+ def _load_config(self, module_name: Optional[str]) -> RedisConfig:
98
+ """Load Redis configuration from environment variables."""
99
+ host = os.getenv("NEXUS_REDIS_HOST", "localhost")
100
+ port = int(os.getenv("NEXUS_REDIS_PORT", "6379"))
101
+ db = int(os.getenv("NEXUS_REDIS_DB", "0"))
102
+ password = os.getenv("NEXUS_REDIS_PASSWORD")
103
+ name = module_name or os.getenv("NEXUS_MODULE_NAME", "python_module")
104
+
105
+ return RedisConfig(
106
+ host=host,
107
+ port=port,
108
+ db=db,
109
+ password=password,
110
+ module_name=name
111
+ )
112
+
113
+ def connect(self) -> None:
114
+ """
115
+ Connect to Redis and initialize pub/sub.
116
+
117
+ Raises:
118
+ NexusError: If connection fails
119
+ """
120
+ try:
121
+ # Create Redis client
122
+ self.redis_client = redis.Redis(
123
+ host=self.config.host,
124
+ port=self.config.port,
125
+ db=self.config.db,
126
+ password=self.config.password,
127
+ decode_responses=True,
128
+ socket_connect_timeout=5,
129
+ socket_timeout=5
130
+ )
131
+
132
+ # Test connection
133
+ self.redis_client.ping()
134
+
135
+ # Create pub/sub instance
136
+ self.pubsub = self.redis_client.pubsub()
137
+
138
+ print(f"[NEXUS-Python] Connected to Redis at {self.config.host}:{self.config.port}")
139
+ print(f"[NEXUS-Python] Module: {self.config.module_name} (PID: {self._process_id})")
140
+
141
+ except redis.ConnectionError as e:
142
+ raise NexusError(
143
+ ErrorCode.REDIS_CONNECTION_FAILED,
144
+ f"Failed to connect to Redis: {e}"
145
+ )
146
+ except Exception as e:
147
+ raise NexusError(
148
+ ErrorCode.REDIS_CONNECTION_FAILED,
149
+ f"Unexpected error connecting to Redis: {e}"
150
+ )
151
+
152
+ def disconnect(self) -> None:
153
+ """Disconnect from Redis and cleanup resources."""
154
+ self._running = False
155
+
156
+ if self._listener_thread:
157
+ self._listener_thread.join(timeout=2)
158
+
159
+ if self.pubsub:
160
+ try:
161
+ self.pubsub.close()
162
+ except Exception:
163
+ pass
164
+
165
+ if self.redis_client:
166
+ try:
167
+ self.redis_client.close()
168
+ except Exception:
169
+ pass
170
+
171
+ self._subscribed = False
172
+ print("[NEXUS-Python] Disconnected from Redis")
173
+
174
+ # ========================================================================
175
+ # STATE OPERATIONS
176
+ # ========================================================================
177
+
178
+ def read_state(self) -> Dict[str, Any]:
179
+ """
180
+ Read the current state from Redis.
181
+
182
+ Returns:
183
+ Dictionary containing the current state
184
+
185
+ Raises:
186
+ NexusError: If read fails
187
+ """
188
+ if not self.redis_client:
189
+ raise NexusError(
190
+ ErrorCode.STATE_READ_FAILED,
191
+ "Not connected to Redis"
192
+ )
193
+
194
+ try:
195
+ state_json = self.redis_client.get(self.CHANNEL_STATE)
196
+ if state_json is None:
197
+ return {}
198
+
199
+ return json.loads(state_json)
200
+
201
+ except json.JSONDecodeError as e:
202
+ raise NexusError(
203
+ ErrorCode.JSON_PARSE_ERROR,
204
+ f"Failed to parse state JSON: {e}"
205
+ )
206
+ except Exception as e:
207
+ raise NexusError(
208
+ ErrorCode.STATE_READ_FAILED,
209
+ f"Failed to read state: {e}"
210
+ )
211
+
212
+ def write_state(self, state: Dict[str, Any]) -> None:
213
+ """
214
+ Write state to Redis and publish state change notification.
215
+
216
+ Args:
217
+ state: Dictionary containing the state to write
218
+
219
+ Raises:
220
+ NexusError: If write fails
221
+ """
222
+ if not self.redis_client:
223
+ raise NexusError(
224
+ ErrorCode.STATE_WRITE_FAILED,
225
+ "Not connected to Redis"
226
+ )
227
+
228
+ try:
229
+ state_json = json.dumps(state)
230
+
231
+ # Write state to Redis
232
+ self.redis_client.set(self.CHANNEL_STATE, state_json)
233
+
234
+ # Publish state change notification
235
+ notification = {
236
+ "module": self.config.module_name,
237
+ "pid": self._process_id,
238
+ "timestamp": time.time()
239
+ }
240
+ self.redis_client.publish(
241
+ self.CHANNEL_STATE_CHANGED,
242
+ json.dumps(notification)
243
+ )
244
+
245
+ except Exception as e:
246
+ raise NexusError(
247
+ ErrorCode.STATE_WRITE_FAILED,
248
+ f"Failed to write state: {e}"
249
+ )
250
+
251
+ # ========================================================================
252
+ # EVENT OPERATIONS
253
+ # ========================================================================
254
+
255
+ def publish_event(self, event_type: str, data: Any) -> None:
256
+ """
257
+ Publish an event to the events channel.
258
+
259
+ Args:
260
+ event_type: Type of event (e.g., "function_call", "state_update")
261
+ data: Event data (will be JSON serialized)
262
+
263
+ Raises:
264
+ NexusError: If publish fails
265
+ """
266
+ if not self.redis_client:
267
+ raise NexusError(
268
+ ErrorCode.EVENT_PUBLISH_FAILED,
269
+ "Not connected to Redis"
270
+ )
271
+
272
+ try:
273
+ event = {
274
+ "type": event_type,
275
+ "module": self.config.module_name,
276
+ "pid": self._process_id,
277
+ "timestamp": time.time(),
278
+ "data": data
279
+ }
280
+
281
+ self.redis_client.publish(
282
+ self.CHANNEL_EVENTS,
283
+ json.dumps(event)
284
+ )
285
+
286
+ except Exception as e:
287
+ raise NexusError(
288
+ ErrorCode.EVENT_PUBLISH_FAILED,
289
+ f"Failed to publish event: {e}"
290
+ )
291
+
292
+ def broadcast(self, message: Any) -> None:
293
+ """
294
+ Broadcast a message to all modules.
295
+
296
+ Args:
297
+ message: Message to broadcast (will be JSON serialized)
298
+
299
+ Raises:
300
+ NexusError: If broadcast fails
301
+ """
302
+ if not self.redis_client:
303
+ raise NexusError(
304
+ ErrorCode.EVENT_PUBLISH_FAILED,
305
+ "Not connected to Redis"
306
+ )
307
+
308
+ try:
309
+ broadcast_msg = {
310
+ "module": self.config.module_name,
311
+ "pid": self._process_id,
312
+ "timestamp": time.time(),
313
+ "message": message
314
+ }
315
+
316
+ self.redis_client.publish(
317
+ self.CHANNEL_BROADCAST,
318
+ json.dumps(broadcast_msg)
319
+ )
320
+
321
+ except Exception as e:
322
+ raise NexusError(
323
+ ErrorCode.EVENT_PUBLISH_FAILED,
324
+ f"Failed to broadcast: {e}"
325
+ )
326
+
327
+ # ========================================================================
328
+ # SUBSCRIPTION HANDLERS
329
+ # ========================================================================
330
+
331
+ def on_event(self, event_type: str) -> Callable:
332
+ """
333
+ Decorator to register an event handler.
334
+
335
+ Args:
336
+ event_type: Type of event to handle
337
+
338
+ Returns:
339
+ Decorator function
340
+ """
341
+ def decorator(handler: Callable):
342
+ if event_type not in self._event_handlers:
343
+ self._event_handlers[event_type] = []
344
+ self._event_handlers[event_type].append(handler)
345
+ return handler
346
+ return decorator
347
+
348
+ def on_state_changed(self, handler: Callable) -> None:
349
+ """
350
+ Register a handler for state change events.
351
+
352
+ Args:
353
+ handler: Function to call when state changes
354
+ """
355
+ self._state_change_handlers.append(handler)
356
+
357
+ def on_broadcast(self, handler: Callable) -> None:
358
+ """
359
+ Register a handler for broadcast messages.
360
+
361
+ Args:
362
+ handler: Function to call when broadcast received
363
+ """
364
+ self._broadcast_handlers.append(handler)
365
+
366
+ # ========================================================================
367
+ # PUB/SUB LISTENER
368
+ # ========================================================================
369
+
370
+ def subscribe(self) -> None:
371
+ """
372
+ Subscribe to Redis channels and start the listener thread.
373
+
374
+ This subscribes to:
375
+ - nexus:state:changed - State change notifications
376
+ - nexus:events - All events
377
+ - nexus:broadcast - Broadcast messages
378
+ """
379
+ if not self.pubsub:
380
+ raise NexusError(
381
+ ErrorCode.REDIS_SUBSCRIPTION_FAILED,
382
+ "PubSub not initialized"
383
+ )
384
+
385
+ try:
386
+ # Subscribe to channels
387
+ self.pubsub.subscribe(
388
+ self.CHANNEL_STATE_CHANGED,
389
+ self.CHANNEL_EVENTS,
390
+ self.CHANNEL_BROADCAST
391
+ )
392
+
393
+ self._subscribed = True
394
+ self._running = True
395
+
396
+ # Start listener thread
397
+ self._listener_thread = threading.Thread(
398
+ target=self._message_listener,
399
+ daemon=True
400
+ )
401
+ self._listener_thread.start()
402
+
403
+ print("[NEXUS-Python] Subscribed to Redis channels")
404
+
405
+ except Exception as e:
406
+ raise NexusError(
407
+ ErrorCode.REDIS_SUBSCRIPTION_FAILED,
408
+ f"Failed to subscribe: {e}"
409
+ )
410
+
411
+ def _message_listener(self) -> None:
412
+ """
413
+ Background thread that listens for Redis pub/sub messages.
414
+ This runs in a separate thread and dispatches messages to handlers.
415
+ """
416
+ while self._running and self.pubsub:
417
+ try:
418
+ message = self.pubsub.get_message(timeout=1)
419
+ if message and message['type'] == 'message':
420
+ self._dispatch_message(message)
421
+
422
+ except Exception as e:
423
+ print(f"[NEXUS-Python] Error in message listener: {e}")
424
+
425
+ def _dispatch_message(self, message: Dict[str, Any]) -> None:
426
+ """
427
+ Dispatch a received message to the appropriate handlers.
428
+
429
+ Args:
430
+ message: Redis pub/sub message
431
+ """
432
+ channel = message['channel']
433
+ data = message['data']
434
+
435
+ try:
436
+ payload = json.loads(data)
437
+
438
+ # Handle state change notifications
439
+ if channel == self.CHANNEL_STATE_CHANGED:
440
+ for handler in self._state_change_handlers:
441
+ try:
442
+ handler(payload)
443
+ except Exception as e:
444
+ print(f"[NEXUS-Python] Error in state change handler: {e}")
445
+
446
+ # Handle events
447
+ elif channel == self.CHANNEL_EVENTS:
448
+ event_type = payload.get('type')
449
+ if event_type and event_type in self._event_handlers:
450
+ for handler in self._event_handlers[event_type]:
451
+ try:
452
+ handler(payload)
453
+ except Exception as e:
454
+ print(f"[NEXUS-Python] Error in event handler: {e}")
455
+
456
+ # Handle broadcasts
457
+ elif channel == self.CHANNEL_BROADCAST:
458
+ for handler in self._broadcast_handlers:
459
+ try:
460
+ handler(payload)
461
+ except Exception as e:
462
+ print(f"[NEXUS-Python] Error in broadcast handler: {e}")
463
+
464
+ except json.JSONDecodeError as e:
465
+ print(f"[NEXUS-Python] Failed to parse message: {e}")
466
+
467
+ # ========================================================================
468
+ # RPC SUPPORT
469
+ # ========================================================================
470
+
471
+ def register_function(self, name: str, handler: Callable) -> None:
472
+ """
473
+ Register a function for cross-language RPC calls.
474
+
475
+ Args:
476
+ name: Function name
477
+ handler: Function to call
478
+ """
479
+ # Publish function registration event
480
+ registration = {
481
+ "type": "function_register",
482
+ "name": name,
483
+ "module": self.config.module_name,
484
+ "pid": self._process_id,
485
+ "lang": "python"
486
+ }
487
+ self.publish_event("function_register", registration)
488
+ print(f"[NEXUS-Python] Registered function: {name}")
489
+
490
+ def call_function(self, name: str, args: List[Any], timeout: float = 30.0) -> Any:
491
+ """
492
+ Call a function registered in another module.
493
+
494
+ Args:
495
+ name: Function name
496
+ args: Function arguments
497
+ timeout: Timeout in seconds
498
+
499
+ Returns:
500
+ Function result
501
+
502
+ Raises:
503
+ NexusError: If call fails or times out
504
+ """
505
+ call_id = uuid.uuid4().hex[:12]
506
+
507
+ # Publish function call event
508
+ call_request = {
509
+ "id": call_id,
510
+ "fn": name,
511
+ "args": args,
512
+ "caller": self._process_id,
513
+ "timestamp": time.time()
514
+ }
515
+ self.publish_event("function_call", call_request)
516
+
517
+ # Wait for response (simplified - in production use a proper response queue)
518
+ start_time = time.time()
519
+ while time.time() - start_time < timeout:
520
+ # Check for response in a real implementation
521
+ time.sleep(0.05)
522
+
523
+ raise NexusError(
524
+ ErrorCode.EVENT_PUBLISH_FAILED,
525
+ f"Call to {name} timed out after {timeout}s"
526
+ )
527
+
528
+ # ========================================================================
529
+ # CONTEXT MANAGER
530
+ # ========================================================================
531
+
532
+ def __enter__(self):
533
+ """Context manager entry."""
534
+ self.connect()
535
+ return self
536
+
537
+ def __exit__(self, exc_type, exc_val, exc_tb):
538
+ """Context manager exit."""
539
+ self.disconnect()
540
+
541
+
542
+ # ============================================================================
543
+ # EXAMPLE USAGE
544
+ # ============================================================================
545
+
546
+ if __name__ == "__main__":
547
+ # Example usage of the Nexus Python adapter
548
+ with NexusModule() as nexus:
549
+ # Subscribe to channels
550
+ nexus.subscribe()
551
+
552
+ # Register event handlers
553
+ @nexus.on_event("function_call")
554
+ def handle_function_call(event):
555
+ print(f"[NEXUS-Python] Received function call: {event}")
556
+
557
+ nexus.on_state_changed(lambda e: print(f"[NEXUS-Python] State changed: {e}"))
558
+ nexus.on_broadcast(lambda e: print(f"[NEXUS-Python] Broadcast: {e}"))
559
+
560
+ # Register a function
561
+ def my_function(args):
562
+ return {"result": "Hello from Python!"}
563
+
564
+ nexus.register_function("my_function", my_function)
565
+
566
+ # Write state
567
+ nexus.write_state({"counter": 0, "message": "Hello from Python"})
568
+
569
+ # Read state
570
+ state = nexus.read_state()
571
+ print(f"[NEXUS-Python] Current state: {state}")
572
+
573
+ # Publish an event
574
+ nexus.publish_event("test_event", {"data": "test"})
575
+
576
+ # Broadcast a message
577
+ nexus.broadcast("Hello from Python module!")
578
+
579
+ # Keep running
580
+ try:
581
+ while True:
582
+ time.sleep(1)
583
+ except KeyboardInterrupt:
584
+ print("\n[NEXUS-Python] Shutting down...")
qyro/cli/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """
2
+ Qyro CLI
3
+ Command-line interface for the polyglot runtime
4
+ """
5
+
6
+ from .cli import main
7
+
8
+ __all__ = ['main']
qyro/cli/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Qyro CLI main entry point"""
2
+ from qyro.cli import main
3
+
4
+ if __name__ == '__main__':
5
+ main()