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
@@ -0,0 +1,176 @@
1
+ import json
2
+ import ast
3
+
4
+ class QyroTypeGenerator:
5
+ def __init__(self, schema_json):
6
+ self.schema = json.loads(schema_json) if isinstance(schema_json, str) else schema_json
7
+
8
+ def _infer_type_from_value(self, value):
9
+ """Infer the appropriate type for a given value."""
10
+ if isinstance(value, list):
11
+ if len(value) > 0:
12
+ element_type = self._infer_type_from_value(value[0])
13
+ return f"list[{element_type}]"
14
+ else:
15
+ return "list[any]"
16
+ elif isinstance(value, dict):
17
+ return "dict"
18
+ elif isinstance(value, int):
19
+ return "int"
20
+ elif isinstance(value, float):
21
+ return "float"
22
+ elif isinstance(value, str):
23
+ return "string"
24
+ elif isinstance(value, bool):
25
+ return "bool"
26
+ else:
27
+ return "any"
28
+
29
+ def _get_c_type(self, value):
30
+ """Get appropriate C type for a value."""
31
+ if isinstance(value, list):
32
+ if len(value) > 0:
33
+ element_type = self._get_c_type(value[0])
34
+ # For C, we'll use void* for generic list and handle serialization separately
35
+ return "void*" # Will be serialized as JSON string
36
+ else:
37
+ return "void*"
38
+ elif isinstance(value, dict):
39
+ return "void*" # Will be serialized as JSON string
40
+ elif isinstance(value, int):
41
+ if -2147483648 <= value <= 2147483647:
42
+ return "int"
43
+ else:
44
+ return "long long"
45
+ elif isinstance(value, float):
46
+ return "double"
47
+ elif isinstance(value, str):
48
+ return "char*" # Dynamic string allocation instead of fixed size
49
+ elif isinstance(value, bool):
50
+ return "int" # C doesn't have bool, use int
51
+ else:
52
+ return "void*"
53
+
54
+ def _get_rust_type(self, value):
55
+ """Get appropriate Rust type for a value."""
56
+ if isinstance(value, list):
57
+ if len(value) > 0:
58
+ element_type = self._get_rust_type(value[0])
59
+ return f"Vec<{element_type}>"
60
+ else:
61
+ return "Vec<any>"
62
+ elif isinstance(value, dict):
63
+ return "std::collections::HashMap<String, serde_json::Value>"
64
+ elif isinstance(value, int):
65
+ if -2147483648 <= value <= 2147483647:
66
+ return "i32"
67
+ else:
68
+ return "i64"
69
+ elif isinstance(value, float):
70
+ return "f64"
71
+ elif isinstance(value, str):
72
+ return "String"
73
+ elif isinstance(value, bool):
74
+ return "bool"
75
+ else:
76
+ return "serde_json::Value"
77
+
78
+ def _get_java_type(self, value):
79
+ """Get appropriate Java type for a value."""
80
+ if isinstance(value, list):
81
+ if len(value) > 0:
82
+ element_type = self._get_java_type(value[0])
83
+ return f"java.util.List<{element_type}>"
84
+ else:
85
+ return "java.util.List<Object>"
86
+ elif isinstance(value, dict):
87
+ return "java.util.Map<String, Object>"
88
+ elif isinstance(value, int):
89
+ if -2147483648 <= value <= 2147483647:
90
+ return "int"
91
+ else:
92
+ return "long"
93
+ elif isinstance(value, float):
94
+ return "double"
95
+ elif isinstance(value, str):
96
+ return "String"
97
+ elif isinstance(value, bool):
98
+ return "boolean"
99
+ else:
100
+ return "Object"
101
+
102
+ def _get_ts_type(self, value):
103
+ """Get appropriate TypeScript type for a value."""
104
+ if isinstance(value, list):
105
+ if len(value) > 0:
106
+ element_type = self._get_ts_type(value[0])
107
+ return f"{element_type}[]"
108
+ else:
109
+ return "any[]"
110
+ elif isinstance(value, dict):
111
+ return "{ [key: string]: any }"
112
+ elif isinstance(value, int) or isinstance(value, float):
113
+ return "number"
114
+ elif isinstance(value, str):
115
+ return "string"
116
+ elif isinstance(value, bool):
117
+ return "boolean"
118
+ else:
119
+ return "any"
120
+
121
+ def generate_c_structs(self):
122
+ code = "#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n"
123
+ code += "// Note: Complex types (lists, dicts) are stored as JSON strings and require serialization/deserialization\n"
124
+ code += "typedef struct {\n"
125
+ for key, value in self.schema.items():
126
+ c_type = self._get_c_type(value)
127
+ if c_type == "char*":
128
+ # For strings, we'll use char* instead of fixed-size arrays to avoid buffer overflows
129
+ code += f" {c_type} {key}; // Dynamically allocated string\n"
130
+ else:
131
+ code += f" {c_type} {key};\n"
132
+ code += "} GlobalState;\n\n"
133
+ # Add helper functions for managing dynamic strings
134
+ code += "// Helper functions for dynamic memory management\n"
135
+ code += "void init_GlobalState(GlobalState* state) {\n"
136
+ for key, value in self.schema.items():
137
+ c_type = self._get_c_type(value)
138
+ if c_type == "char*":
139
+ code += f" state->{key} = NULL;\n"
140
+ elif "void*" in c_type: # For lists and dicts
141
+ code += f" state->{key} = NULL;\n"
142
+ code += "}\n\n"
143
+ code += "void free_GlobalState(GlobalState* state) {\n"
144
+ for key, value in self.schema.items():
145
+ c_type = self._get_c_type(value)
146
+ if c_type == "char*":
147
+ code += f" if (state->{key}) free(state->{key});\n"
148
+ elif "void*" in c_type: # For lists and dicts
149
+ code += f" if (state->{key}) free(state->{key});\n"
150
+ code += "}\n"
151
+ return code
152
+
153
+ def generate_rust_structs(self):
154
+ code = "use serde::{Serialize, Deserialize};\nuse std::collections::HashMap;\n\n"
155
+ code += "#[derive(Serialize, Deserialize, Debug, Clone)]\npub struct GlobalState {\n"
156
+ for key, value in self.schema.items():
157
+ rust_type = self._get_rust_type(value)
158
+ code += f" pub {key}: {rust_type},\n"
159
+ code += "}\n"
160
+ return code
161
+
162
+ def generate_java_class(self):
163
+ code = "package nexus;\n\nimport java.util.*;\n\npublic class GlobalState {\n"
164
+ for key, value in self.schema.items():
165
+ java_type = self._get_java_type(value)
166
+ code += f" public {java_type} {key};\n"
167
+ code += "}\n"
168
+ return code
169
+
170
+ def generate_ts_interface(self):
171
+ code = "export interface GlobalState {\n"
172
+ for key, value in self.schema.items():
173
+ ts_type = self._get_ts_type(value)
174
+ code += f" {key}: {ts_type};\n"
175
+ code += "}\n"
176
+ return code
@@ -0,0 +1,401 @@
1
+ """
2
+ Nexus Input Validation and Sanitization
3
+ Production-grade validation and sanitization for secure data handling.
4
+ """
5
+
6
+ import json
7
+ import re
8
+ from typing import Any, Dict, List, Optional, Union, Tuple
9
+ from .errors import NexusError, ErrorCode, JSONError, Result
10
+ import html
11
+ import urllib.parse
12
+
13
+
14
+ class ValidationError(NexusError):
15
+ """Specific exception for validation errors."""
16
+ pass
17
+
18
+
19
+ class InputValidator:
20
+ """
21
+ Production-grade input validation system with multiple layers of security.
22
+ """
23
+
24
+ # Dangerous patterns that should be blocked
25
+ DANGEROUS_PATTERNS = [
26
+ # SQL injection patterns
27
+ r"(?i)(union\s+select|drop\s+\w+|delete\s+from|insert\s+into|update\s+\w+\s+set)",
28
+ # Command injection patterns
29
+ r"(?i)(exec|system|popen|subprocess|os\.)",
30
+ # Path traversal
31
+ r"(\.\.\/|\.\.\\|%2e%2e%2f|%2e%2e%5c)",
32
+ # JavaScript injection
33
+ r"(?i)(<script|javascript:|on\w+\s*=)",
34
+ # File inclusion
35
+ r"(?i)(include|require|open\()",
36
+ # Network operations
37
+ r"(?i)(socket|connect|bind|listen|accept|urlopen|request)",
38
+ ]
39
+
40
+ # Safe patterns for different contexts
41
+ SAFE_PATTERNS = {
42
+ 'identifier': r'^[a-zA-Z_][a-zA-Z0-9_]*$',
43
+ 'email': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
44
+ 'url': r'^https?://[^\s/$.?#].[^\s]*$',
45
+ 'phone': r'^\+?[\d\s\-\(\)]+$',
46
+ 'alphanumeric': r'^[a-zA-Z0-9]+$',
47
+ 'alpha': r'^[a-zA-Z]+$',
48
+ 'numeric': r'^\d+$',
49
+ }
50
+
51
+ @classmethod
52
+ def validate_pattern(cls, value: str, pattern_name: str) -> bool:
53
+ """
54
+ Validate a string against a predefined safe pattern.
55
+
56
+ Args:
57
+ value: String to validate
58
+ pattern_name: Name of the pattern to use
59
+
60
+ Returns:
61
+ True if valid, False otherwise
62
+ """
63
+ if pattern_name not in cls.SAFE_PATTERNS:
64
+ raise ValidationError(ErrorCode.JSON_PARSE_ERROR, f"Unknown pattern: {pattern_name}")
65
+
66
+ pattern = cls.SAFE_PATTERNS[pattern_name]
67
+ return bool(re.match(pattern, value))
68
+
69
+ @classmethod
70
+ def validate_dangerous_content(cls, value: str) -> Tuple[bool, Optional[str]]:
71
+ """
72
+ Check if a string contains dangerous patterns.
73
+
74
+ Args:
75
+ value: String to check
76
+
77
+ Returns:
78
+ Tuple of (is_safe, reason_if_unsafe)
79
+ """
80
+ for i, pattern in enumerate(cls.DANGEROUS_PATTERNS):
81
+ if re.search(pattern, value, re.IGNORECASE):
82
+ return False, f"Dangerous pattern #{i+1} detected"
83
+ return True, None
84
+
85
+ @classmethod
86
+ def sanitize_string(cls, value: str, max_length: int = 10000, allow_html: bool = False) -> str:
87
+ """
88
+ Sanitize a string input with multiple layers of protection.
89
+
90
+ Args:
91
+ value: String to sanitize
92
+ max_length: Maximum allowed length
93
+ allow_html: Whether to allow HTML (will be escaped if False)
94
+
95
+ Returns:
96
+ Sanitized string
97
+ """
98
+ if not isinstance(value, str):
99
+ value = str(value)
100
+
101
+ # Truncate to max length
102
+ value = value[:max_length]
103
+
104
+ # Remove null bytes
105
+ value = value.replace('\x00', '')
106
+
107
+ if not allow_html:
108
+ # Escape HTML entities
109
+ value = html.escape(value, quote=True)
110
+
111
+ return value
112
+
113
+ @classmethod
114
+ def sanitize_json(cls, data: Union[Dict, List, str], max_depth: int = 10) -> Union[Dict, List]:
115
+ """
116
+ Recursively sanitize JSON data with depth protection.
117
+
118
+ Args:
119
+ data: JSON data to sanitize
120
+ max_depth: Maximum recursion depth
121
+
122
+ Returns:
123
+ Sanitized JSON data
124
+ """
125
+ return cls._sanitize_json_recursive(data, max_depth, 0)
126
+
127
+ @classmethod
128
+ def _sanitize_json_recursive(cls, data: Any, max_depth: int, current_depth: int) -> Any:
129
+ """Helper method for recursive sanitization."""
130
+ if current_depth > max_depth:
131
+ raise ValidationError(ErrorCode.JSON_PARSE_ERROR, f"JSON exceeds maximum depth of {max_depth}")
132
+
133
+ if isinstance(data, str):
134
+ return cls.sanitize_string(data)
135
+ elif isinstance(data, dict):
136
+ return {k: cls._sanitize_json_recursive(v, max_depth, current_depth + 1) for k, v in data.items()}
137
+ elif isinstance(data, list):
138
+ return [cls._sanitize_json_recursive(item, max_depth, current_depth + 1) for item in data]
139
+ else:
140
+ return data
141
+
142
+ @classmethod
143
+ def validate_json_structure(cls, data: Dict, schema: Dict, path: str = "root") -> List[str]:
144
+ """
145
+ Validate JSON data against a schema with detailed error reporting.
146
+
147
+ Args:
148
+ data: Data to validate
149
+ schema: Schema to validate against
150
+ path: Current path in the validation (for error reporting)
151
+
152
+ Returns:
153
+ List of validation errors
154
+ """
155
+ errors = []
156
+
157
+ for key, expected_type in schema.items():
158
+ full_path = f"{path}.{key}" if path != "root" else key
159
+
160
+ if key not in data:
161
+ if isinstance(expected_type, dict) and expected_type.get('_required', True):
162
+ errors.append(f"Missing required field: {full_path}")
163
+ continue
164
+
165
+ actual_value = data[key]
166
+ actual_type = type(actual_value)
167
+ expected_python_type = cls._get_python_type(expected_type)
168
+
169
+ if actual_type != expected_python_type:
170
+ # Allow int for float fields
171
+ if not (expected_python_type == float and actual_type == int):
172
+ errors.append(f"Type mismatch at {full_path}: expected {expected_python_type.__name__}, got {actual_type.__name__}")
173
+ continue
174
+
175
+ # Validate nested objects
176
+ if isinstance(expected_type, dict) and '_type' not in expected_type:
177
+ if isinstance(actual_value, dict):
178
+ errors.extend(cls.validate_json_structure(actual_value, expected_type, full_path))
179
+ else:
180
+ errors.append(f"Expected object at {full_path}, got {actual_type.__name__}")
181
+
182
+ return errors
183
+
184
+ @classmethod
185
+ def _get_python_type(cls, expected_type: Any) -> type:
186
+ """Convert schema type to Python type."""
187
+ type_mapping = {
188
+ 'string': str,
189
+ 'number': (int, float),
190
+ 'integer': int,
191
+ 'boolean': bool,
192
+ 'array': list,
193
+ 'object': dict,
194
+ str: str,
195
+ int: int,
196
+ float: float,
197
+ bool: bool,
198
+ list: list,
199
+ dict: dict,
200
+ }
201
+
202
+ if isinstance(expected_type, str):
203
+ return type_mapping.get(expected_type, str)
204
+ elif isinstance(expected_type, type):
205
+ return expected_type
206
+ else:
207
+ return type(expected_type) if expected_type else type(None)
208
+
209
+
210
+ class SchemaValidator:
211
+ """
212
+ Advanced schema validation with type safety and structure consistency.
213
+ """
214
+
215
+ def __init__(self, schema: Dict[str, Any]):
216
+ self.schema = schema
217
+ self.validator = InputValidator()
218
+
219
+ def validate(self, data: Dict[str, Any]) -> Result:
220
+ """
221
+ Validate data against schema with comprehensive error reporting.
222
+
223
+ Args:
224
+ data: Data to validate against schema
225
+
226
+ Returns:
227
+ Result object with validation outcome
228
+ """
229
+ try:
230
+ # First, check for dangerous content
231
+ sanitized_data = self.validator.sanitize_json(data)
232
+
233
+ # Then validate structure
234
+ errors = self.validator.validate_json_structure(sanitized_data, self.schema)
235
+
236
+ if errors:
237
+ return Result.err(ValidationError(
238
+ ErrorCode.JSON_SCHEMA_MISMATCH,
239
+ f"Schema validation failed: {'; '.join(errors)}"
240
+ ))
241
+
242
+ return Result.ok(sanitized_data)
243
+ except Exception as e:
244
+ return Result.err(ValidationError(
245
+ ErrorCode.JSON_PARSE_ERROR,
246
+ f"Validation error: {str(e)}"
247
+ ))
248
+
249
+
250
+ class RateLimiter:
251
+ """
252
+ Production-grade token bucket rate limiter with sliding window support.
253
+ """
254
+
255
+ def __init__(self, rate: int = 100, per_seconds: int = 60):
256
+ self.rate = rate
257
+ self.per_seconds = per_seconds
258
+ self._buckets: Dict[str, Dict] = {}
259
+ import threading
260
+ self._lock = threading.Lock()
261
+
262
+ def is_allowed(self, key: str) -> bool:
263
+ """
264
+ Check if request is allowed for given key with thread safety.
265
+
266
+ Args:
267
+ key: Identifier for the rate limiting bucket
268
+
269
+ Returns:
270
+ True if allowed, False if rate limited
271
+ """
272
+ import time
273
+ now = time.time()
274
+
275
+ with self._lock:
276
+ if key not in self._buckets:
277
+ self._buckets[key] = {
278
+ "tokens": self.rate,
279
+ "last_update": now
280
+ }
281
+
282
+ bucket = self._buckets[key]
283
+
284
+ # Refill tokens based on time passed
285
+ time_passed = now - bucket["last_update"]
286
+ tokens_to_add = time_passed * (self.rate / self.per_seconds)
287
+ bucket["tokens"] = min(self.rate, bucket["tokens"] + tokens_to_add)
288
+ bucket["last_update"] = now
289
+
290
+ # Check if we have a token available
291
+ if bucket["tokens"] >= 1:
292
+ bucket["tokens"] -= 1
293
+ return True
294
+
295
+ return False
296
+
297
+ def get_retry_after(self, key: str) -> float:
298
+ """
299
+ Get seconds until next request is allowed.
300
+
301
+ Args:
302
+ key: Identifier for the rate limiting bucket
303
+
304
+ Returns:
305
+ Seconds until next request is allowed
306
+ """
307
+ if key not in self._buckets:
308
+ return 0
309
+
310
+ with self._lock:
311
+ bucket = self._buckets[key]
312
+ if bucket["tokens"] >= 1:
313
+ return 0
314
+
315
+ tokens_needed = 1 - bucket["tokens"]
316
+ return tokens_needed * (self.per_seconds / self.rate)
317
+
318
+
319
+ class InputSanitizer:
320
+ """
321
+ Comprehensive input sanitization system.
322
+ """
323
+
324
+ @classmethod
325
+ def sanitize_for_sql(cls, value: str) -> str:
326
+ """Sanitize input for SQL queries."""
327
+ # Remove dangerous SQL keywords and characters
328
+ dangerous_sql = [
329
+ 'DROP', 'DELETE', 'INSERT', 'UPDATE', 'CREATE', 'ALTER', 'EXEC',
330
+ 'UNION', 'SELECT', 'WHERE', 'FROM', 'JOIN', '--', ';', '/*', '*/'
331
+ ]
332
+
333
+ result = value
334
+ for sql_keyword in dangerous_sql:
335
+ # Case insensitive replacement
336
+ result = re.sub(sql_keyword, '', result, flags=re.IGNORECASE)
337
+
338
+ # Remove SQL comment patterns
339
+ result = re.sub(r'/\*.*?\*/', '', result) # Block comments
340
+ result = re.sub(r'--.*', '', result) # Line comments
341
+
342
+ return result.strip()
343
+
344
+ @classmethod
345
+ def sanitize_for_shell(cls, value: str) -> str:
346
+ """Sanitize input for shell commands."""
347
+ # Remove shell metacharacters
348
+ dangerous_chars = [';', '|', '&', '`', '$', '(', ')', '<', '>']
349
+ result = value
350
+ for char in dangerous_chars:
351
+ result = result.replace(char, '')
352
+
353
+ return result.strip()
354
+
355
+ @classmethod
356
+ def sanitize_for_html(cls, value: str) -> str:
357
+ """Sanitize input for HTML output."""
358
+ return html.escape(value, quote=True)
359
+
360
+ @classmethod
361
+ def sanitize_for_url(cls, value: str) -> str:
362
+ """Sanitize input for URL usage."""
363
+ return urllib.parse.quote(value, safe='/:?#[]@!$&\'()*+,;=')
364
+
365
+
366
+ # Global validator instance
367
+ _global_validator = None
368
+
369
+
370
+ def get_validator() -> InputValidator:
371
+ """Get the global input validator instance."""
372
+ global _global_validator
373
+ if _global_validator is None:
374
+ _global_validator = InputValidator()
375
+ return _global_validator
376
+
377
+
378
+ def validate_input(value: str, pattern_name: str = None) -> Tuple[bool, str]:
379
+ """
380
+ Convenience function to validate input.
381
+
382
+ Args:
383
+ value: Value to validate
384
+ pattern_name: Optional pattern name to validate against
385
+
386
+ Returns:
387
+ Tuple of (is_valid, message)
388
+ """
389
+ validator = get_validator()
390
+
391
+ if pattern_name:
392
+ if validator.validate_pattern(value, pattern_name):
393
+ return True, "Valid input"
394
+ else:
395
+ return False, f"Does not match pattern: {pattern_name}"
396
+
397
+ is_safe, reason = validator.validate_dangerous_content(value)
398
+ if is_safe:
399
+ return True, "Valid input"
400
+ else:
401
+ return False, f"Contains dangerous content: {reason}"