VirtualBox

Changeset 103028 in vbox for trunk/src/VBox/Main/glue


Ignore:
Timestamp:
Jan 24, 2024 3:53:59 PM (12 months ago)
Author:
vboxsync
Message:

Main/Python: Big revamp to modernize our vboxapi Python package to now use a so-called src-layout, which also can be used with pip directly. Also added compatibility w/ Python 3.12 where distutils are not shipped anymore. We also now do have automatic linting for our code, which hopefully should improve quality in this area. Moved some Python-related files into an own sub folder so that it's more clear to which these belong to. The "sdk/installer" directories also have an own "python" sub directory where the stuff resides now. The Windows installer also uses "sdk/installer" instead of "sdk/install", to match the other platforms. bugref:10579

Location:
trunk/src/VBox/Main/glue/python
Files:
3 added
1 copied
3 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/glue/python/Config.kmk

    r102983 r103028  
    11# $Id$
    22## @file
    3 # kBuild Configuration file for VBoxShell.
     3# kBuild Configuration file for Main glue for Python.
    44#
    55
     
    2626#
    2727
    28 VBOX_VBOXSHELL_CONFIG_KMK_INCLUDED = 1
     28VBOX_MAIN_GLUE_PYTHON_CONFIG_KMK_INCLUDED = 1
    2929
    3030# Include the top-level configure file.
     
    3636# Globals
    3737#
    38 VBOX_PATH_VBOXSHELL_SRC := $(PATH_ROOT)/src/VBox/Frontends/VBoxShell
     38VBOX_PATH_MAIN_GLUE_PYTHON_SRC := $(PATH_ROOT)/src/VBox/Main/glue/python
    3939
    4040#
    4141# List of python sources that should be linted.
    4242#
    43 VBOX_VBOXSHELL_PYTHON_SOURCES    :=
    44 VBOX_VBOXSHELL_PYLINT_TARGETS    :=
     43VBOX_MAIN_GLUE_PYTHON_PYTHON_SOURCES :=
     44VBOX_MAIN_GLUE_PYTHON_PYLINT_TARGETS :=
    4545
    4646ifdef VBOX_WITH_PYLINT
     
    5151# Process python sources.
    5252#
    53 if1of ($(KBUILD_TARGET), win os2)
    54  VBOX_PYTHONPATH_VBOXSHELL = $(PYTHONPATH);$(VBOX_PATH_VBOXSHELL_SRC)
    55 else
    56  VBOX_PYTHONPATH_VBOXSHELL = $(PYTHONPATH):$(VBOX_PATH_VBOXSHELL_SRC)
    57 endif
     53VBOX_PYTHONPATH_MAIN_GLUE_PYTHON = $(PYTHONPATH):$(VBOX_PATH_MAIN_GLUE_PYTHON_SRC)
    5854BLDDIRS += $(PATH_TARGET)/pylint
    5955
    60 define def_vbox_vboxshell_py_check
     56define def_vbox_main_glue_python_py_check
    6157 $(eval name:=$(basename $(notdir $(py))))
    6258
     
    6662 ifdef VBOX_WITH_PYLINT
    6763        $(QUIET2)$(call MSG_L1,Subjecting $(py) to pylint...)
    68         $(QUIET)$(REDIRECT) -C "$(dir $(py))" -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_VBOXSHELL)" -- \
    69                 $(VBOX_PYLINT) --rcfile=$(VBOX_PATH_VBOXSHELL_SRC)/pylintrc $$(VBOX_PYLINT_FLAGS) $$($(py)_VBOX_PYLINT_FLAGS) ./$(notdir $(py))
     64        $(QUIET)$(REDIRECT) -C "$(dir $(py))" -E LC_ALL=C -E PYTHONPATH="$(VBOX_PYTHONPATH_MAIN_GLUE_PYTHON)" -- \
     65                $(VBOX_PYLINT) --rcfile=$(VBOX_PATH_MAIN_GLUE_PYTHON_SRC)/pylintrc $$(VBOX_PYLINT_FLAGS) $$($(py)_VBOX_PYLINT_FLAGS) ./$(notdir $(py))
    7066 endif
    7167        $(QUIET)$(APPEND) -t "$(PATH_TARGET)/pylint/$(name).o"
    7268 TESTING += $(name)-py-phony.o
    73  VBOX_VBOXSHELL_PYLINT_TARGETS    += $(PATH_TARGET)/pylint/$(name).o
    74 endef # def_vbox_vboxshell_py_check
     69 VBOX_MAIN_GLUE_PYTHON_PYLINT_TARGETS    += $(PATH_TARGET)/pylint/$(name).o
     70endef # def_vbox_main_glue_python_py_check
    7571
    7672
    77 define def_vbox_vboxshell_process_python_sources
     73define def_vbox_main_glue_python_process_python_sources
    7874 if $(words $(_SUB_MAKEFILE_STACK)) <= 0 || "$1" == "FORCE"
    79   $(foreach py, $(VBOX_VBOXSHELL_PYTHON_SOURCES), $(eval $(def_vbox_vboxshell_py_check)))
     75  $(foreach py, $(VBOX_MAIN_GLUE_PYTHON_PYTHON_SOURCES), $(eval $(def_vbox_main_glue_python_py_check)))
    8076 endif
    8177endef
  • trunk/src/VBox/Main/glue/python/vboxapi.py

    r103027 r103028  
    11# -*- coding: utf-8 -*-
    22# $Id$
     3# pylint: disable=import-error -- for cross-platform Win32 imports
     4# pylint: disable=unused-import
     5# pylint: disable=protected-access -- for XPCOM _xpcom member
    36"""
    47VirtualBox Python API Glue.
     
    4952
    5053if sys.version_info >= (3, 0):
    51     xrange = range
    52     long = int
     54    xrange = range # pylint: disable=invalid-name
     55    long = int     # pylint: disable=invalid-name
    5356
    5457#
     
    5659#
    5760import platform
    58 VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
    59 VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
    60 
    61 if VBoxBinDir is None:
     61g_sVBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
     62g_sVBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
     63
     64if g_sVBoxBinDir is None:
    6265    if platform.system() == 'Darwin':
    63         VBoxBinDir = '/Applications/VirtualBox.app/Contents/MacOS'
     66        g_sVBoxBinDir = '/Applications/VirtualBox.app/Contents/MacOS'
    6467    else: # Will be set by the installer
    65         VBoxBinDir = "%VBOX_INSTALL_PATH%"
     68        g_sVBoxBinDir = "%VBOX_INSTALL_PATH%"
    6669else:
    67     VBoxBinDir = os.path.abspath(VBoxBinDir)
    68 
    69 if VBoxSdkDir is None:
     70    g_sVBoxBinDir = os.path.abspath(g_sVBoxBinDir)
     71
     72if g_sVBoxSdkDir is None:
    7073    if platform.system() == 'Darwin':
    71         VBoxSdkDir = '/Applications/VirtualBox.app/Contents/MacOS/sdk'
     74        g_sVBoxSdkDir = '/Applications/VirtualBox.app/Contents/MacOS/sdk'
    7275    else: # Will be set by the installer
    73         VBoxSdkDir = "%VBOX_SDK_PATH%"
     76        g_sVBoxSdkDir = "%VBOX_SDK_PATH%"
    7477else:
    75     VBoxSdkDir = os.path.abspath(VBoxSdkDir)
    76 
    77 os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir
    78 os.environ["VBOX_SDK_PATH"] = VBoxSdkDir
    79 sys.path.append(VBoxBinDir)
     78    g_sVBoxSdkDir = os.path.abspath(g_sVBoxSdkDir)
     79
     80os.environ["VBOX_PROGRAM_PATH"] = g_sVBoxBinDir
     81os.environ["VBOX_SDK_PATH"] = g_sVBoxSdkDir
     82sys.path.append(g_sVBoxBinDir)
    8083
    8184
     
    104107        """
    105108        self.mgr = mgr
    106         self.isMscom = (mgr.type == 'MSCOM')
     109        self.isMscom = mgr.type == 'MSCOM'
    107110        self.collector = vbox.performanceCollector
    108111
     
    138141        # parameters (see #3953) for MSCOM.
    139142        if self.isMscom:
    140             (values, names, objects, names_out, objects_out, units, scales, sequence_numbers,
     143            (values, names, objects, names_out, objects_out, units, scales, _sequence_numbers,
    141144             indices, lengths) = self.collector.queryMetricsData(names, objects)
    142145        else:
    143             (values, names_out, objects_out, units, scales, sequence_numbers,
     146            (values, names_out, objects_out, units, scales, _sequence_numbers,
    144147             indices, lengths) = self.collector.queryMetricsData(names, objects)
    145148        out = []
    146         for i in xrange(0, len(names_out)):
     149        for i in enumerate(names_out):
    147150            scale = int(scales[i])
    148151            if scale != 1:
     
    165168# Attribute hacks.
    166169#
    167 def ComifyName(name):
     170def comifyName(name):
    168171    return name[0].capitalize() + name[1:]
    169172
     
    171174## This is for saving the original DispatchBaseClass __getattr__ and __setattr__
    172175#  method references.
    173 _g_dCOMForward = {
    174     'getattr': None,
    175     'setattr': None,
    176 }
     176_g_dCOMForward = {}
    177177
    178178
     
    193193    # Slow path.
    194194    try:
    195         return _g_dCOMForward['getattr'](self, ComifyName(sAttr))
     195        return _g_dCOMForward['getattr'](self, comifyName(sAttr))
    196196    except AttributeError:
    197197        return _g_dCOMForward['getattr'](self, sAttr)
     
    201201    """ Our setattr replacement for DispatchBaseClass. """
    202202    try:
    203         return _g_dCOMForward['setattr'](self, ComifyName(sAttr), oValue)
     203        return _g_dCOMForward['setattr'](self, comifyName(sAttr), oValue)
    204204    except AttributeError:
    205205        return _g_dCOMForward['setattr'](self, sAttr, oValue)
     
    331331        return None
    332332
    333     def queryInterface(self, oIUnknown, sClassName):
     333    def queryInterface(self, _oIUnknown, _sClassName):
    334334        """
    335335        IUnknown::QueryInterface wrapper.
     
    344344    #
    345345
    346     def xcptGetStatus(self, oXcpt):
     346    def xcptGetStatus(self, _oXcpt):
    347347        """
    348348        Returns the COM status code from the VBox API given exception.
    349349        """
    350         return None
    351 
    352     def xcptIsDeadInterface(self, oXcpt):
     350        raise AttributeError
     351
     352    def xcptIsDeadInterface(self, _oXcpt):
    353353        """
    354354        Returns True if the exception indicates that the interface is dead, False if not.
     
    379379        return False
    380380
    381     def xcptGetMessage(self, oXcpt):
     381    def xcptGetMessage(self, _oXcpt):
    382382        """
    383383        Returns the best error message found in the COM-like exception.
     
    407407            if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'):
    408408                oAttr = getattr(oSrc, sAttr)
    409                 if type(oAttr) is int:
     409                if isinstance(oAttr, int):
    410410                    setattr(oDst, sAttr, oAttr)
    411411        return oDst
     
    447447
    448448        self.winerror = winerror
     449        self.oHandle  = None;
    449450
    450451        # Setup client impersonation in COM calls.
     
    472473        # Hack the COM dispatcher base class so we can modify method and
    473474        # attribute names to match those in xpcom.
    474         if _g_dCOMForward['setattr'] is None:
     475        if 'getattr' not in _g_dCOMForward:
    475476            _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
     477            setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr)
     478
     479        if 'setattr' not in _g_dCOMForward:
    476480            _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
    477             setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr)
    478481            setattr(DispatchBaseClass, '__setattr__', _CustomSetAttr)
    479482
     
    543546
    544547    def getArray(self, oInterface, sAttrib):
    545         return oInterface.__getattr__(sAttrib)
     548        return getattr(oInterface, sAttrib)
    546549
    547550    def setArray(self, oInterface, sAttrib, aoArray):
     
    558561        aPropMapGet = getattr(oInterface, '_prop_map_get_')
    559562        aPropMapPut = getattr(oInterface, '_prop_map_put_')
    560         sComAttrib  = sAttrib if sAttrib in aPropMapGet else ComifyName(sAttrib)
     563        sComAttrib  = sAttrib if sAttrib in aPropMapGet else comifyName(sAttrib)
    561564        try:
    562             aArgs, aDefaultArgs = aPropMapPut[sComAttrib]
    563             aGetArgs            = aPropMapGet[sComAttrib]
     565            aArgs, _aDefaultArgs = aPropMapPut[sComAttrib]
     566            aGetArgs             = aPropMapGet[sComAttrib]
    564567        except KeyError: # fallback.
    565             return oInterface.__setattr__(sAttrib, aoArray)
     568            return setattr(oInterface, sAttrib, aoArray)
    566569
    567570        import pythoncom
     
    572575                            (aGetArgs[2],),             # argTypes - trick: we get the type from the getter.
    573576                            aoArray,)                   # The array
     577        return True
    574578
    575579    def initPerThread(self):
     
    582586
    583587    def createListener(self, oImplClass, dArgs):
    584         if True:
    585             raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
    586                             'returns new gateway objects all the time, thus breaking EventQueue '
    587                             'assumptions about the listener interface pointer being constants between calls ')
    588         # Did this code ever really work?
    589         d = {}
    590         d['BaseClass'] = oImplClass
    591         d['dArgs'] = dArgs
    592         d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
    593         d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR
    594         d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR
    595         str_ = ""
    596         str_ += "import win32com.server.util\n"
    597         str_ += "import pythoncom\n"
    598 
    599         str_ += "class ListenerImpl(BaseClass):\n"
    600         str_ += "   _com_interfaces_ = ['IEventListener']\n"
    601         str_ += "   _typelib_guid_ = tlb_guid\n"
    602         str_ += "   _typelib_version_ = tlb_major, tlb_minor\n"
    603         str_ += "   _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
    604         # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
    605         str_ += "   _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
    606 
    607         # capitalized version of listener method
    608         str_ += "   HandleEvent=BaseClass.handleEvent\n"
    609         str_ += "   def __init__(self): BaseClass.__init__(self, dArgs)\n"
    610         str_ += "result = win32com.server.util.wrap(ListenerImpl())\n"
    611         exec(str_, d, d)
    612         return d['result']
    613 
    614     def waitForEvents(self, timeout):
     588        raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
     589                        'returns new gateway objects all the time, thus breaking EventQueue '
     590                        'assumptions about the listener interface pointer being constants between calls ')
     591
     592    def waitForEvents(self, cMsTimeout):
    615593        from win32api import GetCurrentThreadId
    616594        from win32event import INFINITE
     
    619597        import types
    620598
    621         if not isinstance(timeout, int):
     599        if not isinstance(cMsTimeout, int):
    622600            raise TypeError("The timeout argument is not an integer")
    623601        if self.tid != GetCurrentThreadId():
    624602            raise Exception("wait for events from the same thread you inited!")
    625603
    626         if timeout < 0:
     604        if cMsTimeout < 0:
    627605            cMsTimeout = INFINITE
    628         else:
    629             cMsTimeout = timeout
    630606        rc = MsgWaitForMultipleObjects(self.aoHandles, 0, cMsTimeout, QS_ALLINPUT)
    631607        if WAIT_OBJECT_0 <= rc < WAIT_OBJECT_0 + len(self.aoHandles):
     
    757733    def __init__(self, dParams):
    758734        PlatformBase.__init__(self, dParams)
    759         sys.path.append(VBoxSdkDir + '/bindings/xpcom/python/')
     735        sys.path.append(g_sVBoxSdkDir + '/bindings/xpcom/python/')
    760736        import xpcom.vboxxpcom
    761737        import xpcom
     
    776752
    777753    def getArray(self, oInterface, sAttrib):
    778         return oInterface.__getattr__('get' + ComifyName(sAttrib))()
     754        return getattr(oInterface, 'get' + comifyName(sAttrib));
    779755
    780756    def setArray(self, oInterface, sAttrib, aoArray):
    781         return oInterface.__getattr__('set' + ComifyName(sAttrib))(aoArray)
     757        return setattr(oInterface, 'set' + comifyName(sAttrib), aoArray)
    782758
    783759    def initPerThread(self):
     
    790766
    791767    def createListener(self, oImplClass, dArgs):
    792         d = {}
    793         d['BaseClass'] = oImplClass
    794         d['dArgs'] = dArgs
    795         str = ""
    796         str += "import xpcom.components\n"
    797         str += "class ListenerImpl(BaseClass):\n"
    798         str += "   _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
    799         str += "   def __init__(self): BaseClass.__init__(self, dArgs)\n"
    800         str += "result = ListenerImpl()\n"
    801         exec(str, d, d)
    802         return d['result']
    803 
    804     def waitForEvents(self, timeout):
     768        notDocumentedDict = {}
     769        notDocumentedDict['BaseClass'] = oImplClass
     770        notDocumentedDict['dArgs'] = dArgs
     771        sEval = ""
     772        sEval += "import xpcom.components\n"
     773        sEval += "class ListenerImpl(BaseClass):\n"
     774        sEval += "   _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
     775        sEval += "   def __init__(self): BaseClass.__init__(self, dArgs)\n"
     776        sEval += "result = ListenerImpl()\n"
     777        exec(sEval, notDocumentedDict, notDocumentedDict) # pylint: disable=exec-used
     778        return notDocumentedDict['result']
     779
     780    def waitForEvents(self, cMsTimeout):
    805781        import xpcom
    806         return xpcom._xpcom.WaitForEvents(timeout)
     782        return xpcom._xpcom.WaitForEvents(cMsTimeout)
    807783
    808784    def interruptWaitEvents(self):
     
    868844        PlatformBase.__init__(self, dParams)
    869845        # Import web services stuff.  Fix the sys.path the first time.
    870         sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')
     846        sWebServLib = os.path.join(g_sVBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')
    871847        if sWebServLib not in sys.path:
    872848            sys.path.append(sWebServLib)
     
    904880
    905881    def getArray(self, oInterface, sAttrib):
    906         return oInterface.__getattr__(sAttrib)
     882        return getattr(oInterface, sAttrib)
    907883
    908884    def setArray(self, oInterface, sAttrib, aoArray):
    909         return oInterface.__setattr__(sAttrib, aoArray)
    910 
    911     def waitForEvents(self, timeout):
     885        return setattr(oInterface, sAttrib, aoArray)
     886
     887    def waitForEvents(self, _timeout):
    912888        # Webservices cannot do that yet
    913889        return 2
    914890
    915     def interruptWaitEvents(self, timeout):
     891    def interruptWaitEvents(self):
    916892        # Webservices cannot do that yet
    917893        return False
     
    924900
    925901    def queryInterface(self, oIUnknown, sClassName):
    926         d = {}
    927         d['oIUnknown'] = oIUnknown
    928         str = ""
    929         str += "from VirtualBox_wrappers import " + sClassName + "\n"
    930         str += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n"
     902        notDocumentedDict = {}
     903        notDocumentedDict['oIUnknown'] = oIUnknown
     904        sEval = ""
     905        sEval += "from VirtualBox_wrappers import " + sClassName + "\n"
     906        sEval += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n"
    931907        # wrong, need to test if class indeed implements this interface
    932         exec(str, d, d)
    933         return d['result']
     908        exec(sEval, notDocumentedDict, notDocumentedDict) # pylint: disable=exec-used
     909        return notDocumentedDict['result']
    934910
    935911    #
     
    970946# platforms at the same time, so this should be sufficent for most uses and
    971947# be way simpler to use than VirtualBoxManager::oXcptClass.
    972 CurXcptClass = None
     948g_curXcptClass = None
    973949
    974950
     
    10291005        ## The exception class for the selected platform.
    10301006        self.oXcptClass = self.platform.xcptGetBaseXcpt()
    1031         global CurXcptClass
    1032         CurXcptClass = self.oXcptClass
     1007        global g_curXcptClass
     1008        g_curXcptClass = self.oXcptClass
    10331009
    10341010        # Get the virtualbox singleton.
    10351011        try:
    1036             vbox = self.platform.getVirtualBox()
     1012            self.platform.getVirtualBox()
    10371013        except NameError:
    10381014            print("Installation problem: check that appropriate libs in place")
     
    11571133        Returns the VirtualBox binary directory.
    11581134        """
    1159         global VBoxBinDir
    1160         return VBoxBinDir
     1135        return g_sVBoxBinDir
    11611136
    11621137    def getSdkDir(self):
     
    11641139        Returns the VirtualBox SDK directory.
    11651140        """
    1166         global VBoxSdkDir
    1167         return VBoxSdkDir
     1141        return g_sVBoxSdkDir
    11681142
    11691143    def getEnumValueName(self, sEnumTypeNm, oEnumValue, fTypePrefix = False):
     
    11821156            if len(dNamedValues) > 0:
    11831157
    1184                 dValueNames = dict();
     1158                dValueNames = {};
    11851159                for sName in dNamedValues:
    11861160                    dValueNames[dNamedValues[sName]] = sName;
     
    12661240        # Build the dictionary on demand.
    12671241        if self._dErrorValToName is None:
    1268             dErrorValToName = dict()
     1242            dErrorValToName = {}
    12691243            for sKey in dir(self.statuses):
    12701244                if sKey[0].isupper():
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette