maqet 0.0.1.4__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.
- maqet/__init__.py +50 -6
- maqet/__main__.py +96 -0
- maqet/__version__.py +3 -0
- maqet/api/__init__.py +35 -0
- maqet/api/decorators.py +184 -0
- maqet/api/metadata.py +147 -0
- maqet/api/registry.py +182 -0
- maqet/cli.py +71 -0
- maqet/config/__init__.py +26 -0
- maqet/config/merger.py +237 -0
- maqet/config/parser.py +198 -0
- maqet/config/validators.py +519 -0
- maqet/config_handlers.py +684 -0
- maqet/constants.py +200 -0
- maqet/exceptions.py +226 -0
- maqet/formatters.py +294 -0
- maqet/generators/__init__.py +12 -0
- maqet/generators/base_generator.py +101 -0
- maqet/generators/cli_generator.py +635 -0
- maqet/generators/python_generator.py +247 -0
- maqet/generators/rest_generator.py +58 -0
- maqet/handlers/__init__.py +12 -0
- maqet/handlers/base.py +108 -0
- maqet/handlers/init.py +147 -0
- maqet/handlers/stage.py +196 -0
- maqet/ipc/__init__.py +29 -0
- maqet/ipc/retry.py +265 -0
- maqet/ipc/runner_client.py +285 -0
- maqet/ipc/unix_socket_server.py +239 -0
- maqet/logger.py +160 -55
- maqet/machine.py +884 -0
- maqet/managers/__init__.py +7 -0
- maqet/managers/qmp_manager.py +333 -0
- maqet/managers/snapshot_coordinator.py +327 -0
- maqet/managers/vm_manager.py +683 -0
- maqet/maqet.py +1120 -0
- maqet/os_interactions.py +46 -0
- maqet/process_spawner.py +395 -0
- maqet/qemu_args.py +76 -0
- maqet/qmp/__init__.py +10 -0
- maqet/qmp/commands.py +92 -0
- maqet/qmp/keyboard.py +311 -0
- maqet/qmp/qmp.py +17 -0
- maqet/snapshot.py +473 -0
- maqet/state.py +958 -0
- maqet/storage.py +702 -162
- maqet/validation/__init__.py +9 -0
- maqet/validation/config_validator.py +170 -0
- maqet/vm_runner.py +523 -0
- maqet-0.0.5.dist-info/METADATA +237 -0
- maqet-0.0.5.dist-info/RECORD +55 -0
- {maqet-0.0.1.4.dist-info → maqet-0.0.5.dist-info}/WHEEL +1 -1
- maqet-0.0.5.dist-info/entry_points.txt +2 -0
- maqet-0.0.5.dist-info/licenses/LICENSE +21 -0
- {maqet-0.0.1.4.dist-info → maqet-0.0.5.dist-info}/top_level.txt +0 -1
- maqet/core.py +0 -411
- maqet/functions.py +0 -104
- maqet-0.0.1.4.dist-info/METADATA +0 -6
- maqet-0.0.1.4.dist-info/RECORD +0 -33
- qemu/machine/__init__.py +0 -36
- qemu/machine/console_socket.py +0 -142
- qemu/machine/machine.py +0 -954
- qemu/machine/py.typed +0 -0
- qemu/machine/qtest.py +0 -191
- qemu/qmp/__init__.py +0 -59
- qemu/qmp/error.py +0 -50
- qemu/qmp/events.py +0 -717
- qemu/qmp/legacy.py +0 -319
- qemu/qmp/message.py +0 -209
- qemu/qmp/models.py +0 -146
- qemu/qmp/protocol.py +0 -1057
- qemu/qmp/py.typed +0 -0
- qemu/qmp/qmp_client.py +0 -655
- qemu/qmp/qmp_shell.py +0 -618
- qemu/qmp/qmp_tui.py +0 -655
- qemu/qmp/util.py +0 -219
- qemu/utils/__init__.py +0 -162
- qemu/utils/accel.py +0 -84
- qemu/utils/py.typed +0 -0
- qemu/utils/qemu_ga_client.py +0 -323
- qemu/utils/qom.py +0 -273
- qemu/utils/qom_common.py +0 -175
- qemu/utils/qom_fuse.py +0 -207
maqet/__init__.py
CHANGED
@@ -1,7 +1,51 @@
|
|
1
|
-
|
1
|
+
"""
|
2
|
+
> LESS MARKETING, MORE FACTS!.
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
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.
|
maqet/api/decorators.py
ADDED
@@ -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
|