@@ -23,6 +23,63 @@ class ModuleNotFoundError(ImportError):
2323 "Microscopy Suite program." )
2424 DM = None
2525
26+ class DMScriptError (RuntimeError ):
27+ """Error in executed dm-script code."""
28+ # The doc is shown in the GMS error message. Keeping the real doc string
29+ # makes the error message very big and fills it with completely useless
30+ # information
31+ #
32+ # """This represents an error in the executed dm-script code.
33+ #
34+ # Parameters
35+ # ----------
36+ # msg : str
37+ # The text to show
38+ # script_origin : str
39+ # The name of the executed script that the error occurres in
40+ # line_in_origin : int
41+ # The line in the executed script (the `script_origin`) where the error
42+ # occurres
43+ # line_in_complete : int
44+ # The line in the compound script (including the setvars and readvars
45+ # code)
46+ # """
47+
48+ def __init__ (self , msg : str , script_origin : str , line_in_origin : int ,
49+ line_in_complete : int ) -> None :
50+ """
51+ Parameters
52+ ----------
53+ msg : str
54+ The text to show
55+ script_origin : str
56+ The name of the executed script that the error occurres in
57+ line_in_origin : int
58+ The line in the executed script (the `script_origin`) where the error
59+ occurres
60+ line_in_complete : int
61+ The line in the compound script (including the setvars and readvars
62+ code)
63+ """
64+ super (DMScriptError , self ).__init__ (msg )
65+ self .script_origin = script_origin
66+ self .line_in_origin = line_in_origin
67+ self .line_in_complete = line_in_complete
68+ self .msg = msg
69+
70+ def __str__ (self ) -> str :
71+ """Get the error as a a string.
72+
73+ Returns
74+ -------
75+ str
76+ The error with (most of) the details
77+ """
78+
79+ return ("Error in dm-script code {} in line {} (line {} in complete " +
80+ "code): {}" ).format (self .script_origin , self .line_in_origin ,
81+ self .line_in_complete , self .msg )
82+
2683Convertable = typing .Union [int , float , bool , str , dict , list , tuple , None ]
2784
2885_python_dm_type_map = ({
@@ -335,6 +392,7 @@ def __init__(self,
335392 self .debug = bool (debug )
336393 self .debug_file = debug_file
337394 self ._slash_split_reg = re .compile ("(?<!/)/(?!/)" )
395+ self ._script_sources = []
338396
339397 # add all setvars to the readvars to allow accessing them after the
340398 # script is executed
@@ -373,7 +431,37 @@ def __call__(self) -> bool:
373431 "because file is running in debug mode." ).format (path ))
374432 return True
375433 else :
376- DM .ExecuteScriptString (dmscript )
434+ try :
435+ DM .ExecuteScriptString (dmscript )
436+ except RuntimeError as e :
437+ matches = re .match (r"Error in line ([\d]+)\s*\n(.*)" , str (e ))
438+
439+ if matches is not None :
440+ # there is an error in the executed script, read the line
441+ # and check in which script the error is, then calculate
442+ # the line in the script, this is better information for
443+ # the user as he/she doesn't know what code is prefixed
444+ # and suffixed
445+ line = int (matches .group (1 ))
446+
447+ for script_src in self ._script_sources :
448+ if (script_src ["start" ] <= line and
449+ line <= script_src ["end" ]):
450+ msg = matches .group (2 )
451+ src = script_src ["origin-name" ]
452+ l = line - script_src ["start" ] + 1
453+
454+ error = DMScriptError (msg , src , l , line )
455+
456+ # GMS shows the docstring in their error message,
457+ # so to offer a useful text, overwrite the docstring
458+ error .__doc__ = str (error )
459+ type(error ).__doc__ = str (error )
460+ DMScriptError .__doc__ = str (error )
461+
462+ raise error from e
463+ else :
464+ raise e
377465 self ._loadVariablesFromDMScript ()
378466 return True
379467
@@ -428,36 +516,84 @@ def getExecDMScriptCode(self) -> str:
428516 str
429517 The code to execute
430518 """
519+ self ._script_sources = []
520+ last_pos = 1
521+
431522 dmscript = [
432523 "// This code is created automatically by concatenating files and " ,
433524 "// code fragments." ,
434525 "// " ,
435- "// This code is generated with the exedmscript module."
436- "" ,
437- "" ,
438- self .getSetVarsDMCode ()
526+ "// This code is generated with the exedmscript module." ,
527+ "//" ,
528+ "//" + " =" * 50
439529 ]
530+ new_pos = last_pos + len (dmscript ) - 1
531+ self ._script_sources .append ({
532+ "start" : last_pos ,
533+ "end" : new_pos ,
534+ "origin-name" : "<comments>" ,
535+ "origin-detail" : None
536+ })
537+ last_pos = new_pos + 1
538+
539+ setvars_code = self .getSetVarsDMCode ()
540+ dmscript .append (setvars_code )
541+
542+ new_pos = last_pos + setvars_code .count ("\n " )
543+ self ._script_sources .append ({
544+ "start" : last_pos ,
545+ "end" : new_pos ,
546+ "origin-name" : "<setvars code>" ,
547+ "origin-detail" : self .getSetVarsDMCode
548+ })
549+ last_pos = new_pos + 1
440550
441- for kind , script in self .scripts :
551+ for i , ( kind , script ) in enumerate ( self .scripts ) :
442552 if isinstance (kind , str ):
443553 kind = kind .lower ()
444554
445555 if kind == "file" :
556+ source = script
557+ comment = "// File {}" .format (escape_dm_string (source ))
446558 with open (script , "r" ) as f :
447- dmscript += [
448- "// File {}" .format (script ),
449- f .read (),
450- ""
451- ]
559+ code = f .read ()
452560 elif kind == "script" :
453- dmscript += [
454- "// Directly given script" ,
455- script ,
456- ""
457- ]
561+ source = "<inline script in parameter {}>" .format (i )
562+ comment = "// Directly given script"
563+ code = script
564+
565+ dmscript .append (comment )
566+ new_pos = last_pos + comment .count ("\n " )
567+ self ._script_sources .append ({
568+ "start" : last_pos ,
569+ "end" : new_pos ,
570+ "origin-name" : "<comment>" ,
571+ "origin-detail" : None
572+ })
573+ last_pos = new_pos + 1
574+
575+ dmscript .append (code )
576+ new_pos = last_pos + code .count ("\n " )
577+ self ._script_sources .append ({
578+ "start" : last_pos ,
579+ "end" : new_pos ,
580+ "origin-name" : source ,
581+ "origin-detail" : script
582+ })
583+ last_pos = new_pos + 1
458584
459- dmscript .append (self .getSyncDMCode ())
585+ readvars_code = self .getSyncDMCode ()
586+ dmscript .append (readvars_code )
460587
588+ new_pos = last_pos + readvars_code .count ("\n " )
589+ self ._script_sources .append ({
590+ "start" : last_pos ,
591+ "end" : new_pos ,
592+ "origin-name" : "<readvars code>" ,
593+ "origin-type" : self .getSyncDMCode
594+ })
595+ last_pos = new_pos + 1
596+
461597 return "\n " .join (dmscript )
462598
463599 def getSetVarsDMCode (self ) -> str :
@@ -473,7 +609,6 @@ def getSetVarsDMCode(self) -> str:
473609 return ""
474610
475611 dmscript = [
476- "" ,
477612 "// Setting variables from python values"
478613 ]
479614 for name , val in self .setvars .items ():
0 commit comments