1- import hashlib
21from contextlib import contextmanager
2+ import hashlib
33import importlib
44import inspect
5- import os
6- import tempfile
7- import uuid
85import logging
6+ import os
7+ from pathlib import Path
98import pkgutil
10-
119import sys
10+ import tempfile
11+ import traceback
12+ import uuid
1213
1314try :
1415 # for cx_freeze
1920 pass
2021
2122from meshroom .core .submitter import BaseSubmitter
23+ from meshroom .env import EnvVar , meshroomFolder
2224from . import desc
25+ from .desc import MrNodeType
2326
2427# Setup logging
2528logging .basicConfig (format = '[%(asctime)s][%(levelname)s] %(message)s' , level = logging .INFO )
2831sessionUid = str (uuid .uuid1 ())
2932
3033cacheFolderName = 'MeshroomCache'
31- defaultCacheFolder = os .environ .get ('MESHROOM_CACHE' , os .path .join (tempfile .gettempdir (), cacheFolderName ))
32- nodesDesc = {}
33- submitters = {}
34- pipelineTemplates = {}
34+ nodesDesc : dict [str , desc .BaseNode ] = {}
35+ submitters : dict [str , BaseSubmitter ] = {}
36+ pipelineTemplates : dict [str , str ] = {}
3537
3638
37- def hashValue (value ):
39+ def hashValue (value ) -> str :
3840 """ Hash 'value' using sha1. """
3941 hashObject = hashlib .sha1 (str (value ).encode ('utf-8' ))
4042 return hashObject .hexdigest ()
@@ -52,19 +54,34 @@ def add_to_path(p):
5254 sys .path = old_path
5355
5456
55- def loadPlugins (folder , packageName , classType ):
57+ def loadClasses (folder , packageName , classType ):
5658 """
5759 """
58-
59- pluginTypes = []
60+ classes = []
6061 errors = []
6162
63+ resolvedFolder = str (Path (folder ).resolve ())
6264 # temporarily add folder to python path
63- with add_to_path (folder ):
65+ with add_to_path (resolvedFolder ):
6466 # import node package
65- package = importlib .import_module (packageName )
66- packageName = package .packageName if hasattr (package , 'packageName' ) else package .__name__
67- packageVersion = getattr (package , "__version__" , None )
67+
68+ try :
69+ package = importlib .import_module (packageName )
70+ packageName = package .packageName if hasattr (package , 'packageName' ) else package .__name__
71+ packageVersion = getattr (package , "__version__" , None )
72+ packagePath = os .path .dirname (package .__file__ )
73+ except Exception as e :
74+ tb = traceback .extract_tb (e .__traceback__ )
75+ last_call = tb [- 1 ]
76+ logging .warning (f' * Failed to load package "{ packageName } " from folder "{ resolvedFolder } " ({ type (e ).__name__ } ): { str (e )} \n '
77+ # filename:lineNumber functionName
78+ f'{ last_call .filename } :{ last_call .lineno } { last_call .name } \n '
79+ # line of code with the error
80+ f'{ last_call .line } '
81+ # Full traceback
82+ f'\n { traceback .format_exc ()} \n \n '
83+ )
84+ return []
6885
6986 for importer , pluginName , ispkg in pkgutil .iter_modules (package .__path__ ):
7087 pluginModuleName = '.' + pluginName
@@ -75,7 +92,7 @@ def loadPlugins(folder, packageName, classType):
7592 if plugin .__module__ == '{}.{}' .format (package .__name__ , pluginName )
7693 and issubclass (plugin , classType )]
7794 if not plugins :
78- logging .warning ("No class defined in plugin: {}" . format ( pluginModuleName ) )
95+ logging .warning (f "No class defined in plugin: { pluginModuleName } " )
7996
8097 importPlugin = True
8198 for p in plugins :
@@ -88,16 +105,26 @@ def loadPlugins(folder, packageName, classType):
88105 break
89106 p .packageName = packageName
90107 p .packageVersion = packageVersion
108+ p .packagePath = packagePath
91109 if importPlugin :
92- pluginTypes .extend (plugins )
110+ classes .extend (plugins )
93111 except Exception as e :
94- errors .append (' * {}: {}' .format (pluginName , str (e )))
112+ tb = traceback .extract_tb (e .__traceback__ )
113+ last_call = tb [- 1 ]
114+ errors .append (f' * { pluginName } ({ type (e ).__name__ } ): { str (e )} \n '
115+ # filename:lineNumber functionName
116+ f'{ last_call .filename } :{ last_call .lineno } { last_call .name } \n '
117+ # line of code with the error
118+ f'{ last_call .line } '
119+ # Full traceback
120+ f'\n { traceback .format_exc ()} \n \n '
121+ )
95122
96123 if errors :
97- logging .warning ('== The following "{package}" plugins could not be loaded == \n '
124+ logging .warning (' The following "{package}" plugins could not be loaded: \n '
98125 '{errorMsg}\n '
99126 .format (package = packageName , errorMsg = '\n ' .join (errors )))
100- return pluginTypes
127+ return classes
101128
102129
103130def validateNodeDesc (nodeDesc ):
@@ -283,75 +310,114 @@ def registerNodeType(nodeType):
283310
284311 After registration, nodes of this type can be instantiated in a Graph.
285312 """
286- global nodesDesc
287313 if nodeType .__name__ in nodesDesc :
288- logging .error ("Node Desc {} is already registered." . format ( nodeType . __name__ ) )
314+ logging .error (f "Node Desc { nodeType . __name__ } is already registered." )
289315 nodesDesc [nodeType .__name__ ] = nodeType
290316
291317
292318def unregisterNodeType (nodeType ):
293319 """ Remove 'nodeType' from the list of register node types. """
294- global nodesDesc
295320 assert nodeType .__name__ in nodesDesc
296321 del nodesDesc [nodeType .__name__ ]
297322
298323
299324def loadNodes (folder , packageName ):
300- return loadPlugins (folder , packageName , desc .Node )
325+ if not os .path .isdir (folder ):
326+ logging .error (f"Node folder '{ folder } ' does not exist." )
327+ return
328+
329+ return loadClasses (folder , packageName , desc .BaseNode )
301330
302331
303332def loadAllNodes (folder ):
304- global nodesDesc
305333 for importer , package , ispkg in pkgutil .walk_packages ([folder ]):
306334 if ispkg :
307335 nodeTypes = loadNodes (folder , package )
308336 for nodeType in nodeTypes :
309337 registerNodeType (nodeType )
310- logging .debug ('Nodes loaded [{}]: {}' .format (package , ', ' .join ([nodeType .__name__ for nodeType in nodeTypes ])))
338+ nodesStr = ', ' .join ([nodeType .__name__ for nodeType in nodeTypes ])
339+ logging .debug (f'Nodes loaded [{ package } ]: { nodesStr } ' )
340+
341+
342+ def loadPluginFolder (folder ):
343+ if not os .path .isdir (folder ):
344+ logging .info (f"Plugin folder '{ folder } ' does not exist." )
345+ return
346+
347+ mrFolder = Path (folder , 'meshroom' )
348+ if not mrFolder .exists ():
349+ logging .info (f"Plugin folder '{ folder } ' does not contain a 'meshroom' folder." )
350+ return
351+
352+ binFolders = [Path (folder , 'bin' )]
353+ libFolders = [Path (folder , 'lib' ), Path (folder , 'lib64' )]
354+ pythonPathFolders = [Path (folder )] + binFolders
355+
356+ loadAllNodes (folder = mrFolder )
357+ loadPipelineTemplates (folder = mrFolder )
358+
359+
360+ def loadPluginsFolder (folder ):
361+ if not os .path .isdir (folder ):
362+ logging .debug (f"PluginSet folder '{ folder } ' does not exist." )
363+ return
364+
365+ for file in os .listdir (folder ):
366+ if os .path .isdir (file ):
367+ subFolder = os .path .join (folder , file )
368+ loadPluginFolder (subFolder )
311369
312370
313371def registerSubmitter (s ):
314- global submitters
315372 if s .name in submitters :
316- logging .error ("Submitter {} is already registered." . format ( s . name ) )
373+ logging .error (f "Submitter { s . name } is already registered." )
317374 submitters [s .name ] = s
318375
319376
320377def loadSubmitters (folder , packageName ):
321- return loadPlugins (folder , packageName , BaseSubmitter )
378+ if not os .path .isdir (folder ):
379+ logging .error (f"Submitters folder '{ folder } ' does not exist." )
380+ return
381+
382+ return loadClasses (folder , packageName , BaseSubmitter )
322383
323384
324385def loadPipelineTemplates (folder ):
325- global pipelineTemplates
386+ if not os .path .isdir (folder ):
387+ logging .error (f"Pipeline templates folder '{ folder } ' does not exist." )
388+ return
326389 for file in os .listdir (folder ):
327390 if file .endswith (".mg" ) and file not in pipelineTemplates :
328391 pipelineTemplates [os .path .splitext (file )[0 ]] = os .path .join (folder , file )
329392
330393
331394def initNodes ():
332- meshroomFolder = os .path .dirname (os .path .dirname (__file__ ))
333- additionalNodesPath = os .environ .get ("MESHROOM_NODES_PATH" , "" ).split (os .pathsep )
334- # filter empty strings
335- additionalNodesPath = [i for i in additionalNodesPath if i ]
395+ additionalNodesPath = EnvVar .getList (EnvVar .MESHROOM_NODES_PATH )
336396 nodesFolders = [os .path .join (meshroomFolder , 'nodes' )] + additionalNodesPath
337397 for f in nodesFolders :
338398 loadAllNodes (folder = f )
339399
340400
341401def initSubmitters ():
342- meshroomFolder = os .path .dirname (os .path .dirname (__file__ ))
343- subs = loadSubmitters (os .environ .get ("MESHROOM_SUBMITTERS_PATH" , meshroomFolder ), 'submitters' )
344- for sub in subs :
345- registerSubmitter (sub ())
402+ additionalPaths = EnvVar .getList (EnvVar .MESHROOM_SUBMITTERS_PATH )
403+ allSubmittersFolders = [meshroomFolder ] + additionalPaths
404+ for folder in allSubmittersFolders :
405+ subs = loadSubmitters (folder , 'submitters' )
406+ for sub in subs :
407+ registerSubmitter (sub ())
346408
347409
348410def initPipelines ():
349- meshroomFolder = os . path . dirname ( os . path . dirname ( __file__ ))
350- # Load pipeline templates: check in any folder the user might have added to the environment variable
351- pipelinesPath = os . environ . get ( "MESHROOM_PIPELINE_TEMPLATES_PATH" , "" ). split ( os . pathsep )
352- pipelineTemplatesFolders = [i for i in pipelinesPath if i ]
411+ # Load pipeline templates: check in the default folder and any folder the user might have
412+ # added to the environment variable
413+ additionalPipelinesPath = EnvVar . getList ( EnvVar . MESHROOM_PIPELINE_TEMPLATES_PATH )
414+ pipelineTemplatesFolders = [os . path . join ( meshroomFolder , 'pipelines' )] + additionalPipelinesPath
353415 for f in pipelineTemplatesFolders :
354- if os .path .isdir (f ):
355- loadPipelineTemplates (f )
356- else :
357- logging .warning ("Pipeline templates folder '{}' does not exist." .format (f ))
416+ loadPipelineTemplates (f )
417+
418+
419+ def initPlugins ():
420+ additionalpluginsPath = EnvVar .getList (EnvVar .MESHROOM_PLUGINS_PATH )
421+ nodesFolders = [os .path .join (meshroomFolder , 'plugins' )] + additionalpluginsPath
422+ for f in nodesFolders :
423+ loadPluginFolder (folder = f )
0 commit comments