diff --git a/src/App/FreeCADInit.py b/src/App/FreeCADInit.py index 0a3e96ee34..284926ff76 100644 --- a/src/App/FreeCADInit.py +++ b/src/App/FreeCADInit.py @@ -50,11 +50,6 @@ FreeCAD._importFromFreeCAD = removeFromPath def InitApplications(): - try: - import sys,os,traceback,io - except ImportError: - FreeCAD.Console.PrintError("\n\nSeems the python standard libs are not installed, bailing out!\n\n") - raise # Checking on FreeCAD module path ++++++++++++++++++++++++++++++++++++++++++ ModDir = FreeCAD.getHomePath()+'Mod' ModDir = os.path.realpath(ModDir) @@ -62,10 +57,22 @@ def InitApplications(): ExtDir = os.path.realpath(ExtDir) BinDir = FreeCAD.getHomePath()+'bin' BinDir = os.path.realpath(BinDir) + libpaths = [] LibDir = FreeCAD.getHomePath()+'lib' LibDir = os.path.realpath(LibDir) + if os.path.exists(LibDir): + libpaths.append(LibDir) Lib64Dir = FreeCAD.getHomePath()+'lib64' Lib64Dir = os.path.realpath(Lib64Dir) + if os.path.exists(Lib64Dir): + libpaths.append(Lib64Dir) + if sys.version_info[0] == 3: + LibPyDir = FreeCAD.getHomePath()+'lib-py3' + else: + LibPyDir = FreeCAD.getHomePath()+'lib-py2' + LibPyDir = os.path.realpath(LibPyDir) + if (os.path.exists(LibPyDir)): + libpaths.append(LibPyDir) AddPath = FreeCAD.ConfigGet("AdditionalModulePaths").split(";") HomeMod = FreeCAD.getUserAppDataDir()+"Mod" HomeMod = os.path.realpath(HomeMod) @@ -117,12 +124,12 @@ def InitApplications(): # this allows importing with: # from FreeCAD.Module import package - FreeCAD.__path__ = [ModDir, Lib64Dir, LibDir, HomeMod] + FreeCAD.__path__ = [ModDir] + libpaths + [HomeMod] # also add these directories to the sys.path to # not change the old behaviour. once we have moved to # proper python modules this can eventuelly be removed. - sys.path = [ModDir, Lib64Dir, LibDir, ExtDir] + sys.path + sys.path = [ModDir] + libpaths + [ExtDir] + sys.path for Dir in ModDict.values(): if ((Dir != '') & (Dir != 'CVS') & (Dir != '__init__.py')): @@ -140,7 +147,7 @@ def InitApplications(): Log('-'*100+'\n') Log(traceback.format_exc()) Log('-'*100+'\n') - Err('During initialization the error ' + str(inst) + ' occurred in ' + InstallFile + '\n') + Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n') Err('Please look into the log file for further information\n') else: Log('Init: Initializing ' + Dir + '... done\n') @@ -156,25 +163,25 @@ def InitApplications(): for _, freecad_module_name, freecad_module_ispkg in pkgutil.iter_modules(freecad.__path__, "freecad."): if freecad_module_ispkg: Log('Init: Initializing ' + freecad_module_name + '\n') - freecad_module = importlib.import_module(freecad_module_name) - extension_modules += [freecad_module_name] - if any (module_name == 'init' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)): - try: + try: + freecad_module = importlib.import_module(freecad_module_name) + extension_modules += [freecad_module_name] + if any (module_name == 'init' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)): importlib.import_module(freecad_module_name + '.init') Log('Init: Initializing ' + freecad_module_name + '... done\n') - except Exception as inst: - Err('During initialization the error ' + str(inst) + ' occurred in ' + freecad_module_name + '\n') - Err('-'*80+'\n') - Err(traceback.format_exc()) - Err('-'*80+'\n') - Log('Init: Initializing ' + freecad_module_name + '... failed\n') - Log('-'*80+'\n') - Log(traceback.format_exc()) - Log('-'*80+'\n') - else: - Log('Init: No init module found in ' + freecad_module_name + ', skipping\n') + else: + Log('Init: No init module found in ' + freecad_module_name + ', skipping\n') + except Exception as inst: + Err('During initialization the error "' + str(inst) + '" occurred in ' + freecad_module_name + '\n') + Err('-'*80+'\n') + Err(traceback.format_exc()) + Err('-'*80+'\n') + Log('Init: Initializing ' + freecad_module_name + '... failed\n') + Log('-'*80+'\n') + Log(traceback.format_exc()) + Log('-'*80+'\n') except ImportError as inst: - Err('During initialization the error ' + str(inst) + ' occurred\n') + Err('During initialization the error "' + str(inst) + '" occurred\n') Log("Using "+ModDir+" as module path!\n") # In certain cases the PathExtension list can contain invalid strings. We concatenate them to a single string @@ -235,6 +242,378 @@ App.__unit_test__ = [] Log ('Init: starting App::FreeCADInit.py\n') +try: + import sys,os,traceback,io,inspect + from datetime import datetime +except ImportError: + FreeCAD.Console.PrintError("\n\nSeems the python standard libs are not installed, bailing out!\n\n") + raise + +class FCADLogger(object): + '''Convenient class for tagged logging. + + Example usage: + >>> logger = FreeCAD.Logger('MyModule') + >>> logger.info('log test {}',1) + 24.36053 (1): test log 1 + + The default output format is: + (line number): message + + The message is formated using new style Python string formating, e.g. + 'test {}'.format(1). It is strongly recommanded to not directly use + Python string formating, but pass additional argument indirectly through + various logger print function, because the logger can skip string + evaluation in case the logging level is disabled. For more options, + please consult the docstring of __init__(), catch() and report(). + + To set/get logger level: + >>> FreeCAD.setLogLevel('MyModule','Trace') + >>> FreeCAD.getLogLevel('MyModule') + 4 + + There are five predefined logger level, each corresponding to an integer + value, as shown below together with the corresponding logger print + method, + 0: Error, Logger.error() + 1: Warning, Logger.warn() + 2: Message, Logger.msg() or info() + 3: Log, Logger.log() or debug() + 4: Trace, Logger.trace() + + FreeCAD.setLogLevel() supports both text and integer value, which allows + you to define your own levels. The level set is persisted to user + configuration file. + + By defult any tag has a log level of 2 for release, and 3 for debug + build. + ''' + + _string_type = str if sys.version_info[0] >= 3 else basestring + + _levels = { 'Error':0, 'error':0, + 'Warning':1, 'warn':1, + 'Message':2, 'msg':2, 'info':2, + 'Log':3, 'log':3, 'debug':3, + 'Trace':4, 'trace':4,} + _printer = [ + FreeCAD.Console.PrintError, + FreeCAD.Console.PrintWarning, + FreeCAD.Console.PrintMessage, + FreeCAD.Console.PrintLog, + FreeCAD.Console.PrintLog ] + + def __init__(self, tag, **kargs): + '''Construct a logger instance. + + Supported arguments are their default values are, + + * tag: a string tag for this logger. The log level of this logger can be + accessed using FreeCAD.getLogLevel(tag)/setLogLevel(tag,level). + All logger instance with the same tag shares the same level + setting. + + * printTag (True): whether to print tag + + * noUpdateUI (True): whether to update GUI when printing. This is useful + to show log output on lengthy operations. Be + careful though, this may allow unexpected user + interaction when the application is busy, which may + lead to crash + + * timing (True): whether to print time stamp + + * lineno (True): whether to print source file and line number + + * parent (None): provide a parent logger, so that the log printting will + check for parent's log level in addition of its own + + * title ('FreeCAD'): message box title used by report() + ''' + self.tag = tag + self.laststamp = datetime.now() + for key,default in (('printTag',True),('noUpdateUI',True), + ('timing',True),('lineno',True),('parent',None), + ('title','FreeCAD')) : + setattr(self,key,kargs.get(key,default)) + + def _isEnabledFor(self,level): + '''Internal function to check for an integer log level. + + * level: integer log level + ''' + + if self.parent and not self.parent._isEnabledFor(level): + return False + return FreeCAD.getLogLevel(self.tag) >= level + + def isEnabledFor(self,level): + '''To check for an integer or text log level. + + * level: integer or text log level + ''' + if not isinstance(level,int): + level = self.__class__._levels[level] + return self._isEnabledFor(level) + + def error(self,msg,*args,**kargs): + '''"Error" level log printer + + * msg: message string. May contain new style Python string formatter. + + This function accepts additional positional and keyword arguments, + which are forward to string.format() to generate the logging + message. It is strongly recommanded to not directly use Python + string formating, but pass additional arguments here, because the + printer can skip string evaluation in case the logging level is + disabled. + ''' + if self._isEnabledFor(0): + frame = kargs.pop('frame',0)+1 + self._log(0,msg,frame,args,kargs) + + def warn(self,msg,*args,**kargs): + '''"Warning" level log printer + + * msg: message string. May contain new style Python string formatter. + + This function accepts additional positional and keyword arguments, + which are forward to string.format() to generate the logging + message. It is strongly recommanded to not directly use Python + string formating, but pass additional arguments here, because the + printer can skip string evaluation in case the logging level is + disabled. + ''' + if self._isEnabledFor(1): + frame = kargs.pop('frame',0)+1 + self._log(1,msg,frame,args,kargs) + + def msg(self,msg,*args,**kargs): + '''"Message" level log printer + + * msg: message string. May contain new style Python string formatter. + + This function accepts additional positional and keyword arguments, + which are forward to string.format() to generate the logging + message. It is strongly recommanded to not directly use Python + string formating, but pass additional arguments here, because the + printer can skip string evaluation in case the logging level is + disabled. + ''' + if self._isEnabledFor(2): + frame = kargs.pop('frame',0)+1 + self._log(2,msg,frame,args,kargs) + + info = msg + + def log(self,msg,*args,**kargs): + '''"Log" level log printer + + * msg: message string. May contain new style Python string formatter. + + This function accepts additional positional and keyword arguments, + which are forward to string.format() to generate the logging + message. It is strongly recommanded to not directly use Python + string formating, but pass additional arguments here, because the + printer can skip string evaluation in case the logging level is + disabled. + ''' + if self._isEnabledFor(3): + frame = kargs.pop('frame',0)+1 + self._log(3,msg,frame,args,kargs) + + debug = log + + def trace(self,msg,*args,**kargs): + '''"Trace" level log printer + + * msg: message string. May contain new style Python string formatter. + + This function accepts additional positional and keyword arguments, + which are forward to string.format() to generate the logging + message. It is strongly recommanded to not directly use Python + string formating, but pass additional arguments here, because the + printer can skip string evaluation in case the logging level is + disabled. + ''' + if self._isEnabledFor(4): + frame = kargs.pop('frame',0)+1 + self._log(4,msg,frame,args,kargs) + + def _log(self,level,msg,frame=0,args=(),kargs=None): + '''Internal log printing function. + + * level: integer log level + + * msg: message, may contain new style string format specifier + + * frame (0): the calling frame for printing source file and line + number. For example, in case you have your own logging + function, and you want to show the callers source + location, then set frame to one. + + * args: tuple for postiional arguments to be passed to + string.format() + + * kargs: dictionary for keyword arguments to be passed to + string.format() + ''' + + if (args or kargs) and isinstance(msg,self.__class__._string_type): + if not kargs: + msg = msg.format(*args) + else: + msg = msg.format(*args,**kargs) + + prefix = '' + + if self.timing: + now = datetime.now() + prefix += '{} '.format((now-self.laststamp).total_seconds()) + self.laststamp = now + + if self.printTag: + prefix += '<{}> '.format(self.tag) + + if self.lineno: + try: + frame = sys._getframe(frame+1) + prefix += '{}({}): '.format(os.path.basename( + frame.f_code.co_filename),frame.f_lineno) + except Exception: + frame = inspect.stack()[frame+1] + prefix += '{}({}): '.format(os.path.basename(frame[1]),frame[2]) + + self.__class__._printer[level]('{}{}\n'.format(prefix,msg)) + + if not self.noUpdateUI and FreeCAD.GuiUp: + import FreeCADGui + try: + FreeCADGui.updateGui() + except Exception: + pass + + def _catch(self,level,msg,func,args=None,kargs=None): + '''Internal function to log exception of any callable. + + * level: integer log level + + * msg: message string. Unlike _log(), this argument must not contain + any string formatter. + + * func: a callable object + + * args: tuple of positional arguments to be passed to func. + + * kargs: dictionary of keyword arguments to be passed to func. + ''' + try: + if not args: + args = [] + if not kargs: + kargs = {} + return func(*args,**kargs) + except Exception: + if self._isEnabledFor(level): + self._log(level,msg+'\n'+traceback.format_exc(),frame=2) + + def catch(self,msg,func,*args,**kargs): + '''Catch any exception from a function and print as "Error". + + * msg: message string. Unlike log printer, this argument must not + contain any string formatter. + + * func: a callable object + + * args: tuple of positional arguments to be passed to func. + + * kargs: dictionary of keyword arguments to be passed to func. + ''' + return self._catch(0,msg,func,args,kargs) + + def catchWarn(self,msg,func,*args,**kargs): + '''Catch any exception from a function and print as "Warning". + + * msg: message string. Unlike log printer, this argument must not + contain any string formatter. + + * func: a callable object + + * args: tuple of positional arguments to be passed to func. + + * kargs: dictionary of keyword arguments to be passed to func. + ''' + return self._catch(1,msg,func,args,kargs) + + def catchMsg(self,msg,func,*args,**kargs): + '''Catch any exception from a function and print as "Message". + + * msg: message string. Unlike log printer, this argument must not + contain any string formatter. + + * func: a callable object + + * args: tuple of positional arguments to be passed to func. + + * kargs: dictionary of keyword arguments to be passed to func. + ''' + return self._catch(2,msg,func,args,kargs) + + catchInfo = catchMsg + + def catchLog(self,msg,func,*args,**kargs): + '''Catch any exception from a function and print as "Log". + + * msg: message string. Unlike log printer, this argument must not + contain any string formatter. + + * func: a callable object + + * args: tuple of positional arguments to be passed to func. + + * kargs: dictionary of keyword arguments to be passed to func. + ''' + return self._catch(3,msg,func,args,kargs) + + catchDebug = catchLog + + def catchTrace(self,msg,func,*args,**kargs): + '''Catch any exception from a function and print as "Trace". + + * msg: message string. Unlike log printer, this argument must not + contain any string formatter. + + * func: a callable object + + * args: tuple of positional arguments to be passed to func. + + * kargs: dictionary of keyword arguments to be passed to func. + ''' + return self._catch(4,msg,func,args,kargs) + + def report(self,msg,func,*args,**kargs): + '''Catch any exception report it with a message box. + + * msg: message string. Unlike log printer, this argument must not + contain any string formatter. + + * func: a callable object + + * args: tuple of positional arguments to be passed to func. + + * kargs: dictionary of keyword arguments to be passed to func. + ''' + try: + return func(*args,**kargs) + except Exception as e: + self.error(msg+'\n'+traceback.format_exc(),frame=1) + if FreeCAD.GuiUp: + import FreeCADGui,PySide + PySide.QtGui.QMessageBox.critical( + FreeCADGui.getMainWindow(),self.title,str(e)) + +FreeCAD.Logger = FCADLogger + # init every application by importing Init.py try: import traceback diff --git a/src/Gui/FreeCADGuiInit.py b/src/Gui/FreeCADGuiInit.py index a15c50e19f..501852c000 100644 --- a/src/Gui/FreeCADGuiInit.py +++ b/src/Gui/FreeCADGuiInit.py @@ -123,7 +123,7 @@ def InitApplications(): Log('-'*100+'\n') Log(traceback.format_exc()) Log('-'*100+'\n') - Err('During initialization the error ' + str(inst) + ' occurred in ' + InstallFile + '\n') + Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n') Err('Please look into the log file for further information\n') else: Log('Init: Initializing ' + Dir + '... done\n') @@ -139,24 +139,24 @@ def InitApplications(): for _, freecad_module_name, freecad_module_ispkg in pkgutil.iter_modules(freecad.__path__, "freecad."): if freecad_module_ispkg: Log('Init: Initializing ' + freecad_module_name + '\n') - freecad_module = importlib.import_module(freecad_module_name) - if any (module_name == 'init_gui' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)): - try: + try: + freecad_module = importlib.import_module(freecad_module_name) + if any (module_name == 'init_gui' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)): importlib.import_module(freecad_module_name + '.init_gui') Log('Init: Initializing ' + freecad_module_name + '... done\n') - except Exception as inst: - Err('During initialization the error ' + str(inst) + ' occurred in ' + freecad_module_name + '\n') - Err('-'*80+'\n') - Err(traceback.format_exc()) - Err('-'*80+'\n') - Log('Init: Initializing ' + freecad_module_name + '... failed\n') - Log('-'*80+'\n') - Log(traceback.format_exc()) - Log('-'*80+'\n') - else: - Log('Init: No init_gui module found in ' + freecad_module_name + ', skipping\n') + else: + Log('Init: No init_gui module found in ' + freecad_module_name + ', skipping\n') + except Exception as inst: + Err('During initialization the error "' + str(inst) + '" occurred in ' + freecad_module_name + '\n') + Err('-'*80+'\n') + Err(traceback.format_exc()) + Err('-'*80+'\n') + Log('Init: Initializing ' + freecad_module_name + '... failed\n') + Log('-'*80+'\n') + Log(traceback.format_exc()) + Log('-'*80+'\n') except ImportError as inst: - Err('During initialization the error ' + str(inst) + ' occurred\n') + Err('During initialization the error "' + str(inst) + '" occurred\n') Log ('Init: Running FreeCADGuiInit.py start script...\n')