1
0
mirror of https://gitlab.com/libvirt/libvirt-python.git synced 2024-10-26 07:55:06 +03:00
libvirt-python/generator.py
Michal Privoznik c40c5f0cfc Add an override impl for virDomainRestoreParams and virDomainSaveParams
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
2022-05-13 13:28:11 +02:00

1796 lines
68 KiB
Python
Executable File

#!/usr/bin/env python3
#
# generate python wrappers from the XML API description
#
import os
import re
import sys
import xml.sax
from contextlib import closing
from collections import defaultdict
from typing import Dict, IO, List, Optional, Set, Tuple, Union # noqa F401
ArgumentType = Tuple[str, str, str]
FunctionType = Tuple[str, ArgumentType, List[ArgumentType], str, str, str]
EnumValue = Union[str, int]
EnumType = Dict[str, EnumValue]
functions = {} # type: Dict[str, FunctionType]
enums = defaultdict(dict) # type: Dict[str, EnumType] # { enumType: { enumConstant: enumValue } }
event_ids = [] # type: List[str]
params = [] # type: List[Tuple[str, str]] # [ (paramName, paramValue)... ]
quiet = True
#######################################################################
#
# That part if purely the API acquisition phase from the
# libvirt API description
#
#######################################################################
debug = 0
onlyOverrides = False
libvirt_headers = [
"libvirt",
"libvirt-common",
"libvirt-domain",
"libvirt-domain-checkpoint",
"libvirt-domain-snapshot",
"libvirt-event",
"libvirt-host",
"libvirt-interface",
"libvirt-network",
"libvirt-nodedev",
"libvirt-nwfilter",
"libvirt-secret",
"libvirt-storage",
"libvirt-stream",
]
def parse(data: IO[str]) -> None:
target = docParser()
with closing(xml.sax.make_parser()) as parser:
parser.setContentHandler(target)
parser.parse(data)
class docParser(xml.sax.handler.ContentHandler):
def __init__(self) -> None:
self._data = [] # type: List[str]
self.in_function = False
def characters(self, text: str) -> None:
if debug:
print("data %s" % text)
self._data.append(text)
def startElement(self, tag: str, attrs: Dict[str, str]) -> None:
if debug:
print("start %s, %s" % (tag, attrs))
if tag == 'function':
self._data = []
self.in_function = True
self.function_cond = ''
self.function_args = [] # type: List[ArgumentType]
self.function_descr = ''
self.function_return = None # type: Optional[ArgumentType]
self.function = attrs.get('name', '')
self.function_file = attrs.get('file', '')
self.function_module = attrs.get('module', '')
elif tag == 'cond':
self._data = []
elif tag == 'info':
self._data = []
elif tag == 'arg':
if self.in_function:
self.function_arg_name = attrs.get('name', '')
if self.function_arg_name == 'from':
self.function_arg_name = 'frm'
self.function_arg_type = attrs.get('type', '')
self.function_arg_info = attrs.get('info', '')
elif tag == 'return':
if self.in_function:
self.function_return_type = attrs.get('type', '')
self.function_return_info = attrs.get('info', '')
self.function_return_field = attrs.get('field', '')
elif tag == 'enum':
# enums come from header files, hence virterror.h
files = libvirt_headers + ["virerror",
"virterror",
"libvirt-lxc",
"libvirt-qemu"]
if attrs['file'] in files:
enum(attrs['type'], attrs['name'], attrs['value'])
elif tag == "macro":
if "string" in attrs:
params.append((attrs['name'], attrs['string']))
def endElement(self, tag: str) -> None:
if debug:
print("end %s" % tag)
if tag == 'function':
# functions come from source files, hence 'virerror.c'
if self.function:
assert self.function_return
modules = libvirt_headers + ["event",
"virevent",
"virerror",
"virterror",
"libvirt-lxc",
"libvirt-qemu"]
files = ["python", "python-lxc", "python-qemu"]
if (self.function_module in modules or
self.function_file in files):
function(self.function, self.function_descr,
self.function_return, self.function_args,
self.function_file, self.function_module,
self.function_cond)
self.in_function = False
elif tag == 'arg':
if self.in_function:
self.function_args.append((self.function_arg_name,
self.function_arg_type,
self.function_arg_info))
elif tag == 'return':
if self.in_function:
self.function_return = (self.function_return_type,
self.function_return_info,
self.function_return_field)
elif tag == 'info':
str = ''.join(self._data)
if self.in_function:
self.function_descr = str
elif tag == 'cond':
str = ''.join(self._data)
if self.in_function:
self.function_cond = str
def function(name: str, desc: str, ret: ArgumentType, args: List[ArgumentType], file: str, module: str, cond: str) -> None:
if onlyOverrides and name not in functions:
return
if name == "virConnectListDomains":
name = "virConnectListDomainsID"
functions[name] = (desc, ret, args, file, module, cond)
def enum(type: str, name: str, value: EnumValue) -> None:
if (name.startswith('VIR_DOMAIN_EVENT_ID_') or
name.startswith('VIR_NETWORK_EVENT_ID_')):
event_ids.append(name)
if value == 'VIR_TYPED_PARAM_INT':
value = 1
elif value == 'VIR_TYPED_PARAM_UINT':
value = 2
elif value == 'VIR_TYPED_PARAM_LLONG':
value = 3
elif value == 'VIR_TYPED_PARAM_ULLONG':
value = 4
elif value == 'VIR_TYPED_PARAM_DOUBLE':
value = 5
elif value == 'VIR_TYPED_PARAM_BOOLEAN':
value = 6
elif value == 'VIR_DOMAIN_AFFECT_CURRENT':
value = 0
elif value == 'VIR_DOMAIN_AFFECT_LIVE':
value = 1
elif value == 'VIR_DOMAIN_AFFECT_CONFIG':
value = 2
elif value == 'VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK':
value = -2
elif value == 'VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_DEFAULT':
value = -1
elif value == 'VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_NOWAIT':
value = 0
if onlyOverrides and name not in enums[type]:
return
enums[type][name] = value
#######################################################################
#
# Some filtering rules to drop functions/types which should not
# be exposed as-is on the Python interface
#
#######################################################################
skipped_types = {
# 'int *': "usually a return type",
'virConnectDomainEventCallback': "No function types in python",
'virConnectDomainEventGenericCallback': "No function types in python",
'virConnectDomainEventRTCChangeCallback': "No function types in python",
'virConnectDomainEventWatchdogCallback': "No function types in python",
'virConnectDomainEventIOErrorCallback': "No function types in python",
'virConnectDomainEventGraphicsCallback': "No function types in python",
'virConnectDomainQemuMonitorEventCallback': "No function types in python",
'virStreamEventCallback': "No function types in python",
'virEventHandleCallback': "No function types in python",
'virEventTimeoutCallback': "No function types in python",
'virDomainBlockJobInfoPtr': "Not implemented yet",
}
#######################################################################
#
# Table of remapping to/from the python type or class to the C
# counterpart.
#
#######################################################################
py_types = {
'void': ('', '', '', ''),
'int': ('i', '', "int", "int"),
'long': ('l', '', "long", "long"),
'double': ('d', '', "double", "double"),
'unsigned int': ('I', '', "int", "int"),
'unsigned long': ('l', '', "long", "long"),
'long long': ('L', '', "longlong", "long long"),
'unsigned long long': ('L', '', "longlong", "long long"),
'unsigned char *': ('z', '', "charPtr", "char *"),
'char *': ('z', '', "charPtr", "char *"),
'const char *': ('z', '', "constcharPtr", "const char *"),
'size_t': ('n', '', "size_t", "size_t"),
'virDomainPtr': ('O', "virDomain", "virDomainPtr", "virDomainPtr"),
'virDomain *': ('O', "virDomain", "virDomainPtr", "virDomainPtr"),
'const virDomain *': ('O', "virDomain", "virDomainPtr", "virDomainPtr"),
'virNetworkPtr': ('O', "virNetwork", "virNetworkPtr", "virNetworkPtr"),
'virNetwork *': ('O', "virNetwork", "virNetworkPtr", "virNetworkPtr"),
'const virNetwork *': ('O', "virNetwork", "virNetworkPtr", "virNetworkPtr"),
'virNetworkPortPtr': ('O', "virNetworkPort", "virNetworkPortPtr", "virNetworkPortPtr"),
'virNetworkPort *': ('O', "virNetworkPort", "virNetworkPortPtr", "virNetworkPortPtr"),
'const virNetworkPort *': ('O', "virNetworkPort", "virNetworkPortPtr", "virNetworkPortPtr"),
'virInterfacePtr': ('O', "virInterface", "virInterfacePtr", "virInterfacePtr"),
'virInterface *': ('O', "virInterface", "virInterfacePtr", "virInterfacePtr"),
'const virInterface *': ('O', "virInterface", "virInterfacePtr", "virInterfacePtr"),
'virStoragePoolPtr': ('O', "virStoragePool", "virStoragePoolPtr", "virStoragePoolPtr"),
'virStoragePool *': ('O', "virStoragePool", "virStoragePoolPtr", "virStoragePoolPtr"),
'const virStoragePool *': ('O', "virStoragePool", "virStoragePoolPtr", "virStoragePoolPtr"),
'virStorageVolPtr': ('O', "virStorageVol", "virStorageVolPtr", "virStorageVolPtr"),
'virStorageVol *': ('O', "virStorageVol", "virStorageVolPtr", "virStorageVolPtr"),
'const virStorageVol *': ('O', "virStorageVol", "virStorageVolPtr", "virStorageVolPtr"),
'virConnectPtr': ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
'virConnect *': ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
'const virConnect *': ('O', "virConnect", "virConnectPtr", "virConnectPtr"),
'virNodeDevicePtr': ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
'virNodeDevice *': ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
'const virNodeDevice *': ('O', "virNodeDevice", "virNodeDevicePtr", "virNodeDevicePtr"),
'virSecretPtr': ('O', "virSecret", "virSecretPtr", "virSecretPtr"),
'virSecret *': ('O', "virSecret", "virSecretPtr", "virSecretPtr"),
'const virSecret *': ('O', "virSecret", "virSecretPtr", "virSecretPtr"),
'virNWFilterPtr': ('O', "virNWFilter", "virNWFilterPtr", "virNWFilterPtr"),
'virNWFilter *': ('O', "virNWFilter", "virNWFilterPtr", "virNWFilterPtr"),
'const virNWFilter *': ('O', "virNWFilter", "virNWFilterPtr", "virNWFilterPtr"),
'virNWFilterBindingPtr': ('O', "virNWFilterBinding", "virNWFilterBindingPtr", "virNWFilterBindingPtr"),
'virNWFilterBinding *': ('O', "virNWFilterBinding", "virNWFilterBindingPtr", "virNWFilterBindingPtr"),
'const virNWFilterBinding *': ('O', "virNWFilterBinding", "virNWFilterBindingPtr", "virNWFilterBindingPtr"),
'virStreamPtr': ('O', "virStream", "virStreamPtr", "virStreamPtr"),
'virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"),
'const virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"),
'virDomainCheckpointPtr': ('O', "virDomainCheckpoint", "virDomainCheckpointPtr", "virDomainCheckpointPtr"),
'virDomainCheckpoint *': ('O', "virDomainCheckpoint", "virDomainCheckpointPtr", "virDomainCheckpointPtr"),
'const virDomainCheckpoint *': ('O', "virDomainCheckpoint", "virDomainCheckpointPtr", "virDomainCheckpointPtr"),
'virDomainSnapshotPtr': ('O', "virDomainSnapshot", "virDomainSnapshotPtr", "virDomainSnapshotPtr"),
'virDomainSnapshot *': ('O', "virDomainSnapshot", "virDomainSnapshotPtr", "virDomainSnapshotPtr"),
'const virDomainSnapshot *': ('O', "virDomainSnapshot", "virDomainSnapshotPtr", "virDomainSnapshotPtr"),
} # type: Dict[str, Tuple[str, str, str, str]]
#######################################################################
#
# This part writes the C <-> Python stubs libvirt.[ch] and
# the table libvirt-export.c to add when registering the Python module
#
#######################################################################
# Class methods which are written by hand in libvirt.c but the Python-level
# code is still automatically generated (so they are not in skip_function()).
skip_impl = {
'virConnectGetVersion',
'virConnectGetLibVersion',
'virConnectListDomainsID',
'virConnectListDefinedDomains',
'virConnectListNetworks',
'virConnectListDefinedNetworks',
'virConnectListSecrets',
'virConnectListInterfaces',
'virConnectListStoragePools',
'virConnectListDefinedStoragePools',
'virConnectListStorageVols',
'virConnectListDefinedStorageVols',
'virConnectListDefinedInterfaces',
'virConnectListNWFilters',
'virDomainSnapshotListNames',
'virDomainSnapshotListChildrenNames',
'virConnGetLastError',
'virGetLastError',
'virDomainGetInfo',
'virDomainGetState',
'virDomainGetControlInfo',
'virDomainGetBlockInfo',
'virDomainGetJobInfo',
'virDomainGetJobStats',
'virNodeGetInfo',
'virNodeGetSecurityModel',
'virDomainGetSecurityLabel',
'virDomainGetSecurityLabelList',
'virDomainGetUUID',
'virDomainGetUUIDString',
'virDomainLookupByUUID',
'virNetworkGetUUID',
'virNetworkGetUUIDString',
'virNetworkLookupByUUID',
'virNetworkPortGetUUID',
'virNetworkPortGetUUIDString',
'virNetworkPortLookupByUUID',
'virDomainGetAutostart',
'virNetworkGetAutostart',
'virDomainBlockStats',
'virDomainInterfaceStats',
'virDomainMemoryStats',
'virNodeGetCellsFreeMemory',
'virDomainGetSchedulerType',
'virDomainGetSchedulerParameters',
'virDomainGetSchedulerParametersFlags',
'virDomainSetSchedulerParameters',
'virDomainSetSchedulerParametersFlags',
'virDomainSetBlkioParameters',
'virDomainGetBlkioParameters',
'virDomainSetMemoryParameters',
'virDomainGetMemoryParameters',
'virDomainSetNumaParameters',
'virDomainGetNumaParameters',
'virDomainGetVcpus',
'virDomainPinVcpu',
'virDomainPinVcpuFlags',
'virDomainGetVcpuPinInfo',
'virDomainGetEmulatorPinInfo',
'virDomainPinEmulator',
'virDomainGetIOThreadInfo',
'virDomainPinIOThread',
'virDomainSetIOThreadParams',
'virSecretGetValue',
'virSecretSetValue',
'virSecretGetUUID',
'virSecretGetUUIDString',
'virSecretLookupByUUID',
'virNWFilterGetUUID',
'virNWFilterGetUUIDString',
'virNWFilterLookupByUUID',
'virStoragePoolGetUUID',
'virStoragePoolGetUUIDString',
'virStoragePoolLookupByUUID',
'virStoragePoolGetInfo',
'virStorageVolGetInfo',
'virStorageVolGetInfoFlags',
'virStoragePoolGetAutostart',
'virStoragePoolListVolumes',
'virDomainBlockPeek',
'virDomainMemoryPeek',
'virEventRegisterImpl',
'virNodeListDevices',
'virNodeDeviceListCaps',
'virConnectBaselineCPU',
'virDomainRevertToSnapshot',
'virDomainSendKey',
'virNodeGetCPUStats',
'virNodeGetMemoryStats',
'virDomainGetBlockJobInfo',
'virDomainMigrateGetCompressionCache',
'virDomainMigrateGetMaxSpeed',
'virDomainMigrateGetMaxDowntime',
'virDomainBlockStatsFlags',
'virDomainSetBlockIoTune',
'virDomainGetBlockIoTune',
'virDomainSetInterfaceParameters',
'virDomainGetInterfaceParameters',
'virDomainGetCPUStats',
'virDomainGetDiskErrors',
'virNodeGetMemoryParameters',
'virNodeSetMemoryParameters',
'virConnectSetIdentity',
'virNodeGetCPUMap',
'virDomainMigrate3',
'virDomainMigrateToURI3',
'virConnectGetCPUModelNames',
'virNodeGetFreePages',
'virNetworkGetDHCPLeases',
'virDomainBlockCopy',
'virNodeAllocPages',
'virDomainGetFSInfo',
'virDomainInterfaceAddresses',
'virDomainGetPerfEvents',
'virDomainSetPerfEvents',
'virDomainGetGuestVcpus',
'virConnectBaselineHypervisorCPU',
'virDomainGetLaunchSecurityInfo',
'virDomainSetLaunchSecurityState',
'virNodeGetSEVInfo',
'virNetworkPortGetParameters',
'virNetworkPortSetParameters',
'virDomainGetGuestInfo',
'virDomainAuthorizedSSHKeysGet',
'virDomainAuthorizedSSHKeysSet',
'virDomainGetMessages',
'virNodeDeviceGetAutostart',
'virDomainSaveParams',
'virDomainRestoreParams',
'virDomainLxcOpenNamespace',
'virDomainQemuMonitorCommand',
'virDomainQemuAgentCommand',
}
# These are functions which the generator skips completely - no python
# or C code is generated. Generally should not be used for any more
# functions than those already listed
skip_function = {
'virConnectListDomains', # Python API is called virConnectListDomainsID for unknown reasons
'virConnSetErrorFunc', # Not used in Python API XXX is this a bug ?
'virResetError', # Not used in Python API XXX is this a bug ?
'virGetVersion', # Python C code is manually written
'virSetErrorFunc', # Python API is called virRegisterErrorHandler for unknown reasons
'virConnCopyLastError', # Python API is called virConnGetLastError instead
'virCopyLastError', # Python API is called virGetLastError instead
'virConnectOpenAuth', # Python C code is manually written
'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C
'virConnectDomainEventRegister', # overridden in virConnect.py
'virConnectDomainEventDeregister', # overridden in virConnect.py
'virConnectDomainEventRegisterAny', # overridden in virConnect.py
'virConnectDomainEventDeregisterAny', # overridden in virConnect.py
'virConnectNetworkEventRegisterAny', # overridden in virConnect.py
'virConnectNetworkEventDeregisterAny', # overridden in virConnect.py
'virConnectStoragePoolEventRegisterAny', # overridden in virConnect.py
'virConnectStoragePoolEventDeregisterAny', # overridden in virConnect.py
'virConnectNodeDeviceEventRegisterAny', # overridden in virConnect.py
'virConnectNodeDeviceEventDeregisterAny', # overridden in virConnect.py
'virConnectSecretEventRegisterAny', # overridden in virConnect.py
'virConnectSecretEventDeregisterAny', # overridden in virConnect.py
'virSaveLastError', # We have our own python error wrapper
'virFreeError', # Only needed if we use virSaveLastError
'virConnectListAllDomains', # overridden in virConnect.py
'virDomainListAllCheckpoints', # overridden in virDomain.py
'virDomainCheckpointListAllChildren', # overridden in virDomainCheckpoint.py
'virDomainListAllSnapshots', # overridden in virDomain.py
'virDomainSnapshotListAllChildren', # overridden in virDomainSnapshot.py
'virConnectListAllStoragePools', # overridden in virConnect.py
'virStoragePoolListAllVolumes', # overridden in virStoragePool.py
'virConnectListAllNetworks', # overridden in virConnect.py
'virNetworkListAllPorts', # overridden in virConnect.py
'virConnectListAllInterfaces', # overridden in virConnect.py
'virConnectListAllNodeDevices', # overridden in virConnect.py
'virConnectListAllNWFilters', # overridden in virConnect.py
'virConnectListAllNWFilterBindings', # overridden in virConnect.py
'virConnectListAllSecrets', # overridden in virConnect.py
'virConnectGetAllDomainStats', # overridden in virConnect.py
'virDomainListGetStats', # overridden in virConnect.py
'virStreamRecvAll', # Pure python libvirt-override-virStream.py
'virStreamSendAll', # Pure python libvirt-override-virStream.py
'virStreamRecv', # overridden in libvirt-override-virStream.py
'virStreamSend', # overridden in libvirt-override-virStream.py
'virStreamRecvHole', # overridden in libvirt-override-virStream.py
'virStreamSendHole', # overridden in libvirt-override-virStream.py
'virStreamRecvFlags', # overridden in libvirt-override-virStream.py
'virStreamSparseRecvAll', # overridden in libvirt-override-virStream.py
'virStreamSparseSendAll', # overridden in libvirt-override-virStream.py
'virConnectUnregisterCloseCallback', # overridden in virConnect.py
'virConnectRegisterCloseCallback', # overridden in virConnect.py
'virDomainCreateXMLWithFiles', # overridden in virConnect.py
'virDomainCreateWithFiles', # overridden in virDomain.py
'virDomainFSFreeze', # overridden in virDomain.py
'virDomainFSThaw', # overridden in virDomain.py
'virDomainGetTime', # overridden in virDomain.py
'virDomainSetTime', # overridden in virDomain.py
# 'Ref' functions have no use for bindings users.
"virConnectRef",
"virDomainRef",
"virInterfaceRef",
"virNetworkRef",
"virNetworkPortRef",
"virNodeDeviceRef",
"virSecretRef",
"virNWFilterRef",
"virNWFilterBindingRef",
"virStoragePoolRef",
"virStorageVolRef",
"virStreamRef",
"virDomainCheckpointRef",
"virDomainSnapshotRef",
# This functions shouldn't be called via the bindings (and even the docs
# contain an explicit warning to that effect). The equivalent should be
# implemented in pure python for each class
"virDomainGetConnect",
"virInterfaceGetConnect",
"virNetworkGetConnect",
"virNetworkPortGetNetwork",
"virSecretGetConnect",
"virNWFilterGetConnect",
"virStoragePoolGetConnect",
"virStorageVolGetConnect",
"virDomainCheckpointGetConnect",
"virDomainCheckpointGetDomain",
"virDomainSnapshotGetConnect",
"virDomainSnapshotGetDomain",
# only useful in C code, python code uses dict for typed parameters
"virTypedParamsAddBoolean",
"virTypedParamsAddDouble",
"virTypedParamsAddFromString",
"virTypedParamsAddInt",
"virTypedParamsAddLLong",
"virTypedParamsAddString",
"virTypedParamsAddUInt",
"virTypedParamsAddULLong",
"virTypedParamsClear",
"virTypedParamsFree",
"virTypedParamsGet",
"virTypedParamsGetBoolean",
"virTypedParamsGetDouble",
"virTypedParamsGetInt",
"virTypedParamsGetLLong",
"virTypedParamsGetString",
"virTypedParamsGetUInt",
"virTypedParamsGetULLong",
'virNetworkDHCPLeaseFree', # only useful in C, python code uses list
'virDomainStatsRecordListFree', # only useful in C, python uses dict
'virDomainFSInfoFree', # only useful in C, python code uses list
'virDomainIOThreadInfoFree', # only useful in C, python code uses list
'virDomainInterfaceFree', # only useful in C, python code uses list
"virDomainLxcEnterNamespace",
"virDomainLxcEnterSecurityLabel",
# "virDomainQemuAttach",
'virConnectDomainQemuMonitorEventRegister', # overridden in -qemu.py
'virConnectDomainQemuMonitorEventDeregister', # overridden in -qemu.py
'virDomainQemuMonitorCommandWithFiles', # overridden in -qemu.py
}
# Generate C code, but skip python impl
function_skip_python_impl = {
"virStreamFree", # Needed in custom virStream __del__, but free shouldn't
# be exposed in bindings
}
function_skip_index_one = {
"virDomainRevertToSnapshot",
}
def validate_function(name):
(desc, ret, args, file, mod, cond) = functions[name]
if name in skip_function:
return []
if name in skip_impl:
return []
failed = False
unknown = []
for a_name, a_type, a_info in args:
# This should be correct
if a_type[0:6] == "const ":
a_type = a_type[6:]
if a_type in skipped_types:
return []
if a_type not in py_types:
unknown.append(a_type)
r_type, r_info, r_field = ret
if r_type in skipped_types:
return []
if r_type != 'void' and r_type not in py_types:
unknown.append(r_type)
return unknown
def validate_functions():
unknown_types = defaultdict(list) # type: Dict[str, List[str]]
funcs_failed = [] # type: List[str]
for name in sorted(functions):
unknown = validate_function(name)
if unknown:
funcs_failed.append(name)
for thetype in unknown:
unknown_types[thetype].append(name)
if unknown_types:
print("Missing type converters: ")
for type, count in unknown_types.items():
print("%s:%d " % (type, len(count)))
for f in funcs_failed:
print("ERROR: failed %s" % f)
if funcs_failed:
return -1
return 0
def skip_both_impl(name: str) -> bool:
if name in skip_function:
return True
(desc, ret, args, file, mod, cond) = functions[name]
for a_name, a_type, a_info in args:
# This should be correct
if a_type[0:6] == "const ":
a_type = a_type[6:]
if a_type in skipped_types:
return True
r_type, r_info, r_field = ret
if r_type in skipped_types:
return True
return False
def skip_c_impl(name: str) -> bool:
if skip_both_impl(name):
return True
if name in skip_impl:
return True
return False
def skip_py_impl(name: str) -> bool:
if skip_both_impl(name):
return True
if name in function_skip_python_impl:
return True
return False
def print_function_wrapper(package: str, name: str, output: IO[str], export: IO[str], include: IO[str]) -> bool:
"""
:returns: True if generated, False if skipped
"""
(desc, ret, args, file, mod, cond) = functions[name]
if skip_c_impl(name):
return False
c_call = ""
format = ""
format_args = ""
c_args = ""
c_return = ""
c_convert = ""
num_bufs = 0
for a_name, a_type, a_info in args:
# This should be correct
if a_type[0:6] == "const ":
a_type = a_type[6:]
c_args += " %s %s;\n" % (a_type, a_name)
if a_type in py_types:
(f, t, n, c) = py_types[a_type]
if f:
format += f
if t:
format_args += ", &pyobj_%s" % (a_name)
c_args += " PyObject *pyobj_%s;\n" % (a_name)
c_convert += \
" %s = (%s) Py%s_Get(pyobj_%s);\n" % (
a_name, a_type, t, a_name)
else:
format_args += ", &%s" % (a_name)
if f == 't#':
format_args += ", &py_buffsize%d" % num_bufs
c_args += " int py_buffsize%d;\n" % num_bufs
num_bufs += 1
if c_call:
c_call += ", "
c_call += "%s" % (a_name)
else:
raise Exception("Unexpected type %s in function %s" % (a_type, name))
if format:
format += ":%s" % (name)
r_type, r_info, r_field = ret
if r_type == 'void':
if file == "python_accessor":
if args[1][1] == "char *":
c_call = "\n VIR_FREE(%s->%s);\n" % (
args[0][0], args[1][0])
c_call += " %s->%s = (%s)strdup((const xmlChar *)%s);\n" % (
args[0][0], args[1][0], args[1][1], args[1][0])
else:
c_call = "\n %s->%s = %s;\n" % (args[0][0], args[1][0],
args[1][0])
else:
c_call = "\n %s(%s);\n" % (name, c_call)
ret_convert = " Py_INCREF(Py_None);\n return Py_None;\n"
elif r_type in py_types:
(f, t, n, c) = py_types[r_type]
c_return = " %s c_retval;\n" % (r_type)
if file == "python_accessor" and r_field:
c_call = "\n c_retval = %s->%s;\n" % (args[0][0], r_field)
else:
c_call = "\n c_retval = %s(%s);\n" % (name, c_call)
ret_convert = " py_retval = libvirt_%sWrap((%s) c_retval);\n" % (n, c)
if n == "charPtr":
ret_convert += " free(c_retval);\n"
ret_convert += " return py_retval;\n"
else:
raise Exception("Unexpected type %s in function %s" % (r_type, name))
if cond:
include.write("#if %s\n" % cond)
export.write("#if %s\n" % cond)
output.write("#if %s\n" % cond)
include.write("PyObject * ")
include.write("%s_%s(PyObject *self, PyObject *args);\n" % (package, name))
export.write(" { (char *)\"%s\", %s_%s, METH_VARARGS, NULL },\n" %
(name, package, name))
if file == "python":
# Those have been manually generated
if cond:
include.write("#endif\n")
export.write("#endif\n")
output.write("#endif\n")
return True
if file == "python_accessor" and r_type != "void" and not r_field:
# Those have been manually generated
if cond:
include.write("#endif\n")
export.write("#endif\n")
output.write("#endif\n")
return True
output.write("PyObject *\n")
output.write("%s_%s(PyObject *self ATTRIBUTE_UNUSED," % (package, name))
output.write(" PyObject *args")
if format == "":
output.write(" ATTRIBUTE_UNUSED")
output.write(") {\n")
if r_type != 'void':
output.write(" PyObject *py_retval;\n")
if c_return:
output.write(c_return)
if c_args:
output.write(c_args)
if format:
output.write("\n if (!PyArg_ParseTuple(args, (char *)\"%s\"%s))\n" %
(format, format_args))
output.write(" return NULL;\n")
if c_convert:
output.write(c_convert + "\n")
output.write(" LIBVIRT_BEGIN_ALLOW_THREADS;")
output.write(c_call)
output.write(" LIBVIRT_END_ALLOW_THREADS;\n")
output.write(ret_convert)
output.write("}\n\n")
if cond:
include.write("#endif /* %s */\n" % cond)
export.write("#endif /* %s */\n" % cond)
output.write("#endif /* %s */\n" % cond)
return True
def print_c_pointer(classname: str, output: IO[str], export: IO[str], include: IO[str]) -> None:
output.write("PyObject *\n")
output.write("libvirt_%s_pointer(PyObject *self ATTRIBUTE_UNUSED, PyObject *args)\n" % classname)
output.write("{\n")
output.write(" %sPtr ptr;\n" % classname)
output.write(" PyObject *pyptr;\n")
output.write(" PyObject *pylong;\n")
output.write("\n")
output.write(" if (!PyArg_ParseTuple(args, (char *) \"O\", &pyptr))\n")
output.write(" return NULL;\n")
output.write(" ptr = (%sPtr) Py%s_Get(pyptr);\n" % (classname, classname))
output.write(" pylong = PyLong_FromVoidPtr(ptr);\n")
output.write(" return pylong;\n")
output.write("}\n")
output.write("\n")
include.write("PyObject *libvirt_%s_pointer(PyObject *self, PyObject *args);\n" % classname)
export.write(" { (char *)\"%s_pointer\", libvirt_%s_pointer, METH_VARARGS, NULL },\n" %
(classname, classname))
def load_apis(module: str, api_xml: str):
global onlyOverrides
try:
onlyOverrides = False
with open(api_xml) as stream:
parse(stream)
except IOError as msg:
print(api_xml, ":", msg)
sys.exit(1)
n = len(functions)
if not quiet:
print("Found %d functions in %s" % ((n), api_xml))
override_api_xml = "%s-override-api.xml" % module
py_types['pythonObject'] = ('O', "pythonObject", "pythonObject", "pythonObject")
try:
onlyOverrides = True
with open(override_api_xml) as stream:
parse(stream)
except IOError as msg:
print(override_api_xml, ":", msg)
if not quiet:
# XXX: This is not right, same function already in @functions
# will be overwritten.
print("Found %d functions in %s" % (len(functions) - n, override_api_xml))
def emit_c_code(module: str) -> None:
package = module.replace('-', '_')
nb_wrap = 0
header_file = "build/%s.h" % module
export_file = "build/%s-export.c" % module
wrapper_file = "build/%s.c" % module
include = open(header_file, "w")
include.write("/* Generated by generator.py */\n\n")
export = open(export_file, "w")
export.write("/* Generated by generator.py */\n\n")
wrapper = open(wrapper_file, "w")
wrapper.write("/* Generated by generator.py */\n\n")
wrapper.write("#include <Python.h>\n")
wrapper.write("#include <libvirt/%s.h>\n" % (module,))
wrapper.write("#include \"typewrappers.h\"\n")
wrapper.write("#include \"build/%s.h\"\n\n" % (module,))
for function in sorted(functions):
if print_function_wrapper(package, function, wrapper, export, include):
nb_wrap += 1
if module == "libvirt":
# Write C pointer conversion functions.
for classname in primary_classes:
print_c_pointer(classname, wrapper, export, include)
# Write define wrappers around event id enums, so that the
# preprocessor can see which enums were available.
for event_id in event_ids:
include.write("#define %s %s\n" % (event_id, event_id))
include.close()
export.close()
wrapper.close()
if not quiet:
print("Generated %d wrapper functions" % nb_wrap)
#######################################################################
#
# This part writes part of the Python front-end classes based on
# mapping rules between types and classes and also based on function
# renaming to get consistent function names at the Python level
#
#######################################################################
#
# The type automatically remapped to generated classes
# "C-type" -> (accessor, create, class, parent-class)
#
classes_type = {
"virDomainPtr": ("._o", "virDomain(%(p)s, _obj=%(o)s)", "virDomain", "virConnect"),
"virDomain *": ("._o", "virDomain(%(p)s, _obj=%(o)s)", "virDomain", "virConnect"),
"virNetworkPtr": ("._o", "virNetwork(%(p)s, _obj=%(o)s)", "virNetwork", "virConnect"),
"virNetwork *": ("._o", "virNetwork(%(p)s, _obj=%(o)s)", "virNetwork", "virConnect"),
"virNetworkPortPtr": ("._o", "virNetworkPort(%(p)s, _obj=%(o)s)", "virNetworkPort", "virNetwork"),
"virNetworkPort *": ("._o", "virNetworkPort(%(p)s, _obj=%(o)s)", "virNetworkPort", "virNetwork"),
"virInterfacePtr": ("._o", "virInterface(%(p)s, _obj=%(o)s)", "virInterface", "virConnect"),
"virInterface *": ("._o", "virInterface(%(p)s, _obj=%(o)s)", "virInterface", "virConnect"),
"virStoragePoolPtr": ("._o", "virStoragePool(%(p)s, _obj=%(o)s)", "virStoragePool", "virConnect"),
"virStoragePool *": ("._o", "virStoragePool(%(p)s, _obj=%(o)s)", "virStoragePool", "virConnect"),
"virStorageVolPtr": ("._o", "virStorageVol(%(p)s, _obj=%(o)s)", "virStorageVol", "virConnect"),
"virStorageVol *": ("._o", "virStorageVol(%(p)s, _obj=%(o)s)", "virStorageVol", "virConnect"),
"virNodeDevicePtr": ("._o", "virNodeDevice(%(p)s, _obj=%(o)s)", "virNodeDevice", "virConnect"),
"virNodeDevice *": ("._o", "virNodeDevice(%(p)s, _obj=%(o)s)", "virNodeDevice", "virConnect"),
"virSecretPtr": ("._o", "virSecret(%(p)s, _obj=%(o)s)", "virSecret", "virConnect"),
"virSecret *": ("._o", "virSecret(%(p)s, _obj=%(o)s)", "virSecret", "virConnect"),
"virNWFilterPtr": ("._o", "virNWFilter(%(p)s, _obj=%(o)s)", "virNWFilter", "virConnect"),
"virNWFilter *": ("._o", "virNWFilter(%(p)s, _obj=%(o)s)", "virNWFilter", "virConnect"),
"virNWFilterBindingPtr": ("._o", "virNWFilterBinding(%(p)s, _obj=%(o)s)", "virNWFilterBinding", "virConnect"),
"virNWFilterBinding *": ("._o", "virNWFilterBinding(%(p)s, _obj=%(o)s)", "virNWFilterBinding", "virConnect"),
"virStreamPtr": ("._o", "virStream(%(p)s, _obj=%(o)s)", "virStream", "virConnect"),
"virStream *": ("._o", "virStream(%(p)s, _obj=%(o)s)", "virStream", "virConnect"),
"virConnectPtr": ("._o", "virConnect(_obj=%(o)s)", "virConnect", ""),
"virConnect *": ("._o", "virConnect(_obj=%(o)s)", "virConnect", ""),
"virDomainCheckpointPtr": ("._o", "virDomainCheckpoint(%(p)s, _obj=%(o)s)", "virDomainCheckpoint", "virDomain"),
"virDomainCheckpoint *": ("._o", "virDomainCheckpoint(%(p)s, _obj=%(o)s)", "virDomainCheckpoint", "virDomain"),
"virDomainSnapshotPtr": ("._o", "virDomainSnapshot(%(p)s, _obj=%(o)s)", "virDomainSnapshot", "virDomain"),
"virDomainSnapshot *": ("._o", "virDomainSnapshot(%(p)s, _obj=%(o)s)", "virDomainSnapshot", "virDomain"),
}
primary_classes = ["virDomain", "virNetwork", "virNetworkPort",
"virInterface", "virStoragePool", "virStorageVol",
"virConnect", "virNodeDevice", "virSecret",
"virNWFilter", "virNWFilterBinding",
"virStream", "virDomainCheckpoint", "virDomainSnapshot"]
classes_destructors = {
"virDomain": "virDomainFree",
"virNetwork": "virNetworkFree",
"virNetworkPort": "virNetworkPortFree",
"virInterface": "virInterfaceFree",
"virStoragePool": "virStoragePoolFree",
"virStorageVol": "virStorageVolFree",
"virNodeDevice": "virNodeDeviceFree",
"virSecret": "virSecretFree",
"virNWFilter": "virNWFilterFree",
"virNWFilterBinding": "virNWFilterBindingFree",
"virDomainCheckpoint": "virDomainCheckpointFree",
"virDomainSnapshot": "virDomainSnapshotFree",
# We hand-craft __del__ for this one
# "virStream": "virStreamFree",
}
class_skip_connect_impl = {
"virConnect",
}
class_domain_impl = {
"virDomainCheckpoint",
"virDomainSnapshot",
}
class_network_impl = {
"virNetworkPort",
}
functions_noexcept = {
'virDomainGetID',
'virDomainGetName',
'virNetworkGetName',
'virInterfaceGetName',
'virStoragePoolGetName',
'virStorageVolGetName',
'virStorageVolGetkey',
'virNodeDeviceGetName',
'virNodeDeviceGetParent',
'virSecretGetUsageType',
'virSecretGetUsageID',
'virNWFilterGetName',
'virNWFilterBindingGetFilterName',
'virNWFilterBindingGetPortDev',
}
function_classes = {
"None": []
} # type: Dict[str, List[Tuple[int, str, str, ArgumentType, List[ArgumentType], str, str]]]
# Functions returning an integral type which need special rules to
# check for errors and raise exceptions.
functions_int_exception_test = {
'virDomainGetMaxMemory': "%s == 0",
}
functions_int_default_test = "%s == -1"
def is_integral_type(name: str) -> bool:
return re.search("^(unsigned)? ?(int|long)$", name) is not None
def is_optional_arg(info: str) -> bool:
return re.search(r"^\(?optional\)?", info) is not None
def is_python_noninteger_type(name: str) -> bool:
return name[-1:] == "*"
def nameFixup(name: str, classe: str, type: str, file: str) -> str:
# avoid a disastrous clash
listname = classe + "List"
l = len(classe)
if name[0:l] == listname:
func = name[l:]
func = func[0:1].lower() + func[1:]
elif name[0:16] == "virNetworkDefine":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:19] == "virNetworkCreateXML":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:16] == "virNetworkLookup":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:23] == "virNetworkPortCreateXML":
func = name[10:]
func = func[0:1].lower() + func[1:]
elif name[0:20] == "virNetworkPortLookup":
func = name[10:]
func = func[0:1].lower() + func[1:]
elif name[0:18] == "virInterfaceDefine":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:21] == "virInterfaceCreateXML":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:18] == "virInterfaceLookup":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:15] == "virSecretDefine":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:15] == "virSecretLookup":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:27] == "virNWFilterBindingCreateXML":
func = name[3:]
func = func[0:3].lower() + func[3:]
elif name[0:24] == "virNWFilterBindingLookup":
func = name[3:]
func = func[0:3].lower() + func[3:]
elif name[0:24] == "virNWFilterBindingDefine":
func = name[3:]
func = func[0:3].lower() + func[3:]
elif name[0:24] == "virNWFilterBindingLookup":
func = name[3:]
func = func[0:3].lower() + func[3:]
elif name[0:17] == "virNWFilterDefine":
func = name[3:]
func = func[0:3].lower() + func[3:]
elif name[0:17] == "virNWFilterLookup":
func = name[3:]
func = func[0:3].lower() + func[3:]
elif name[0:20] == "virStoragePoolDefine":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:23] == "virStoragePoolCreateXML":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:20] == "virStoragePoolLookup":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:19] == "virStorageVolDefine":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:19] == "virStorageVolLookup":
func = name[3:]
func = func[0:1].lower() + func[1:]
elif name[0:20] == "virDomainGetCPUStats":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:24] == "virDomainGetIOThreadInfo":
func = name[12:]
func = func[0:2].lower() + func[2:]
elif name[0:18] == "virDomainGetFSInfo":
func = name[12:]
func = func[0:2].lower() + func[2:]
elif name[0:12] == "virDomainGet":
func = name[12:]
func = func[0:1].lower() + func[1:]
elif name[0:31] == "virDomainCheckpointLookupByName":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:28] == "virDomainCheckpointCreateXML":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:19] == "virDomainCheckpoint":
func = name[19:]
func = func[0:1].lower() + func[1:]
elif name[0:29] == "virDomainSnapshotLookupByName":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:26] == "virDomainSnapshotListNames":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:28] == "virDomainSnapshotNumChildren":
func = name[17:]
func = func[0:1].lower() + func[1:]
elif name[0:20] == "virDomainSnapshotNum":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:26] == "virDomainSnapshotCreateXML":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:24] == "virDomainSnapshotCurrent":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:17] == "virDomainSnapshot":
func = name[17:]
func = func[0:1].lower() + func[1:]
elif name[0:9] == "virDomain":
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:17] == "virNetworkPortGet":
func = name[17:]
func = func[0:1].lower() + func[1:]
elif name[0:13] == "virNetworkGet":
func = name[13:]
func = func[0:1].lower() + func[1:]
func = func.replace("dHCP", "DHCP")
elif name[0:14] == "virNetworkPort":
func = name[14:]
func = func[0:1].lower() + func[1:]
elif name[0:10] == "virNetwork":
func = name[10:]
func = func[0:1].lower() + func[1:]
elif name[0:15] == "virInterfaceGet":
func = name[15:]
func = func[0:1].lower() + func[1:]
elif name[0:12] == "virInterface":
func = name[12:]
func = func[0:1].lower() + func[1:]
elif name[0:12] == 'virSecretGet':
func = name[12:]
func = func[0:1].lower() + func[1:]
elif name[0:9] == 'virSecret':
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:21] == 'virNWFilterBindingGet':
func = name[21:]
func = func[0:1].lower() + func[1:]
elif name[0:18] == 'virNWFilterBinding':
func = name[18:]
func = func[0:1].lower() + func[1:]
elif name[0:14] == 'virNWFilterGet':
func = name[14:]
func = func[0:1].lower() + func[1:]
elif name[0:11] == 'virNWFilter':
func = name[11:]
func = func[0:1].lower() + func[1:]
elif name[0:12] == 'virStreamNew':
func = "newStream"
elif name[0:9] == 'virStream':
func = name[9:]
func = func[0:1].lower() + func[1:]
elif name[0:17] == "virStoragePoolGet":
func = name[17:]
func = func[0:1].lower() + func[1:]
elif name[0:14] == "virStoragePool":
func = name[14:]
func = func[0:1].lower() + func[1:]
elif name[0:16] == "virStorageVolGet":
func = name[16:]
func = func[0:1].lower() + func[1:]
elif name[0:13] == "virStorageVol":
func = name[13:]
func = func[0:1].lower() + func[1:]
elif name[0:16] == "virNodeDeviceGet":
func = name[16].lower() + name[17:]
elif name[0:19] == "virNodeDeviceLookup":
func = name[3].lower() + name[4:]
elif name[0:22] == "virNodeDeviceCreateXML":
func = name[3].lower() + name[4:]
elif name[0:19] == "virNodeDeviceDefine":
func = name[3].lower() + name[4:]
elif name[0:13] == "virNodeDevice":
func = name[13].lower() + name[14:]
elif name[0:7] == "virNode":
func = name[7:]
func = func[0:1].lower() + func[1:]
elif name[0:10] == "virConnect":
func = name[10:]
func = func[0:1].lower() + func[1:]
elif name[0:3] == "xml":
func = name[3:]
func = func[0:1].lower() + func[1:]
else:
func = name
if func == "iD":
func = "ID"
if func == "uUID":
func = "UUID"
if func == "uUIDString":
func = "UUIDString"
if func == "oSType":
func = "OSType"
if func == "xMLDesc":
func = "XMLDesc"
if func == "mACString":
func = "MACString"
return func
def functionSortKey(info: Tuple) -> Tuple[str, str]:
(index, func, name, ret, args, filename, mod) = info
return func, filename
def writeDoc(module: str, name: str, args: List[ArgumentType], indent: str, output: IO) -> None:
if not functions[name][0]:
return
val = functions[name][0]
val = val.replace("NULL", "None")
sep = '\n%s' % (indent,)
output.write('%s"""%s """\n' % (indent, sep.join(val.splitlines())))
def emit_py_code(module: str) -> None:
package = module.replace('-', '_')
if module == "libvirt":
pymod = "libvirtmod"
cygmod = "cygvirtmod"
elif module == "libvirt-lxc":
pymod = "libvirtmod_lxc"
cygmod = "cygvirtmod_lxc"
elif module == "libvirt-qemu":
pymod = "libvirtmod_qemu"
cygmod = "cygvirtmod_qemu"
else:
raise Exception("Unknown module '%s'" % module)
for tinfo in classes_type.values():
function_classes[tinfo[2]] = []
#
# Build the list of C types to look for ordered to start
# with primary classes
#
ctypes = [] # type: List[str]
classes_list = [] # type: List[str]
ctypes_processed = set() # type: Set[str]
classes_processed = set() # type: Set[str]
for classe in primary_classes:
classes_list.append(classe)
classes_processed.add(classe)
for type, tinfo in classes_type.items():
if tinfo[2] == classe:
ctypes.append(type)
ctypes_processed.add(type)
for type, tinfo in classes_type.items():
if type in ctypes_processed:
continue
if tinfo[2] not in classes_processed:
classes_list.append(tinfo[2])
classes_processed.add(tinfo[2])
ctypes.append(type)
ctypes_processed.add(type)
for name, (desc, ret, args, file, mod, cond) in functions.items():
if skip_py_impl(name):
continue
for type in ctypes:
classe = classes_type[type][2]
if name[0:3] == "vir" and len(args) >= 1 and args[0][1] == type:
func = nameFixup(name, classe, type, file)
info = (0, func, name, ret, args, file, mod)
function_classes[classe].append(info)
break
elif name[0:3] == "vir" and len(args) >= 2 and args[1][1] == type \
and file != "python_accessor" and name not in function_skip_index_one:
func = nameFixup(name, classe, type, file)
info = (1, func, name, ret, args, file, mod)
function_classes[classe].append(info)
break
else:
func = nameFixup(name, "None", file, file)
info = (0, func, name, ret, args, file, mod)
function_classes['None'].append(info)
classes_file = "build/%s.py" % package
extra_file = "%s-override.py" % module
extra = None
classes = open(classes_file, "w")
if os.path.exists(extra_file):
extra = open(extra_file, "r")
classes.write("#\n")
classes.write("# WARNING WARNING WARNING WARNING\n")
classes.write("#\n")
classes.write("# This file is automatically written by generator.py. Any changes\n")
classes.write("# made here will be lost.\n")
classes.write("#\n")
classes.write("# To change the manually written methods edit %s-override.py\n" % (module,))
classes.write("# To change the automatically written methods edit generator.py\n")
classes.write("#\n")
classes.write("# WARNING WARNING WARNING WARNING\n")
classes.write("#\n")
classes.write("try:\n")
classes.write(" import %s # type: ignore\n" % pymod)
classes.write("except ImportError as lib_e:\n")
classes.write(" try:\n")
classes.write(" import %s as %s # type: ignore\n" % (cygmod, pymod))
classes.write(" except ImportError as cyg_e:\n")
classes.write(" if \"No module named\" in str(cyg_e):\n")
classes.write(" raise lib_e\n\n")
if module != "libvirt":
classes.write("import libvirt\n")
classes.write("\n")
if extra:
classes.write("# WARNING WARNING WARNING WARNING\n")
classes.write("#\n")
classes.write("# Manually written part of python bindings for %s\n" % module)
classes.writelines(extra.readlines())
classes.write("#\n")
classes.write("# WARNING WARNING WARNING WARNING\n")
classes.write("#\n")
classes.write("# Automatically written part of python bindings for %s\n" % module)
classes.write("#\n")
classes.write("# WARNING WARNING WARNING WARNING\n")
if extra:
extra.close()
if "None" in function_classes:
flist = function_classes["None"]
oldfile = ""
for (index, func, name, ret, args, file, mod) in sorted(flist, key=functionSortKey):
if file != oldfile:
classes.write("#\n# Functions from module %s\n#\n\n" % file)
oldfile = file
classes.write("def %s(" % func)
for n, (a_name, a_type, a_info) in enumerate(args):
if n != 0:
classes.write(", ")
classes.write("%s" % a_name)
if a_name == "flags" or is_optional_arg(a_info):
if is_integral_type(a_type):
classes.write("=0")
else:
classes.write("=None")
classes.write("):\n")
writeDoc(module, name, args, ' ', classes)
for a_name, a_type, a_info in args:
if a_type in classes_type:
classes.write(" if %s is None:\n"
" %s__o = None\n" %
(a_name, a_name))
classes.write(" else:\n"
" %s__o = %s%s\n" %
(a_name, a_name, classes_type[a_type][0]))
r_type, r_info, r_field = ret
if r_type != "void":
classes.write(" ret = ")
else:
classes.write(" ")
classes.write("%s.%s(" % (pymod, name))
for n, (a_name, a_type, a_info) in enumerate(args):
if n != 0:
classes.write(", ")
classes.write("%s" % a_name)
if a_type in classes_type:
classes.write("__o")
classes.write(")\n")
if r_type != "void":
if r_type in classes_type:
#
# Raise an exception
#
if name in functions_noexcept:
classes.write(" if ret is None:\n"
" return None\n")
else:
classes.write(
" if ret is None:\n"
" raise libvirtError('%s() failed')\n" %
(name))
tinfo = classes_type[r_type]
classes.write(" return ")
classes.write(tinfo[1] % {"o": "ret"})
classes.write("\n")
# For functions returning an integral type there are
# several things that we can do, depending on the
# contents of functions_int_*:
elif is_integral_type(r_type):
if name not in functions_noexcept:
test = functions_int_exception_test.get(name, functions_int_default_test) % ("ret",)
classes.write(
" if %s:\n"
" raise libvirtError('%s() failed')\n" %
(test, name))
classes.write(" return ret\n")
elif is_python_noninteger_type(r_type):
if name not in functions_noexcept:
classes.write(
" if ret is None:\n"
" raise libvirtError('%s() failed')\n" %
(name,))
classes.write(" return ret\n")
else:
classes.write(" return ret\n")
classes.write("\n")
modclasses = []
if module == "libvirt":
modclasses = classes_list
for classname in modclasses:
PARENTS = {
"virConnect": "self._conn",
"virDomain": "self._dom",
"virNetwork": "self._net",
classname: "self",
}
if classname == "None":
pass
else:
classes.write("class %s(object):\n" % (classname))
if classname == "virStorageVol":
classes.write(" # The size (in bytes) of buffer used in sendAll(),\n")
classes.write(" # recvAll(), sparseSendAll() and sparseRecvAll()\n")
classes.write(" # methods. This corresponds to the size of payload\n")
classes.write(" # of a stream packet.\n")
classes.write(" streamBufSize = 262120\n\n")
if classname in ["virDomain", "virNetwork", "virInterface", "virStoragePool",
"virStorageVol", "virNodeDevice", "virSecret", "virStream",
"virNWFilter", "virNWFilterBinding"]:
classes.write(" def __init__(self, conn, _obj=None):\n")
classes.write(" self._conn = conn\n")
elif classname in ["virDomainCheckpoint", "virDomainSnapshot"]:
classes.write(" def __init__(self, dom, _obj=None):\n")
classes.write(" self._dom = dom\n")
classes.write(" self._conn = dom.connect()\n")
elif classname in ["virNetworkPort"]:
classes.write(" def __init__(self, net, _obj=None) -> None:\n")
classes.write(" self._net = net\n")
classes.write(" self._conn = net.connect()\n")
else:
classes.write(" def __init__(self, _obj=None):\n")
classes.write(" if type(_obj).__name__ not in [\"PyCapsule\", \"PyCObject\"]:\n")
classes.write(" raise Exception(\"Expected a wrapped C Object but got %s\" % type(_obj))\n")
classes.write(" self._o = _obj\n\n")
destruct = None
if classname in classes_destructors:
classes.write(" def __del__(self):\n")
classes.write(" if self._o is not None:\n")
classes.write(" %s.%s(self._o)\n" %
(pymod, classes_destructors[classname]))
classes.write(" self._o = None\n\n")
destruct = classes_destructors[classname]
if classname not in class_skip_connect_impl:
# Build python safe 'connect' method
classes.write(" def connect(self):\n")
classes.write(" return self._conn\n\n")
if classname in class_domain_impl:
classes.write(" def domain(self):\n")
classes.write(" return self._dom\n\n")
if classname in class_network_impl:
classes.write(" def network(self):\n")
classes.write(" return self._net\n\n")
classes.write(" def c_pointer(self):\n")
classes.write(" \"\"\"Get C pointer to underlying object\"\"\"\n")
classes.write(" return %s.%s_pointer(self._o)\n\n" %
(pymod, classname))
flist = function_classes[classname]
oldfile = ""
for (index, func, name, ret, args, file, mod) in sorted(flist, key=functionSortKey):
#
# Do not provide as method the destructors for the class
# to avoid double free
#
if name == destruct:
continue
if file != oldfile:
if file == "python_accessor":
classes.write(" # accessors for %s\n" % (classname))
else:
classes.write(" #\n")
classes.write(" # %s functions from module %s\n" % (
classname, file))
classes.write(" #\n\n")
oldfile = file
classes.write(" def %s(self" % func)
for n, (a_name, a_type, a_info) in enumerate(args):
if n != index:
classes.write(", %s" % a_name)
if a_name == "flags" or is_optional_arg(a_info):
if is_integral_type(a_type):
classes.write("=0")
else:
classes.write("=None")
classes.write("):\n")
writeDoc(module, name, args, ' ', classes)
for n, (a_name, a_type, a_info) in enumerate(args):
if a_type in classes_type:
if n != index:
classes.write(" if %s is None:\n"
" %s__o = None\n" %
(a_name, a_name))
classes.write(" else:\n"
" %s__o = %s%s\n" %
(a_name, a_name, classes_type[a_type][0]))
r_type, r_info, r_field = ret
if r_type != "void":
classes.write(" ret = ")
else:
classes.write(" ")
classes.write("%s.%s(" % (pymod, name))
for n, (a_name, a_type, a_info) in enumerate(args):
if n != 0:
classes.write(", ")
if n != index:
classes.write("%s" % a_name)
if a_type in classes_type:
classes.write("__o")
else:
classes.write("self")
if a_type in classes_type:
classes.write(classes_type[a_type][0])
classes.write(")\n")
if name == "virConnectClose":
classes.write(" self._o = None\n")
# For functions returning object types:
if r_type != "void":
if r_type in classes_type:
#
# Raise an exception
#
if name in functions_noexcept:
classes.write(
" if ret is None:\n"
" return None\n")
else:
classes.write(
" if ret is None:\n"
" raise libvirtError('%s() failed')\n" %
(name,))
#
# generate the returned class wrapper for the object
#
tinfo = classes_type[r_type]
classes.write(" __tmp = ")
classes.write(tinfo[1] % {"o": "ret", "p": PARENTS[tinfo[3]]})
classes.write("\n")
#
# return the class
#
classes.write(" return __tmp\n")
# For functions returning an integral type there
# are several things that we can do, depending on
# the contents of functions_int_*:
elif is_integral_type(r_type):
if name not in functions_noexcept:
test = functions_int_exception_test.get(name, functions_int_default_test) % ("ret",)
classes.write(
" if %s:\n"
" raise libvirtError('%s() failed')\n" %
(test, name))
classes.write(" return ret\n")
elif is_python_noninteger_type(r_type):
if name not in functions_noexcept:
classes.write(
" if ret is None:\n"
" raise libvirtError('%s() failed')\n" %
(name,))
classes.write(" return ret\n")
else:
classes.write(" return ret\n")
classes.write("\n")
# Append "<classname>.py" to class def, iff it exists
class_override = "%s-override-%s.py" % (module, classname)
if os.path.exists(class_override):
extra = open(class_override, "r")
classes.write(" #\n")
classes.write(" # %s methods from %s.py (hand coded)\n" % (classname, classname))
classes.write(" #\n")
cached = None
# Since we compile with older libvirt, we don't want to pull
# in manually written python methods which call C methods
# that don't exist. This code attempts to detect which
# methods to skip by looking at the libvirtmod.XXXX calls
def shouldSkip(lines: List[str]) -> bool:
for line in lines:
offset = line.find(pymod + ".")
if offset != -1:
func = line[offset + 11:]
offset = func.find("(")
func = func[0:offset]
if not skip_c_impl(func) and func != "virConnectListDomains":
return True
return False
for line in extra.readlines():
offset = line.find(" def ")
if offset != -1:
name = line[offset + 5:]
offset = name.find("(")
name = name[0:offset]
if cached:
if not shouldSkip(cached):
classes.writelines(cached)
if name == "__del__":
cached = None
classes.write(line)
else:
cached = [line]
else:
if cached:
cached.append(line)
else:
classes.write(line)
if cached is not None and not shouldSkip(cached):
classes.writelines(cached)
classes.write("\n")
extra.close()
direct_functions = {}
if module != "libvirt":
direct_functions = functions
classes.write("#\n# Functions from module %s\n#\n\n" % module)
#
# Generate functions directly, no classes
#
for name, (desc, ret, args, file, mod, cond) in sorted(direct_functions.items()):
if skip_py_impl(name):
continue
func = nameFixup(name, 'None', '', '')
classes.write("def %s(" % func)
for n, (a_name, a_type, a_info) in enumerate(args):
if n != 0:
classes.write(", ")
classes.write("%s" % a_name)
classes.write("):\n")
writeDoc(module, name, args, ' ', classes)
r_type, r_info, r_field = ret
if r_type != "void":
classes.write(" ret = ")
else:
classes.write(" ")
classes.write("%s.%s(" % (pymod, name))
conn = None
for n, (a_name, a_type, a_info) in enumerate(args):
if a_type == "virConnectPtr":
conn = a_name
if n != 0:
classes.write(", ")
if a_type in ["virDomainPtr", "virConnectPtr"]:
# FIXME: This might have problem if the function
# has multiple args which are objects.
classes.write("%s.%s" % (a_name, "_o"))
else:
classes.write("%s" % a_name)
classes.write(")\n")
if r_type != "void":
classes.write(" if ret is None:\n"
" raise libvirt.libvirtError('%s() failed')\n" % (name,))
if r_type == "virDomainPtr":
classes.write(" __tmp = libvirt.virDomain(%s, _obj=ret)\n" % (conn,))
classes.write(" return __tmp\n")
else:
classes.write(" return ret\n")
classes.write("\n")
#
# Generate enum constants
#
def enumsSortKey(data: Tuple[str, EnumValue]) -> Tuple[Union[int, float], str]:
try:
value = int(data[1]) # type: Union[int, float]
except ValueError:
value = float('inf')
return value, data[0]
# Resolve only one level of reference
def resolveEnum(enum: EnumType, data: EnumType) -> EnumType:
for name, val in enum.items():
try:
int(val)
except ValueError:
enum[name] = data[val] # type: ignore
return enum
enumvals = list(enums.items())
# convert list of dicts to one dict
enumData = {} # type: EnumType
for type, enum in enumvals:
enumData.update(enum)
for type, enum in sorted(enumvals):
classes.write("# %s\n" % type)
items = sorted(resolveEnum(enum, enumData).items(), key=enumsSortKey)
if items[-1][0].endswith('_LAST'):
del items[-1]
for name, value in items:
classes.write("%s = %s\n" % (name, value))
classes.write("\n")
if params:
classes.write("# typed parameter names\n")
for name, value in params:
classes.write("%s = \"%s\"\n" % (name, value))
classes.close()
if sys.argv[1] not in ["libvirt", "libvirt-lxc", "libvirt-qemu"]:
print("ERROR: unknown module %s" % sys.argv[1])
sys.exit(1)
load_apis(sys.argv[1], sys.argv[2])
if validate_functions() < 0:
sys.exit(1)
quiet = False
if not os.path.exists("build"):
os.mkdir("build")
output = None
if len(sys.argv) == 4:
output = sys.argv[3]
if output == "c" or output is None:
emit_c_code(sys.argv[1])
if output == "py" or output is None:
emit_py_code(sys.argv[1])
sys.exit(0)