2222
2323psutil = get_module ('psutil' )
2424
25+ from _plotly_future_ import _future_flags
26+
2527# Valid image format constants
2628# ----------------------------
2729valid_formats = ('png' , 'jpeg' , 'webp' , 'svg' , 'pdf' , 'eps' )
@@ -417,7 +419,11 @@ def executable(self):
417419 -------
418420 str
419421 """
420- return self ._props .get ('executable' , 'orca' )
422+ executable_list = self ._props .get ('executable_list' , ['orca' ])
423+ if executable_list is None :
424+ return None
425+ else :
426+ return ' ' .join (executable_list )
421427
422428 @executable .setter
423429 def executable (self , val ):
@@ -429,7 +435,9 @@ def executable(self, val):
429435 raise ValueError ("""
430436The executable property must be a string, but received value of type {typ}.
431437 Received value: {val}""" .format (typ = type (val ), val = val ))
432- self ._props ['executable' ] = val
438+ if isinstance (val , string_types ):
439+ val = [val ]
440+ self ._props ['executable_list' ] = val
433441
434442 # Server and validation must restart before setting is active
435443 reset_status ()
@@ -661,6 +669,28 @@ def mapbox_access_token(self, val):
661669 # Server must restart before setting is active
662670 shutdown_server ()
663671
672+ @property
673+ def use_xvfb (self ):
674+ dflt = 'auto' if 'orca_defaults' in _future_flags else False
675+ return self ._props .get ('use_xvfb' , dflt )
676+
677+ @use_xvfb .setter
678+ def use_xvfb (self , val ):
679+ valid_vals = [True , False , 'auto' ]
680+ if val is None :
681+ self ._props .pop ('use_xvfb' , None )
682+ else :
683+ if val not in valid_vals :
684+ raise ValueError ("""
685+ The use_xvfb property must be one of {valid_vals}
686+ Received value of type {typ}: {val}""" .format (
687+ valid_vals = valid_vals , typ = type (val ), val = repr (val )))
688+
689+ self ._props ['use_xvfb' ] = val
690+
691+ # Server and validation must restart before setting is active
692+ reset_status ()
693+
664694 @property
665695 def plotlyjs (self ):
666696 """
@@ -704,6 +734,7 @@ def __repr__(self):
704734 mathjax: {mathjax}
705735 topojson: {topojson}
706736 mapbox_access_token: {mapbox_access_token}
737+ use_xvfb: {use_xvfb}
707738
708739constants
709740---------
@@ -721,7 +752,8 @@ def __repr__(self):
721752 topojson = self .topojson ,
722753 mapbox_access_token = self .mapbox_access_token ,
723754 plotlyjs = self .plotlyjs ,
724- config_file = self .config_file )
755+ config_file = self .config_file ,
756+ use_xvfb = self .use_xvfb )
725757
726758
727759# Make config a singleton object
@@ -738,7 +770,7 @@ class OrcaStatus(object):
738770 """
739771 _props = {
740772 'state' : 'unvalidated' , # or 'validated' or 'running'
741- 'executable ' : None ,
773+ 'executable_list ' : None ,
742774 'version' : None ,
743775 'pid' : None ,
744776 'port' : None ,
@@ -770,7 +802,11 @@ def executable(self):
770802
771803 This property will be None if the `state` is 'unvalidated'.
772804 """
773- return self ._props ['executable' ]
805+ executable_list = self ._props ['executable_list' ]
806+ if executable_list is None :
807+ return None
808+ else :
809+ return ' ' .join (executable_list )
774810
775811 @property
776812 def version (self ):
@@ -851,7 +887,11 @@ def orca_env():
851887 to orca is transformed into a call to nodejs.
852888 See https://github.com/plotly/orca/issues/149#issuecomment-443506732
853889 """
854- clear_env_vars = ['NODE_OPTIONS' , 'ELECTRON_RUN_AS_NODE' ]
890+ clear_env_vars = [
891+ 'NODE_OPTIONS' ,
892+ 'ELECTRON_RUN_AS_NODE' ,
893+ 'LD_PRELOAD'
894+ ]
855895 orig_env_vars = {}
856896
857897 try :
@@ -932,11 +972,10 @@ def validate_executable():
932972 # -------------------------
933973 # Search for executable name or path in config.executable
934974 executable = which (config .executable )
975+ path = os .environ .get ("PATH" , os .defpath )
976+ formatted_path = path .replace (os .pathsep , '\n ' )
935977
936978 if executable is None :
937- path = os .environ .get ("PATH" , os .defpath )
938- formatted_path = path .replace (os .pathsep , '\n ' )
939-
940979 raise ValueError ("""
941980The orca executable is required to export figures as static images,
942981but it could not be found on the system path.
@@ -949,6 +988,37 @@ def validate_executable():
949988 formatted_path = formatted_path ,
950989 instructions = install_location_instructions ))
951990
991+ # Check if we should run with Xvfb
992+ # --------------------------------
993+ xvfb_args = ["--auto-servernum" ,
994+ "--server-args" ,
995+ "-screen 0 640x480x24 +extension RANDR +extension GLX" ,
996+ executable ]
997+
998+ if config .use_xvfb == True :
999+ # Use xvfb
1000+ xvfb_run_executable = which ('xvfb-run' )
1001+ if not xvfb_run_executable :
1002+ raise ValueError ("""
1003+ The plotly.io.orca.config.use_xvfb property is set to True, but the
1004+ xvfb-run executable could not be found on the system path.
1005+
1006+ Searched for the executable 'xvfb-run' on the following path:
1007+ {formatted_path}""" .format (formatted_path = formatted_path ))
1008+
1009+ executable_list = [xvfb_run_executable ] + xvfb_args
1010+ elif (config .use_xvfb == 'auto' and
1011+ sys .platform .startswith ('linux' ) and
1012+ not os .environ .get ('DISPLAY' ) and
1013+ which ('xvfb-run' )):
1014+ # use_xvfb is 'auto', we're on linux without a display server,
1015+ # and xvfb-run is available. Use it.
1016+ xvfb_run_executable = which ('xvfb-run' )
1017+ executable_list = [xvfb_run_executable ] + xvfb_args
1018+ else :
1019+ # Do not use xvfb
1020+ executable_list = [executable ]
1021+
9521022 # Run executable with --help and see if it's our orca
9531023 # ---------------------------------------------------
9541024 invalid_executable_msg = """
@@ -964,7 +1034,7 @@ def validate_executable():
9641034 # ### Run with Popen so we get access to stdout and stderr
9651035 with orca_env ():
9661036 p = subprocess .Popen (
967- [ executable , '--help' ],
1037+ executable_list + [ '--help' ],
9681038 stdout = subprocess .PIPE ,
9691039 stderr = subprocess .PIPE )
9701040
@@ -977,7 +1047,7 @@ def validate_executable():
9771047
9781048[Return code: {returncode}]
9791049{err_msg}
980- """ .format (executable = executable ,
1050+ """ .format (executable = ' ' . join ( executable_list ) ,
9811051 err_msg = help_error .decode ('utf-8' ),
9821052 returncode = p .returncode )
9831053
@@ -987,17 +1057,25 @@ def validate_executable():
9871057
9881058 err_msg += """\
9891059 Note: When used on Linux, orca requires an X11 display server, but none was
990- detected. Please install X11, or configure your system with Xvfb. See
991- the orca README (https://github.com/plotly/orca) for instructions on using
992- orca with Xvfb.
1060+ detected. Please install Xvfb and configure plotly.py to run orca using Xvfb
1061+ as follows:
1062+
1063+ >>> import plotly.io as pio
1064+ >>> pio.orca.config.use_xvfb = True
1065+
1066+ You can save this configuration for use in future sessions as follows:
1067+ >>> pio.orca.config.save()
1068+
1069+ See https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml
1070+ for more info on Xvfb
9931071"""
9941072 raise ValueError (err_msg )
9951073
9961074 if not help_result :
9971075 raise ValueError (invalid_executable_msg + """
9981076The error encountered is that no output was returned by the command
9991077 $ {executable} --help
1000- """ .format (executable = executable ))
1078+ """ .format (executable = ' ' . join ( executable_list ) ))
10011079
10021080 if ("Plotly's image-exporting utilities" not in
10031081 help_result .decode ('utf-8' )):
@@ -1006,14 +1084,14 @@ def validate_executable():
10061084 $ {executable} --help
10071085
10081086{help_result}
1009- """ .format (executable = executable , help_result = help_result ))
1087+ """ .format (executable = ' ' . join ( executable_list ) , help_result = help_result ))
10101088
10111089 # Get orca version
10121090 # ----------------
10131091 # ### Run with Popen so we get access to stdout and stderr
10141092 with orca_env ():
10151093 p = subprocess .Popen (
1016- [ executable , '--version' ],
1094+ executable_list + [ '--version' ],
10171095 stdout = subprocess .PIPE ,
10181096 stderr = subprocess .PIPE )
10191097
@@ -1029,7 +1107,7 @@ def validate_executable():
10291107
10301108[Return code: {returncode}]
10311109{err_msg}
1032- """ .format (executable = executable ,
1110+ """ .format (executable = ' ' . join ( executable_list ) ,
10331111 err_msg = version_error .decode ('utf-8' ),
10341112 returncode = p .returncode ))
10351113
@@ -1039,11 +1117,11 @@ def validate_executable():
10391117Here is the command that plotly.py ran to request the version:
10401118
10411119 $ {executable} --version
1042- """ .format (executable = executable ))
1120+ """ .format (executable = ' ' . join ( executable_list ) ))
10431121 else :
10441122 version_result = version_result .decode ()
10451123
1046- status ._props ['executable ' ] = executable
1124+ status ._props ['executable_list ' ] = executable_list
10471125 status ._props ['version' ] = version_result .strip ()
10481126 status ._props ['state' ] = 'validated'
10491127
@@ -1061,7 +1139,7 @@ def reset_status():
10611139 None
10621140 """
10631141 shutdown_server ()
1064- status ._props ['executable ' ] = None
1142+ status ._props ['executable_list ' ] = None
10651143 status ._props ['version' ] = None
10661144 status ._props ['state' ] = 'unvalidated'
10671145
@@ -1179,10 +1257,11 @@ def ensure_server():
11791257 orca_state ['port' ] = config .port
11801258
11811259 # Build orca command list
1182- cmd_list = [status .executable , 'serve' ,
1183- '-p' , str (orca_state ['port' ]),
1184- '--plotly' , config .plotlyjs ,
1185- '--graph-only' ]
1260+ cmd_list = status ._props ['executable_list' ] + [
1261+ 'serve' ,
1262+ '-p' , str (orca_state ['port' ]),
1263+ '--plotly' , config .plotlyjs ,
1264+ '--graph-only' ]
11861265
11871266 if config .topojson :
11881267 cmd_list .extend (['--topojson' , config .topojson ])
@@ -1198,8 +1277,9 @@ def ensure_server():
11981277 # specified port.
11991278 DEVNULL = open (os .devnull , 'wb' )
12001279 with orca_env ():
1201- orca_state ['proc' ] = subprocess .Popen (cmd_list ,
1202- stdout = DEVNULL )
1280+ orca_state ['proc' ] = subprocess .Popen (
1281+ cmd_list , stdout = DEVNULL
1282+ )
12031283
12041284 # Update orca.status so the user has an accurate view
12051285 # of the state of the orca server
0 commit comments