7474 The following virtual configurations are available:
7575""" .strip ()
7676
77+ class AutorandrException (Exception ):
78+ def __init__ (self , message , original_exception = None , report_bug = False ):
79+ self .message = message
80+ self .report_bug = report_bug
81+ if original_exception :
82+ self .original_exception = original_exception
83+ trace = sys .exc_info ()[2 ]
84+ while trace .tb_next :
85+ trace = trace .tb_next
86+ self .line = trace .tb_lineno
87+ else :
88+ try :
89+ import inspect
90+ self .line = inspect .currentframe ().f_back .f_lineno
91+ except :
92+ self .line = None
93+ self .original_exception = None
94+
95+ def __str__ (self ):
96+ retval = [ self .message ]
97+ if self .line :
98+ retval .append (" (line %d)" % self .line )
99+ if self .original_exception :
100+ retval .append (":\n " % self .line )
101+ retval .append (str (self .original_exception ).replace ("\n " , "\n " ))
102+ if self .report_bug :
103+ retval .append ("\n This appears to be a bug. Please help improving autorandr by reporting it upstream."
104+ "\n Please attach the output of `xrandr --verbose` to your bug report if appropriate." )
105+ return "" .join (retval )
106+
77107class XrandrOutput (object ):
78108 "Represents an XRandR output"
79109
@@ -199,22 +229,22 @@ def from_xrandr_output(cls, xrandr_output):
199229 xrandr_output = xrandr_output .replace ("\r \n " , "\n " )
200230 match_object = re .search (XrandrOutput .XRANDR_OUTPUT_REGEXP , xrandr_output )
201231 except :
202- raise RuntimeError ("Parsing XRandR output failed, there is an error in the regular expression." )
232+ raise AutorandrException ("Parsing XRandR output failed, there is an error in the regular expression." , report_bug = True )
203233 if not match_object :
204234 debug = debug_regexp (XrandrOutput .XRANDR_OUTPUT_REGEXP , xrandr_output )
205- raise RuntimeError ("Parsing XRandR output failed, the regular expression did not match: %s" % debug )
235+ raise AutorandrException ("Parsing XRandR output failed, the regular expression did not match: %s" % debug , report_bug = True )
206236 remainder = xrandr_output [len (match_object .group (0 )):]
207237 if remainder :
208- raise RuntimeError (("Parsing XRandR output failed, %d bytes left unmatched after regular expression, "
209- "starting at byte %d with ..'%s'." ) % (len (remainder ), len (match_object .group (0 )), remainder [:10 ]))
238+ raise AutorandrException (("Parsing XRandR output failed, %d bytes left unmatched after regular expression, "
239+ "starting at byte %d with ..'%s'." ) % (len (remainder ), len (match_object .group (0 )), remainder [:10 ]), report_bug = True )
210240
211241 match = match_object .groupdict ()
212242
213243 modes = []
214244 if match ["modes" ]:
215245 modes = [ x .groupdict () for x in re .finditer (XrandrOutput .XRANDR_OUTPUT_MODES_REGEXP , match ["modes" ]) ]
216246 if not modes :
217- raise RuntimeError ("Parsing XRandR output failed, couldn't find any display modes" )
247+ raise AutorandrException ("Parsing XRandR output failed, couldn't find any display modes" , report_bug = True )
218248
219249 options = {}
220250 if not match ["connected" ]:
@@ -285,8 +315,7 @@ def from_config_file(cls, edid_map, configuration):
285315 if fuzzy_output in fuzzy_edid_map :
286316 edid = edid_map [list (edid_map .keys ())[fuzzy_edid_map .index (fuzzy_output )]]
287317 elif "off" not in options :
288- raise RuntimeError ("Failed to find an EDID for output `%s' in setup file, required as `%s' is not off in config file."
289- % (options ["output" ], options ["output" ]))
318+ raise AutorandrException ("Failed to find an EDID for output `%s' in setup file, required as `%s' is not off in config file." % (options ["output" ], options ["output" ]))
290319 output = options ["output" ]
291320 del options ["output" ]
292321
@@ -332,21 +361,21 @@ def debug_regexp(pattern, string):
332361 string [partial_length :partial_length + 10 ]))
333362 except ImportError :
334363 pass
335- return "Debug information available if `regex' module is installed."
364+ return "Debug information would be available if the `regex' module was installed."
336365
337366def parse_xrandr_output ():
338367 "Parse the output of `xrandr --verbose' into a list of outputs"
339368 xrandr_output = os .popen ("xrandr -q --verbose" ).read ()
340369 if not xrandr_output :
341- raise RuntimeError ("Failed to run xrandr" )
370+ raise AutorandrException ("Failed to run xrandr" )
342371
343372 # We are not interested in screens
344373 xrandr_output = re .sub ("(?m)^Screen [0-9].+" , "" , xrandr_output ).strip ()
345374
346375 # Split at output boundaries and instanciate an XrandrOutput per output
347376 split_xrandr_output = re .split ("(?m)^([^ ]+ (?:(?:dis)?connected|unknown connection).*)$" , xrandr_output )
348377 if len (split_xrandr_output ) < 2 :
349- raise RuntimeError ("No output boundaries found" )
378+ raise AutorandrException ("No output boundaries found" , report_bug = True )
350379 outputs = OrderedDict ()
351380 modes = OrderedDict ()
352381 for i in range (1 , len (split_xrandr_output ), 2 ):
@@ -483,7 +512,7 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
483512 if auxiliary_changes_pre :
484513 argv = base_argv + list (chain .from_iterable (auxiliary_changes_pre ))
485514 if subprocess .call (argv ) != 0 :
486- raise RuntimeError ("Command failed: %s" % " " .join (argv ))
515+ raise AutorandrException ("Command failed: %s" % " " .join (argv ))
487516
488517 # Disable unused outputs, but make sure that there always is at least one active screen
489518 disable_keep = 0 if remain_active_count else 1
@@ -508,7 +537,7 @@ def apply_configuration(new_configuration, current_configuration, dry_run=False)
508537 for index in range (0 , len (operations ), 2 ):
509538 argv = base_argv + list (chain .from_iterable (operations [index :index + 2 ]))
510539 if subprocess .call (argv ) != 0 :
511- raise RuntimeError ("Command failed: %s" % " " .join (argv ))
540+ raise AutorandrException ("Command failed: %s" % " " .join (argv ))
512541
513542def add_unused_outputs (source_configuration , target_configuration ):
514543 "Add outputs that are missing in target to target, in 'off' state"
@@ -596,14 +625,9 @@ def main(argv):
596625 # Sort by descending mtime
597626 profiles = OrderedDict (sorted (profiles .items (), key = lambda x : - x [1 ]["config-mtime" ]))
598627 except Exception as e :
599- print ("Failed to load profiles:\n %s" % str (e ), file = sys .stderr )
600- sys .exit (1 )
628+ raise AutorandrException ("Failed to load profiles" , e )
601629
602- try :
603- config , modes = parse_xrandr_output ()
604- except Exception as e :
605- print ("Failed to parse current configuration from XRandR:\n %s" % str (e ), file = sys .stderr )
606- sys .exit (1 )
630+ config , modes = parse_xrandr_output ()
607631
608632 if "--fingerprint" in options :
609633 output_setup (config , sys .stdout )
@@ -617,13 +641,11 @@ def main(argv):
617641 options ["--save" ] = options ["-s" ]
618642 if "--save" in options :
619643 if options ["--save" ] in ( x [0 ] for x in virtual_profiles ):
620- print ("Cannot save current configuration as profile '%s':\n This configuration name is a reserved virtual configuration." % options ["--save" ])
621- sys .exit (1 )
644+ raise AutorandrException ("Cannot save current configuration as profile '%s':\n This configuration name is a reserved virtual configuration." % options ["--save" ])
622645 try :
623646 save_configuration (os .path .join (profile_path , options ["--save" ]), config )
624647 except Exception as e :
625- print ("Failed to save current configuration as profile '%s':\n %s" % (options ["--save" ], str (e )), file = sys .stderr )
626- sys .exit (1 )
648+ raise AutorandrException ("Failed to save current configuration as profile '%s'" % (options ["--save" ],), e )
627649 print ("Saved current configuration as profile '%s'" % options ["--save" ])
628650 sys .exit (0 )
629651
@@ -664,8 +686,7 @@ def main(argv):
664686 load_config = profile ["config" ]
665687 scripts_path = profile ["path" ]
666688 except KeyError :
667- print ("Failed to load profile '%s':\n Profile not found" % load_profile , file = sys .stderr )
668- sys .exit (1 )
689+ raise AutorandrException ("Failed to load profile '%s': Profile not found" % load_profile )
669690 if load_profile in detected_profiles and detected_profiles [0 ] != load_profile :
670691 update_mtime (os .path .join (scripts_path , "config" ))
671692 add_unused_outputs (config , load_config )
@@ -682,14 +703,20 @@ def main(argv):
682703 apply_configuration (load_config , config , False )
683704 exec_scripts (scripts_path , "postswitch" )
684705 except Exception as e :
685- print ("Failed to apply profile '%s':\n %s" % (load_profile , str (e )), file = sys .stderr )
686- sys .exit (1 )
706+ raise AutorandrException ("Failed to apply profile '%s'" % load_profile , e , True )
687707
688708 sys .exit (0 )
689709
690710if __name__ == '__main__' :
691711 try :
692712 main (sys .argv )
713+ except AutorandrException as e :
714+ print (file = sys .stderr )
715+ print (e , file = sys .stderr )
716+ sys .exit (1 )
693717 except Exception as e :
694- print ("General failure. Please report this as a bug:\n %s" % (str (e ),), file = sys .stderr )
718+ trace = sys .exc_info ()[2 ]
719+ while trace .tb_next :
720+ trace = trace .tb_next
721+ print ("\n Unhandled exception in line %d. Please report this as a bug:\n %s" % (trace .tb_lineno , "\n " .join (str (e ).split ("\n " )),), file = sys .stderr )
695722 sys .exit (1 )
0 commit comments