maqet 0.0.1.3__py3-none-any.whl → 0.0.5__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 (83) hide show
  1. maqet/__init__.py +50 -6
  2. maqet/__main__.py +96 -0
  3. maqet/__version__.py +3 -0
  4. maqet/api/__init__.py +35 -0
  5. maqet/api/decorators.py +184 -0
  6. maqet/api/metadata.py +147 -0
  7. maqet/api/registry.py +182 -0
  8. maqet/cli.py +71 -0
  9. maqet/config/__init__.py +26 -0
  10. maqet/config/merger.py +237 -0
  11. maqet/config/parser.py +198 -0
  12. maqet/config/validators.py +519 -0
  13. maqet/config_handlers.py +684 -0
  14. maqet/constants.py +200 -0
  15. maqet/exceptions.py +226 -0
  16. maqet/formatters.py +294 -0
  17. maqet/generators/__init__.py +12 -0
  18. maqet/generators/base_generator.py +101 -0
  19. maqet/generators/cli_generator.py +635 -0
  20. maqet/generators/python_generator.py +247 -0
  21. maqet/generators/rest_generator.py +58 -0
  22. maqet/handlers/__init__.py +12 -0
  23. maqet/handlers/base.py +108 -0
  24. maqet/handlers/init.py +147 -0
  25. maqet/handlers/stage.py +196 -0
  26. maqet/ipc/__init__.py +29 -0
  27. maqet/ipc/retry.py +265 -0
  28. maqet/ipc/runner_client.py +285 -0
  29. maqet/ipc/unix_socket_server.py +239 -0
  30. maqet/logger.py +160 -55
  31. maqet/machine.py +884 -0
  32. maqet/managers/__init__.py +7 -0
  33. maqet/managers/qmp_manager.py +333 -0
  34. maqet/managers/snapshot_coordinator.py +327 -0
  35. maqet/managers/vm_manager.py +683 -0
  36. maqet/maqet.py +1120 -0
  37. maqet/os_interactions.py +46 -0
  38. maqet/process_spawner.py +395 -0
  39. maqet/qemu_args.py +76 -0
  40. maqet/qmp/__init__.py +10 -0
  41. maqet/qmp/commands.py +92 -0
  42. maqet/qmp/keyboard.py +311 -0
  43. maqet/qmp/qmp.py +17 -0
  44. maqet/snapshot.py +473 -0
  45. maqet/state.py +958 -0
  46. maqet/storage.py +702 -162
  47. maqet/validation/__init__.py +9 -0
  48. maqet/validation/config_validator.py +170 -0
  49. maqet/vm_runner.py +523 -0
  50. maqet-0.0.5.dist-info/METADATA +237 -0
  51. maqet-0.0.5.dist-info/RECORD +55 -0
  52. {maqet-0.0.1.3.dist-info → maqet-0.0.5.dist-info}/WHEEL +1 -1
  53. maqet-0.0.5.dist-info/entry_points.txt +2 -0
  54. maqet-0.0.5.dist-info/licenses/LICENSE +21 -0
  55. {maqet-0.0.1.3.dist-info → maqet-0.0.5.dist-info}/top_level.txt +0 -1
  56. maqet/core.py +0 -395
  57. maqet/functions.py +0 -104
  58. maqet-0.0.1.3.dist-info/METADATA +0 -104
  59. maqet-0.0.1.3.dist-info/RECORD +0 -33
  60. qemu/machine/__init__.py +0 -36
  61. qemu/machine/console_socket.py +0 -142
  62. qemu/machine/machine.py +0 -954
  63. qemu/machine/py.typed +0 -0
  64. qemu/machine/qtest.py +0 -191
  65. qemu/qmp/__init__.py +0 -59
  66. qemu/qmp/error.py +0 -50
  67. qemu/qmp/events.py +0 -717
  68. qemu/qmp/legacy.py +0 -319
  69. qemu/qmp/message.py +0 -209
  70. qemu/qmp/models.py +0 -146
  71. qemu/qmp/protocol.py +0 -1057
  72. qemu/qmp/py.typed +0 -0
  73. qemu/qmp/qmp_client.py +0 -655
  74. qemu/qmp/qmp_shell.py +0 -618
  75. qemu/qmp/qmp_tui.py +0 -655
  76. qemu/qmp/util.py +0 -219
  77. qemu/utils/__init__.py +0 -162
  78. qemu/utils/accel.py +0 -84
  79. qemu/utils/py.typed +0 -0
  80. qemu/utils/qemu_ga_client.py +0 -323
  81. qemu/utils/qom.py +0 -273
  82. qemu/utils/qom_common.py +0 -175
  83. qemu/utils/qom_fuse.py +0 -207
maqet/__init__.py CHANGED
@@ -1,7 +1,51 @@
1
- from .core import Drive, Machine, Maqet
1
+ """
2
+ > LESS MARKETING, MORE FACTS!.
2
3
 
3
- __all__ = (
4
- 'Drive',
5
- 'Machine',
6
- 'Maqet',
7
- )
4
+ MAQET - M4x0n's QEMU Tool
5
+
6
+ A revolutionary VM management system implementing unified API generation architecture.
7
+
8
+ MAQET transforms traditional VM management by providing a "write once, generate everywhere"
9
+ approach where single @api_method decorated methods automatically become:
10
+ - CLI commands (maqet <command>)
11
+ - Python API methods (maqet.method())
12
+ - Configuration-driven calls (YAML key → method execution)
13
+
14
+ Key Features:
15
+ - Zero duplication: One method definition serves all interfaces
16
+ - Type-safe validation across CLI and Python APIs
17
+ - SQLite state management with XDG directory compliance
18
+ - Complete VM lifecycle management (add, start, stop, rm, etc.)
19
+ - QMP integration for VM control
20
+ - Production-ready for dependent projects
21
+
22
+ Quick Start:
23
+ # CLI usage
24
+ $ maqet add config.yaml --name myvm
25
+ $ maqet start myvm --detach
26
+
27
+ # Python API usage
28
+ from maqet import Maqet
29
+ maqet = Maqet()
30
+ vm_id = maqet.add(name='myvm', memory='4G')
31
+ maqet.start(vm_id, detach=True)
32
+
33
+ # Configuration-driven usage
34
+ add: {name: 'myvm', memory: '4G'}
35
+ start: {vm_id: 'myvm', detach: true}
36
+
37
+ Architecture:
38
+ The unified API system consists of:
39
+ - @api_method decorator for metadata capture
40
+ - APIRegistry for tracking decorated methods
41
+ - Generators for creating interfaces (CLI, Python, Config)
42
+ - StateManager for persistent VM state
43
+ - Machine class for QEMU integration
44
+ """
45
+
46
+ from .api import api_method
47
+ from .maqet import Maqet, MaqetError
48
+ from .state import StateManager, VMInstance
49
+
50
+ __version__ = "0.0.5"
51
+ __all__ = ["Maqet", "MaqetError", "StateManager", "VMInstance", "api_method"]
maqet/__main__.py ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MAQET CLI Entry Point.
4
+
5
+ Main CLI interface that uses the unified API generation system.
6
+ """
7
+
8
+ import json
9
+ import sys
10
+ import traceback
11
+ from enum import IntEnum
12
+
13
+ from .__version__ import __version__
14
+ from .maqet import Maqet
15
+
16
+
17
+ class ExitCode(IntEnum):
18
+ """Exit code constants for MAQET CLI."""
19
+
20
+ SUCCESS = 0
21
+ ERROR = 1
22
+ INTERRUPTED = 2
23
+ INVALID_CONFIG = 3
24
+
25
+
26
+ def main():
27
+ """Execute the main CLI entry point."""
28
+ # Check for --version flag first (before creating Maqet instance)
29
+ if "--version" in sys.argv or "-V" in sys.argv:
30
+ print(f"MAQET version {__version__}")
31
+ return ExitCode.SUCCESS
32
+
33
+ # Check for debug mode - this enables full tracebacks on errors
34
+ # Note: --debug is different from -v/--verbose which controls log verbosity
35
+ # --debug affects error reporting, -v affects logging levels
36
+ debug_mode = "--debug" in sys.argv
37
+ if debug_mode:
38
+ sys.argv.remove("--debug")
39
+
40
+ # Check for output format
41
+ output_format = "auto"
42
+ if "--format" in sys.argv:
43
+ idx = sys.argv.index("--format")
44
+ if idx + 1 < len(sys.argv):
45
+ output_format = sys.argv[idx + 1]
46
+ sys.argv.pop(idx) # Remove --format
47
+ sys.argv.pop(idx) # Remove format value
48
+
49
+ try:
50
+ maqet = Maqet()
51
+ result = maqet.cli()
52
+
53
+ # Handle CLI output based on format
54
+ _format_output(result, output_format)
55
+
56
+ return ExitCode.SUCCESS
57
+
58
+ except KeyboardInterrupt:
59
+ print("\nInterrupted by user", file=sys.stderr)
60
+ sys.exit(ExitCode.INTERRUPTED)
61
+
62
+ except Exception as e:
63
+ if debug_mode:
64
+ # Print full traceback in debug mode
65
+ print("\n=== Debug Traceback ===", file=sys.stderr)
66
+ traceback.print_exc()
67
+ print("=" * 23, file=sys.stderr)
68
+ else:
69
+ print(f"Error: {e}", file=sys.stderr)
70
+ print("Run with --debug for full traceback", file=sys.stderr)
71
+ sys.exit(ExitCode.ERROR)
72
+
73
+
74
+ def _format_output(result, format_type: str = "auto"):
75
+ """
76
+ Format and print output using FormatterFactory.
77
+
78
+ Args:
79
+ result: Result data to format
80
+ format_type: Output format (auto, json, yaml, plain, table)
81
+ """
82
+ if result is None:
83
+ return
84
+
85
+ from .formatters import FormatterFactory
86
+
87
+ try:
88
+ formatter = FormatterFactory.create(format_type)
89
+ formatter.format(result)
90
+ except ValueError as e:
91
+ print(f"Error: {e}", file=sys.stderr)
92
+ sys.exit(ExitCode.ERROR)
93
+
94
+
95
+ if __name__ == "__main__":
96
+ main()
maqet/__version__.py ADDED
@@ -0,0 +1,3 @@
1
+ """Version information for MAQET."""
2
+
3
+ __version__ = "0.0.5"
maqet/api/__init__.py ADDED
@@ -0,0 +1,35 @@
1
+ """
2
+ MAQET API System
3
+
4
+ This module implements the unified API generation system that allows
5
+ single method definitions to automatically become CLI commands, Python API
6
+ methods, REST API endpoints, GraphQL resolvers, and other interfaces.
7
+
8
+ The system is designed to be extensible - new generators can be added by:
9
+ 1. Implementing BaseGenerator interface in generators/
10
+ 2. Reading method metadata from API_REGISTRY
11
+ 3. Generating appropriate interface code (routes, schemas, etc.)
12
+
13
+ Example generators:
14
+ - CLIGenerator: Creates argparse CLI commands
15
+ - PythonAPIGenerator: Creates direct Python method calls
16
+ - RestAPIGenerator: Could create FastAPI/Flask routes
17
+ - GraphQLGenerator: Could create GraphQL schema and resolvers
18
+ - OpenAPIGenerator: Could generate OpenAPI/Swagger documentation
19
+ """
20
+
21
+ from .decorators import AutoRegisterAPI, api_method, register_class_methods
22
+ from .metadata import APIMethodMetadata
23
+ from .registry import API_REGISTRY, APIRegistry
24
+
25
+ __all__ = [
26
+ "api_method",
27
+ "register_class_methods",
28
+ "AutoRegisterAPI",
29
+ "API_REGISTRY",
30
+ "APIRegistry",
31
+ "APIMethodMetadata",
32
+ ]
33
+
34
+ # NOTE: The system is designed to be easily extensible for new interfaces.
35
+ # See module docstring above for implementation guidance.
@@ -0,0 +1,184 @@
1
+ """
2
+ API Method Decorator.
3
+
4
+ The @api_method decorator is the core of MAQET's unified API generation system.
5
+ It captures method metadata to enable automatic CLI and Python API generation.
6
+ """
7
+
8
+ import inspect
9
+ from functools import wraps
10
+ from typing import Callable, List, Optional
11
+
12
+ from .metadata import APIMethodMetadata
13
+ from .registry import API_REGISTRY
14
+
15
+
16
+ def api_method(
17
+ cli_name: Optional[str] = None,
18
+ description: Optional[str] = None,
19
+ category: str = "general",
20
+ requires_vm: bool = False,
21
+ examples: Optional[List[str]] = None,
22
+ aliases: Optional[List[str]] = None,
23
+ hidden: bool = False,
24
+ parent: Optional[str] = None,
25
+ ) -> Callable:
26
+ """
27
+ Decorate MAQET API methods to enable unified CLI and Python API generation.
28
+
29
+ This decorator captures method metadata that is used by generators to automatically
30
+ create CLI commands and Python API methods from the same source.
31
+
32
+ Args:
33
+ cli_name: CLI command name (defaults to method name with underscores as dashes)
34
+ description: Human-readable description (defaults to docstring summary)
35
+ category: Method category for grouping ('vm', 'qmp', 'storage', 'system')
36
+ requires_vm: Whether this method requires a VM to be specified
37
+ examples: List of usage examples
38
+ aliases: Alternative CLI command names
39
+ hidden: Whether to hide from CLI help (for internal methods)
40
+ parent: Parent command name for nested subcommands (e.g., 'qmp')
41
+
42
+ Returns:
43
+ Decorated function with metadata attached
44
+
45
+ Example:
46
+ @api_method(
47
+ cli_name="start",
48
+ description="Start a virtual machine",
49
+ category="vm",
50
+ requires_vm=True,
51
+ examples=["maqet start myvm", "maqet start myvm --detach"]
52
+ )
53
+ def start(self, vm_id: str, detach: bool = False):
54
+ '''Start a virtual machine.'''
55
+ # Implementation here
56
+ """
57
+
58
+ def decorator(func: Callable) -> Callable:
59
+ # Extract description from docstring if not provided
60
+ if description is None:
61
+ doc_description = _extract_description_from_docstring(func.__doc__)
62
+ else:
63
+ doc_description = description
64
+
65
+ # Get method signature
66
+ signature = inspect.signature(func)
67
+
68
+ # Create metadata
69
+ metadata = APIMethodMetadata(
70
+ name=func.__name__,
71
+ function=func,
72
+ owner_class="", # Will be set when method is bound to a class
73
+ cli_name=cli_name,
74
+ description=doc_description,
75
+ signature=signature,
76
+ category=category,
77
+ requires_vm=requires_vm,
78
+ examples=examples or [],
79
+ aliases=aliases or [],
80
+ hidden=hidden,
81
+ parent=parent,
82
+ )
83
+
84
+ # Store metadata on the function for later registration
85
+ func._api_metadata = metadata
86
+
87
+ @wraps(func)
88
+ def wrapper(*args, **kwargs):
89
+ return func(*args, **kwargs)
90
+
91
+ # Copy metadata to wrapper
92
+ wrapper._api_metadata = metadata
93
+
94
+ return wrapper
95
+
96
+ return decorator
97
+
98
+
99
+ def _extract_description_from_docstring(docstring: Optional[str]) -> str:
100
+ """
101
+ Extract first line of docstring as description.
102
+
103
+ Args:
104
+ docstring: Function docstring
105
+
106
+ Returns:
107
+ First non-empty line of docstring or default message
108
+ """
109
+ if not docstring:
110
+ return "No description available"
111
+
112
+ lines = [line.strip() for line in docstring.strip().split("\n")]
113
+ first_line = next((line for line in lines if line), "")
114
+
115
+ return first_line or "No description available"
116
+
117
+
118
+ class AutoRegisterAPI:
119
+ """
120
+ Base class that automatically registers @api_method decorated methods.
121
+
122
+ Any class that inherits from this will automatically have its @api_method
123
+ decorated methods registered with the global API registry when the class
124
+ is defined. This eliminates the need for manual register_class_methods() calls.
125
+
126
+ Example:
127
+ class Maqet(AutoRegisterAPI):
128
+ @api_method(category="vm")
129
+ def start(self, vm_id: str):
130
+ pass
131
+
132
+ # Methods are automatically registered - no manual call needed!
133
+ """
134
+
135
+ def __init_subclass__(cls, **kwargs):
136
+ """
137
+ Automatically register decorated methods when a subclass is created.
138
+
139
+ This method is called whenever a class inherits from AutoRegisterAPI,
140
+ enabling automatic registration without manual function calls.
141
+ """
142
+ super().__init_subclass__(**kwargs)
143
+
144
+ # Register all @api_method decorated methods from this class
145
+ for name, method in inspect.getmembers(
146
+ cls, predicate=inspect.isfunction
147
+ ):
148
+ if hasattr(method, "_api_metadata"):
149
+ metadata: APIMethodMetadata = method._api_metadata
150
+ # Set the owner class now that we know it
151
+ metadata.owner_class = cls.__name__
152
+ # Register with global registry
153
+ API_REGISTRY.register(metadata)
154
+
155
+
156
+ def register_class_methods(cls: type) -> None:
157
+ """
158
+ Register all @api_method decorated methods from a class.
159
+
160
+ This function should be called after class definition to register
161
+ all decorated methods with the global API registry.
162
+
163
+ NOTE: This function is kept for backward compatibility, but new code
164
+ should inherit from AutoRegisterAPI instead for automatic registration.
165
+
166
+ Args:
167
+ cls: Class containing decorated methods
168
+
169
+ Example:
170
+ class Maqet:
171
+ @api_method(category="vm")
172
+ def start(self, vm_id: str):
173
+ pass
174
+
175
+ register_class_methods(Maqet)
176
+
177
+ """
178
+ for name, method in inspect.getmembers(cls, predicate=inspect.isfunction):
179
+ if hasattr(method, "_api_metadata"):
180
+ metadata: APIMethodMetadata = method._api_metadata
181
+ # Set the owner class now that we know it
182
+ metadata.owner_class = cls.__name__
183
+ # Register with global registry
184
+ API_REGISTRY.register(metadata)
maqet/api/metadata.py ADDED
@@ -0,0 +1,147 @@
1
+ """
2
+ API Method Metadata.
3
+
4
+ Data structures for capturing metadata about decorated API methods
5
+ to enable automatic CLI and Python API generation.
6
+ """
7
+
8
+ import inspect
9
+ from dataclasses import dataclass, field
10
+ from typing import Any, Callable, Dict, List, Optional
11
+
12
+
13
+ @dataclass
14
+ class APIMethodMetadata:
15
+ """
16
+ Metadata captured from @api_method decorated functions.
17
+
18
+ This data structure contains all the information needed by generators to create
19
+ CLI commands and Python API methods from a single decorated method definition.
20
+
21
+ Attributes:
22
+ name: Original method name (e.g., 'start_vm')
23
+ function: The actual callable function
24
+ owner_class: Class that owns this method (e.g., 'Maqet')
25
+ cli_name: CLI command name (e.g., 'start-vm', defaults to name with dashes)
26
+ description: Human-readable description for help text
27
+ signature: Function signature for parameter validation
28
+ category: Grouping category ('vm', 'qmp', 'storage', 'system')
29
+ requires_vm: Whether this method requires a VM identifier
30
+ examples: List of usage examples for documentation
31
+ aliases: Alternative CLI command names
32
+ hidden: Whether to hide from CLI help (for internal methods)
33
+ parent: Parent command name for nested subcommands (e.g., 'qmp')
34
+
35
+ Example:
36
+ >>> metadata = APIMethodMetadata(
37
+ ... name='start',
38
+ ... function=start_method,
39
+ ... owner_class='Maqet',
40
+ ... cli_name='start',
41
+ ... description='Start a virtual machine',
42
+ ... signature=inspect.signature(start_method),
43
+ ... category='vm',
44
+ ... requires_vm=True
45
+ ... )
46
+ """
47
+
48
+ name: str
49
+ function: Callable
50
+ owner_class: str
51
+ cli_name: Optional[str]
52
+ description: str
53
+ signature: inspect.Signature
54
+ category: str
55
+ requires_vm: bool = False
56
+ examples: List[str] = field(default_factory=list)
57
+ aliases: List[str] = field(default_factory=list)
58
+ hidden: bool = False
59
+ parent: Optional[str] = None
60
+
61
+ def __post_init__(self) -> None:
62
+ """
63
+ Set defaults and validate metadata after initialization.
64
+
65
+ Automatically converts method names to CLI-friendly format
66
+ (underscores become dashes) if cli_name is not explicitly set.
67
+ """
68
+ if self.cli_name is None:
69
+ self.cli_name = self.name.replace("_", "-")
70
+
71
+ @property
72
+ def full_name(self) -> str:
73
+ """
74
+ Get fully qualified method name including class.
75
+
76
+ Returns:
77
+ Full method name in format "ClassName.method_name"
78
+
79
+ Example:
80
+ >>> metadata.full_name
81
+ 'Maqet.start'
82
+ """
83
+ return f"{self.owner_class}.{self.name}"
84
+
85
+ @property
86
+ def parameters(self) -> Dict[str, inspect.Parameter]:
87
+ """
88
+ Get method parameters excluding 'self'.
89
+
90
+ Returns:
91
+ Dictionary of parameter names to inspect.Parameter objects,
92
+ with 'self' parameter filtered out for instance methods.
93
+
94
+ Example:
95
+ >>> params = metadata.parameters
96
+ >>> list(params.keys())
97
+ ['vm_id', 'detach', 'wait']
98
+ """
99
+ params = dict(self.signature.parameters)
100
+ params.pop("self", None)
101
+ return params
102
+
103
+ @property
104
+ def required_parameters(self) -> List[str]:
105
+ """
106
+ Get list of required parameter names.
107
+
108
+ Returns:
109
+ List of parameter names that have no default value
110
+ and are therefore required when calling the method.
111
+ Excludes VAR_POSITIONAL (*args) and VAR_KEYWORD (**kwargs)
112
+ parameters as they are always optional.
113
+
114
+ Example:
115
+ >>> metadata.required_parameters
116
+ ['vm_id']
117
+ """
118
+ required = []
119
+ for name, param in self.parameters.items():
120
+ # Skip *args and **kwargs parameters - they're always optional
121
+ if param.kind in (
122
+ inspect.Parameter.VAR_POSITIONAL,
123
+ inspect.Parameter.VAR_KEYWORD,
124
+ ):
125
+ continue
126
+ if param.default == inspect.Parameter.empty:
127
+ required.append(name)
128
+ return required
129
+
130
+ @property
131
+ def optional_parameters(self) -> List[str]:
132
+ """
133
+ Get list of optional parameter names.
134
+
135
+ Returns:
136
+ List of parameter names that have default values
137
+ and are therefore optional when calling the method.
138
+
139
+ Example:
140
+ >>> metadata.optional_parameters
141
+ ['detach', 'wait']
142
+ """
143
+ optional = []
144
+ for name, param in self.parameters.items():
145
+ if param.default != inspect.Parameter.empty:
146
+ optional.append(name)
147
+ return optional