From deb989dd6d1f86e74864131be50ed92d8d43768c Mon Sep 17 00:00:00 2001 From: Kenneth Koski Date: Mon, 29 Feb 2016 22:22:10 -0600 Subject: Improving uno.py code style * Fixing pep8 violations * Improving comments, docstrings, and unifying coding style * Using functionality copied from six library for Python 2/3 compatibility * Using standard traceback formatting Change-Id: I62bd0e8513ffc59202163002fa4adea3d92572c3 Reviewed-on: https://gerrit.libreoffice.org/22848 Tested-by: Jenkins Reviewed-by: Michael Stahl Tested-by: Michael Stahl --- pyuno/source/module/uno.py | 619 +++++++++++++++++++++++++++------------------ 1 file changed, 367 insertions(+), 252 deletions(-) (limited to 'pyuno/source') diff --git a/pyuno/source/module/uno.py b/pyuno/source/module/uno.py index 1526aac9d8df..29cc6841131c 100644 --- a/pyuno/source/module/uno.py +++ b/pyuno/source/module/uno.py @@ -16,208 +16,278 @@ # except in compliance with the License. You may obtain a copy of # the License at http://www.apache.org/licenses/LICENSE-2.0 . # +import pyuno import sys +import traceback +import warnings -import pyuno +# since on Windows sal3.dll no longer calls WSAStartup +import socket -try: - import __builtin__ -except ImportError: - import builtins as __builtin__ +# Some Python 2/3 compatibility code copied from the six library +PY2 = sys.version_info[0] == 2 -try: - unicode -except NameError: - # Python 3 compatibility - unicode = str +if PY2: + six_string_types = basestring, +else: + six_string_types = str, -import socket # since on Windows sal3.dll no longer calls WSAStartup +# All functions and variables starting with a underscore (_) must be +# considered private and can be changed at any time. Don't use them. +_component_context = pyuno.getComponentContext() -# all functions and variables starting with a underscore (_) must be considered private -# and can be changed at any time. Don't use them -_g_ctx = pyuno.getComponentContext( ) -_g_delegatee = __builtin__.__dict__["__import__"] def getComponentContext(): - """ returns the UNO component context, that was used to initialize the python runtime. + """Returns the UNO component context used to initialize the Python runtime.""" + + return _component_context + + +def getCurrentContext(): + """Returns the current context. + + See http://udk.openoffice.org/common/man/concept/uno_contexts.html#current_context + for an explanation on the current context concept. """ - return _g_ctx -def getConstantByName( constant ): - "Looks up the value of a idl constant by giving its explicit name" - return pyuno.getConstantByName( constant ) + return pyuno.getCurrentContext() + -def getTypeByName( typeName): - """ returns a uno.Type instance of the type given by typeName. In case the - type does not exist, a com.sun.star.uno.RuntimeException is raised. +def setCurrentContext(newContext): + """Sets newContext as new UNO context. + + The newContext must implement the XCurrentContext interface. The + implementation should handle the desired properties and delegate + unknown properties to the old context. Ensure that the old one + is reset when you leave your stack, see + http://udk.openoffice.org/common/man/concept/uno_contexts.html#current_context """ - return pyuno.getTypeByName( typeName ) -def createUnoStruct( typeName, *args, **kwargs ): - """creates a uno struct or exception given by typeName. Can be called with: + return pyuno.setCurrentContext(newContext) + + +def getConstantByName(constant): + """Looks up the value of an IDL constant by giving its explicit name.""" + + return pyuno.getConstantByName(constant) + + +def getTypeByName(typeName): + """Returns a `uno.Type` instance of the type given by typeName. + + If the type does not exist, a `com.sun.star.uno.RuntimeException` is raised. + """ + + return pyuno.getTypeByName(typeName) + + +def createUnoStruct(typeName, *args, **kwargs): + """Creates a UNO struct or exception given by typeName. + + Can be called with: + 1) No additional argument. - In this case, you get a default constructed uno structure. - ( e.g. createUnoStruct( "com.sun.star.uno.Exception" ) ) + In this case, you get a default constructed UNO structure. + (e.g. `createUnoStruct("com.sun.star.uno.Exception")`) 2) Exactly one additional argument that is an instance of typeName. In this case, a copy constructed instance of typeName is returned - ( e.g. createUnoStruct( "com.sun.star.uno.Exception" , e ) ) + (e.g. `createUnoStruct("com.sun.star.uno.Exception" , e)`) 3) As many additional arguments as the number of elements within typeName - (e.g. createUnoStruct( "com.sun.star.uno.Exception", "foo error" , self) ). + (e.g. `createUnoStruct("com.sun.star.uno.Exception", "foo error" , self)`). 4) Keyword arguments to give values for each element of the struct by name. 5) A mix of 3) and 4), such that each struct element is given a value exactly once, either by a positional argument or by a keyword argument. + The additional and/or keyword arguments must match the type of each struct element, otherwise an exception is thrown. """ - return getClass(typeName)( *args, **kwargs ) -def getClass( typeName ): - """returns the class of a concrete uno exception, struct or interface - """ + return getClass(typeName)(*args, **kwargs) + + +def getClass(typeName): + """Returns the class of a concrete UNO exception, struct, or interface.""" + return pyuno.getClass(typeName) -def isInterface( obj ): - """returns true, when obj is a class of a uno interface""" - return pyuno.isInterface( obj ) + +def isInterface(obj): + """Returns True, when obj is a class of a UNO interface.""" + + return pyuno.isInterface(obj) + def generateUuid(): - "returns a 16 byte sequence containing a newly generated uuid or guid, see rtl/uuid.h " + """Returns a 16 byte sequence containing a newly generated uuid or guid. + + For more information, see rtl/uuid.h. + """ + return pyuno.generateUuid() -def systemPathToFileUrl( systemPath ): - "returns a file-url for the given system path" - return pyuno.systemPathToFileUrl( systemPath ) -def fileUrlToSystemPath( url ): - "returns a system path (determined by the system, the python interpreter is running on)" - return pyuno.fileUrlToSystemPath( url ) +def systemPathToFileUrl(systemPath): + """Returns a file URL for the given system path.""" -def absolutize( path, relativeUrl ): - "returns an absolute file url from the given urls" - return pyuno.absolutize( path, relativeUrl ) + return pyuno.systemPathToFileUrl(systemPath) -def getCurrentContext(): - """Returns the currently valid current context. - see http://udk.openoffice.org/common/man/concept/uno_contexts.html#current_context - for an explanation on the current context concept - """ - return pyuno.getCurrentContext() -def setCurrentContext( newContext ): - """Sets newContext as new uno current context. The newContext must - implement the XCurrentContext interface. The implementation should - handle the desired properties and delegate unknown properties to the - old context. Ensure to reset the old one when you leave your stack ... - see http://udk.openoffice.org/common/man/concept/uno_contexts.html#current_context +def fileUrlToSystemPath(url): + """Returns a system path. + + This path is determined by the system that the Python interpreter is running on. """ - return pyuno.setCurrentContext( newContext ) + + return pyuno.fileUrlToSystemPath(url) + + +def absolutize(path, relativeUrl): + """Returns an absolute file url from the given urls.""" + + return pyuno.absolutize(path, relativeUrl) class Enum: - "Represents a UNO idl enum, use an instance of this class to explicitly pass a boolean to UNO" - #typeName the name of the enum as a string - #value the actual value of this enum as a string - def __init__(self,typeName, value): + """Represents a UNO enum. + + Use an instance of this class to explicitly pass an enum to UNO. + + :param typeName: The name of the enum as a string. + :param value: The actual value of this enum as a string. + """ + + def __init__(self, typeName, value): self.typeName = typeName self.value = value - pyuno.checkEnum( self ) + pyuno.checkEnum(self) def __repr__(self): - return "" % (self.typeName, self.value) + return "" % (self.typeName, self.value) def __eq__(self, that): if not isinstance(that, Enum): return False + return (self.typeName == that.typeName) and (self.value == that.value) + class Type: - "Represents a UNO type, use an instance of this class to explicitly pass a boolean to UNO" -# typeName # Name of the UNO type -# typeClass # python Enum of TypeClass, see com/sun/star/uno/TypeClass.idl + """Represents a UNO type. + + Use an instance of this class to explicitly pass a type to UNO. + + :param typeName: Name of the UNO type + :param typeClass: Python Enum of TypeClass, see com/sun/star/uno/TypeClass.idl + """ + def __init__(self, typeName, typeClass): self.typeName = typeName self.typeClass = typeClass pyuno.checkType(self) + def __repr__(self): return "" % (self.typeName, self.typeClass) def __eq__(self, that): if not isinstance(that, Type): return False + return self.typeClass == that.typeClass and self.typeName == that.typeName def __hash__(self): return self.typeName.__hash__() + class Bool(object): - """Represents a UNO boolean, use an instance of this class to explicitly - pass a boolean to UNO. - Note: This class is deprecated. Use python's True and False directly instead + """Represents a UNO boolean. + + Use an instance of this class to explicitly pass a boolean to UNO. + + Note: This class is deprecated. Use Python's True and False directly instead. """ + def __new__(cls, value): - if isinstance(value, (str, unicode)) and value == "true": + message = "The Bool class is deprecated. Use Python's True and False directly instead." + warnings.warn(message, DeprecationWarning) + + if isinstance(value, six_string_types) and value == "true": return True - if isinstance(value, (str, unicode)) and value == "false": + + if isinstance(value, six_string_types) and value == "false": return False + if value: return True + return False + class Char: - "Represents a UNO char, use an instance of this class to explicitly pass a char to UNO" - # @param value pass a Unicode string with length 1 - def __init__(self,value): - assert isinstance(value, unicode) - assert len(value) == 1 - self.value=value + """Represents a UNO char. + + Use an instance of this class to explicitly pass a char to UNO. + + For Python 2, this class only works with unicode objects. Creating + a Char instance with a normal str object or comparing a Char instance + to a normal str object will raise an AssertionError. + + :param value: A Unicode string with length 1 + """ + + def __init__(self, value): + if PY2: + assert isinstance(value, unicode), "Expected unicode object, got %s instead." % type(value) + else: + assert isinstance(value, str), "Expected str object, got %s instead." % type(value) + + assert len(value) == 1, "Char value must have length of 1." + + self.value = value def __repr__(self): - return "" % (self.value, ) + return "" % (self.value,) def __eq__(self, that): - if isinstance(that, (str, unicode)): + if isinstance(that, six_string_types): if len(that) > 1: return False + return self.value == that[0] + if isinstance(that, Char): return self.value == that.value - return False -# Suggested by Christian, but still some open problems which need to be solved first -# -#class ByteSequence(str): -# -# def __repr__(self): -# return "" % str.__repr__(self) + return False - # for a little bit compatibility; setting value is not possible as - # strings are immutable -# def _get_value(self): -# return self -# -# value = property(_get_value) class ByteSequence: + """Represents a UNO ByteSequence value. + + Use an instance of this class to explicitly pass a byte sequence to UNO. + + :param value: A string or bytesequence + """ + def __init__(self, value): if isinstance(value, bytes): self.value = value - elif isinstance(value, str): - self.value = value.encode("utf-8") # Python 2 compatibility + elif isinstance(value, ByteSequence): self.value = value.value + else: - raise TypeError("expected string or bytesequence") + raise TypeError("Expected bytes object or ByteSequence, got %s instead." % type(value)) def __repr__(self): - return "" % (self.value, ) + return "" % (self.value,) def __eq__(self, that): - if isinstance( that, ByteSequence): - return self.value == that.value if isinstance(that, bytes): return self.value == that - if isinstance(that, str): - return self.value == that.encode("utf-8") + + if isinstance(that, ByteSequence): + return self.value == that.value + return False def __len__(self): @@ -226,211 +296,256 @@ class ByteSequence: def __getitem__(self, index): return self.value[index] - def __iter__( self ): + def __iter__(self): return self.value.__iter__() - def __add__( self , b ): - if isinstance( b, bytes): + def __add__(self, b): + if isinstance(b, bytes): return ByteSequence(self.value + b) - elif isinstance( b, str ): - return ByteSequence( self.value + b.encode("utf-8") ) - elif isinstance( b, ByteSequence ): - return ByteSequence( self.value + b.value ) - raise TypeError( "expected string or ByteSequence as operand" ) - def __hash__( self ): + elif isinstance(b, ByteSequence): + return ByteSequence(self.value + b.value) + + else: + raise TypeError("Can't add ByteString and %s." % type(b)) + + def __hash__(self): return self.value.hash() class Any: - "use only in connection with uno.invoke() to pass an explicit typed any" - def __init__(self, type, value ): - if isinstance( type, Type ): + """Represents a UNO Any value. + + Use only in connection with uno.invoke() to pass an explicit typed Any. + """ + + def __init__(self, type, value): + if isinstance(type, Type): self.type = type else: - self.type = getTypeByName( type ) + self.type = getTypeByName(type) + self.value = value -def invoke( object, methodname, argTuple ): - "use this function to pass exactly typed anys to the callee (using uno.Any)" - return pyuno.invoke( object, methodname, argTuple ) -#--------------------------------------------------------------------------------------- -# don't use any functions beyond this point, private section, likely to change -#--------------------------------------------------------------------------------------- -#def _uno_import( name, globals={}, locals={}, fromlist=[], level=-1 ): -def _uno_import( name, *optargs, **kwargs ): +def invoke(object, methodname, argTuple): + """Use this function to pass exactly typed Anys to the callee (using uno.Any).""" + + return pyuno.invoke(object, methodname, argTuple) + + +# ----------------------------------------------------------------------------- +# Don't use any functions beyond this point; private section, likely to change. +# ----------------------------------------------------------------------------- +_builtin_import = __import__ + + +def _uno_import(name, *optargs, **kwargs): + """Overrides built-in import to allow directly importing LibreOffice classes.""" + try: -# print "optargs = " + repr(optargs) - return _g_delegatee( name, *optargs, **kwargs ) + return _builtin_import(name, *optargs, **kwargs) except ImportError as e: # process optargs - globals, locals, fromlist = list(optargs)[:3] + [kwargs.get('globals',{}), kwargs.get('locals',{}), kwargs.get('fromlist',[])][len(optargs):] + globals, locals, fromlist = list(optargs)[:3] + [kwargs.get('globals', {}), kwargs.get('locals', {}), + kwargs.get('fromlist', [])][len(optargs):] + # from import form only, but skip if an uno lookup has already failed if not fromlist or hasattr(e, '_uno_import_failed'): raise + # hang onto exception for possible use on subsequent uno lookup failure py_import_exc = e - modnames = name.split( "." ) + mod = None d = sys.modules - for x in modnames: - if x in d: - mod = d[x] + + for module in name.split("."): + if module in d: + mod = d[module] else: - mod = pyuno.__class__(x) # How to create a module ?? + # How to create a module ?? + mod = pyuno.__class__(module) + d = mod.__dict__ - RuntimeException = pyuno.getClass( "com.sun.star.uno.RuntimeException" ) - for x in fromlist: - if x not in d: - failed = False - if x.startswith( "typeOf" ): - try: - d[x] = pyuno.getTypeByName( name + "." + x[6:len(x)] ) - except RuntimeException: - failed = True - else: + RuntimeException = pyuno.getClass("com.sun.star.uno.RuntimeException") + + for class_name in fromlist: + if class_name not in d: + failed = False + try: # check for structs, exceptions or interfaces - d[x] = pyuno.getClass( name + "." + x ) + d[class_name] = pyuno.getClass(name + "." + class_name) except RuntimeException: # check for enums try: - d[x] = Enum( name , x ) + d[class_name] = Enum(name, class_name) except RuntimeException: - # check for constants - try: - d[x] = getConstantByName( name + "." + x ) - except RuntimeException: - # check for constant group - try: - d[x] = _impl_getConstantGroupByName( name, x ) - except ValueError: - failed = True - - if failed: - # We have an import failure, but cannot distinguish between - # uno and non-uno errors as uno lookups are attempted for all - # "from xxx import yyy" imports following a python failure. - # - # In Python 3, the original python exception traceback is reused - # to help pinpoint the actual failing location. Its original - # message, unlike Python 2, is unlikely to be helpful for uno - # failures, as it most commonly is just a top level module like - # 'com'. So our exception appends the uno lookup failure. - # This is more ambiguous, but it plus the traceback should be - # sufficient to identify a root cause for python or uno issues. - # - # Our exception is raised outside of the nested exception - # handlers above, to avoid Python 3 nested exception - # information for the RuntimeExceptions during lookups. - # - # Finally, a private attribute is used to prevent further - # processing if this failure was in a nested import. That - # keeps the exception relevant to the primary failure point, - # preventing us from re-processing our own import errors. - - uno_import_exc = ImportError("%s (or '%s.%s' is unknown)" % - (py_import_exc, name, x)) - if sys.version_info[0] >= 3: - uno_import_exc = uno_import_exc.with_traceback(py_import_exc.__traceback__) - uno_import_exc._uno_import_failed = True - raise uno_import_exc + # check for constants + try: + d[class_name] = getConstantByName(name + "." + class_name) + except RuntimeException: + # check for constant group + try: + d[class_name] = _impl_getConstantGroupByName(name, class_name) + except ValueError: + failed = True + + if failed: + # We have an import failure, but cannot distinguish between + # uno and non-uno errors as uno lookups are attempted for all + # "from xxx import yyy" imports following a python failure. + # + # In Python 3, the original python exception traceback is reused + # to help pinpoint the actual failing location. Its original + # message, unlike Python 2, is unlikely to be helpful for uno + # failures, as it most commonly is just a top level module like + # 'com'. So our exception appends the uno lookup failure. + # This is more ambiguous, but it plus the traceback should be + # sufficient to identify a root cause for python or uno issues. + # + # Our exception is raised outside of the nested exception + # handlers above, to avoid Python 3 nested exception + # information for the RuntimeExceptions during lookups. + # + # Finally, a private attribute is used to prevent further + # processing if this failure was in a nested import. That + # keeps the exception relevant to the primary failure point, + # preventing us from re-processing our own import errors. + + uno_import_exc = ImportError("%s (or '%s.%s' is unknown)" % + (py_import_exc, name, class_name)) + + if sys.version_info[0] >= 3: + uno_import_exc = uno_import_exc.with_traceback(py_import_exc.__traceback__) + + uno_import_exc._uno_import_failed = True + raise uno_import_exc return mod -# private + +try: + import __builtin__ +except ImportError: + import builtins as __builtin__ + +# hook into the __import__ chain +__builtin__.__dict__['__import__'] = _uno_import + + class _ConstantGroup(object): + """Represents a group of UNOIDL constants.""" + __slots__ = ['_constants'] + def __init__(self, constants): self._constants = constants + def __dir__(self): return self._constants.keys() - def __getattr__(self,name): + + def __getattr__(self, name): if name in self._constants: return self._constants[name] - raise AttributeError - -# private -def _impl_getConstantGroupByName( module, group ): - CONSTANTS = Enum('com.sun.star.uno.TypeClass', 'CONSTANTS') - ONE = Enum('com.sun.star.reflection.TypeDescriptionSearchDepth', 'ONE') - tdm = _g_ctx.getValueByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager') - tde = tdm.createTypeDescriptionEnumeration(module,(CONSTANTS,),ONE) - qualifiedName = module + '.' + group - for td in tde: - if td.Name == qualifiedName: - return_dict = dict() - for c in td.Constants: - return_dict.update({c.Name.split('.')[-1]: c.ConstantValue}) - return _ConstantGroup(return_dict) - else: - raise ValueError - -# private function, don't use -def _impl_extractName(name): - r = list(range(len(name)-1,0,-1)) - for i in r: - if name[i] == ".": - name = name[i+1:len(name)] - break - return name - -# private, referenced from the pyuno shared library -def _uno_struct__init__(self,*args, **kwargs): - if len(kwargs) == 0 and len(args) == 1 and hasattr(args[0], "__class__") and args[0].__class__ == self.__class__ : - self.__dict__["value"] = args[0] + + raise AttributeError("The constant '%s' could not be found." % name) + + +def _impl_getConstantGroupByName(module, group): + """Gets UNOIDL constant group by name.""" + + constants = Enum('com.sun.star.uno.TypeClass', 'CONSTANTS') + one = Enum('com.sun.star.reflection.TypeDescriptionSearchDepth', 'ONE') + type_desc_mgr = _component_context.getValueByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager') + type_descs = type_desc_mgr.createTypeDescriptionEnumeration(module, (constants,), one) + qualified_name = module + '.' + group + + for type_desc in type_descs: + if type_desc.Name == qualified_name: + return _ConstantGroup(dict( + (c.Name.split('.')[-1], c.ConstantValue) + for c in type_desc.Constants)) + + raise ValueError("The constant group '%s' could not be found." % qualified_name) + + +def _uno_struct__init__(self, *args, **kwargs): + """Initializes a UNO struct. + + Referenced from the pyuno shared library. + + This function can be called with either an already constructed UNO struct, which it + will then just reference without copying, or with arguments to create a new UNO struct. + """ + + # Check to see if this function was passed an existing UNO struct + if len(kwargs) == 0 and len(args) == 1 and getattr(args[0], "__class__", None) == self.__class__: + self.__dict__['value'] = args[0] else: - struct, used = pyuno._createUnoStructHelper(self.__class__.__pyunostruct__,args,**kwargs) - for kw in kwargs.keys(): - if not (kw in used and used[kw]): - RuntimeException = pyuno.getClass( "com.sun.star.uno.RuntimeException" ) - raise RuntimeException("_uno_struct__init__: unused keyword argument '" + kw + "'", None) + struct, used = pyuno._createUnoStructHelper(self.__class__.__pyunostruct__, args, **kwargs) + + for kwarg in kwargs.keys(): + if not used.get(kwarg): + RuntimeException = pyuno.getClass("com.sun.star.uno.RuntimeException") + raise RuntimeException("_uno_struct__init__: unused keyword argument '%s'." % kwarg, None) + self.__dict__["value"] = struct -# private, referenced from the pyuno shared library -def _uno_struct__getattr__(self,name): - return __builtin__.getattr(self.__dict__["value"],name) -# private, referenced from the pyuno shared library -def _uno_struct__setattr__(self,name,value): - return __builtin__.setattr(self.__dict__["value"],name,value) +def _uno_struct__getattr__(self, name): + """Gets attribute from UNO struct. + + Referenced from the pyuno shared library. + """ + + return getattr(self.__dict__["value"], name) + + +def _uno_struct__setattr__(self, name, value): + """Sets attribute on UNO struct. + + Referenced from the pyuno shared library. + """ + + return setattr(self.__dict__["value"], name, value) + -# private, referenced from the pyuno shared library def _uno_struct__repr__(self): + """Converts a UNO struct to a printable string. + + Referenced from the pyuno shared library. + """ + return repr(self.__dict__["value"]) + def _uno_struct__str__(self): + """Converts a UNO struct to a string.""" + return str(self.__dict__["value"]) -# private, referenced from the pyuno shared library -def _uno_struct__eq__(self,cmp): - if hasattr(cmp,"value"): - return self.__dict__["value"] == cmp.__dict__["value"] + +def _uno_struct__eq__(self, that): + """Compares two UNO structs. + + Referenced from the pyuno shared library. + """ + + if hasattr(that, "value"): + return self.__dict__["value"] == that.__dict__["value"] + return False -# referenced from pyuno shared lib and pythonscript.py -def _uno_extract_printable_stacktrace( trace ): - mod = None - try: - mod = __import__("traceback") - except ImportError as e: - pass - ret = "" - if mod: - lst = mod.extract_tb( trace ) - max = len(lst) - for j in range(max): - i = lst[max-j-1] - ret = ret + " " + str(i[0]) + ":" + \ - str(i[1]) + " in function " + \ - str(i[2]) + "() [" + str(i[3]) + "]\n" - else: - ret = "Couldn't import traceback module" - return ret -# hook into the __import__ chain -__builtin__.__dict__["__import__"] = _uno_import +def _uno_extract_printable_stacktrace(trace): + """Extracts a printable stacktrace. + + Referenced from pyuno shared lib and pythonscript.py. + """ + + return ''.join(traceback.format_tb(trace)) # vim:set shiftwidth=4 softtabstop=4 expandtab: -- cgit