@@ -221,6 +221,10 @@ def stop(self):
221221 try :
222222 logger .info ("proc.terminate with pid %s" , self .proc .pid )
223223 self .proc .terminate ()
224+ if self .tmp_app_path and os .path .exists (self .tmp_app_path ):
225+ logger .debug ("removing temporary app path %s" ,
226+ self .tmp_app_path )
227+ shutil .rmtree (self .tmp_app_path )
224228 if utils .PY3 :
225229 # pylint:disable=no-member
226230 _except = subprocess .TimeoutExpired
@@ -285,6 +289,24 @@ def start(self, app, start_timeout=2, cwd=None):
285289 break
286290 if cwd :
287291 logger .info ("RRunner inferred cwd from the Python call stack: %s" , cwd )
292+
293+ # try copying all valid sub folders (i.e. assets) in cwd to tmp
294+ # note that the R assets folder name can be any valid folder name
295+ assets = [
296+ os .path .join (cwd , _ )
297+ for _ in os .listdir (cwd )
298+ if not _ .startswith ("__" ) and os .path .isdir (os .path .join (cwd , _ ))
299+ ]
300+
301+ for asset in assets :
302+ target = os .path .join (self .tmp_app_path , os .path .basename (asset ))
303+ if os .path .exists (target ):
304+ logger .debug ("delete existing target %s" , target )
305+ shutil .rmtree (target )
306+ logger .debug ("copying %s => %s" , asset , self .tmp_app_path )
307+ shutil .copytree (asset , target )
308+ logger .debug ("copied with %s" , os .listdir (target ))
309+
288310 else :
289311 logger .warning (
290312 "RRunner found no cwd in the Python call stack. "
@@ -293,23 +315,6 @@ def start(self, app, start_timeout=2, cwd=None):
293315 "dashr.run_server(app, cwd=os.path.dirname(__file__))"
294316 )
295317
296- # try copying all valid sub folders (i.e. assets) in cwd to tmp
297- # note that the R assets folder name can be any valid folder name
298- assets = [
299- os .path .join (cwd , _ )
300- for _ in os .listdir (cwd )
301- if not _ .startswith ("__" ) and os .path .isdir (os .path .join (cwd , _ ))
302- ]
303-
304- for asset in assets :
305- target = os .path .join (self .tmp_app_path , os .path .basename (asset ))
306- if os .path .exists (target ):
307- logger .debug ("delete existing target %s" , target )
308- shutil .rmtree (target )
309- logger .debug ("copying %s => %s" , asset , self .tmp_app_path )
310- shutil .copytree (asset , target )
311- logger .debug ("copied with %s" , os .listdir (target ))
312-
313318 logger .info ("Run dashR app with Rscript => %s" , app )
314319
315320 args = shlex .split (
@@ -334,3 +339,100 @@ def start(self, app, start_timeout=2, cwd=None):
334339 return
335340
336341 self .started = True
342+
343+
344+ class JuliaRunner (ProcessRunner ):
345+ def __init__ (self , keep_open = False , stop_timeout = 3 ):
346+ super (JuliaRunner , self ).__init__ (keep_open = keep_open , stop_timeout = stop_timeout )
347+ self .proc = None
348+
349+ # pylint: disable=arguments-differ
350+ def start (self , app , start_timeout = 30 , cwd = None ):
351+ """Start the server with subprocess and julia."""
352+
353+ if os .path .isfile (app ) and os .path .exists (app ):
354+ # app is already a file in a dir - use that as cwd
355+ if not cwd :
356+ cwd = os .path .dirname (app )
357+ logger .info ("JuliaRunner inferred cwd from app path: %s" , cwd )
358+ else :
359+ # app is a string chunk, we make a temporary folder to store app.jl
360+ # and its relevants assets
361+ self ._tmp_app_path = os .path .join (
362+ "/tmp" if not self .is_windows else os .getenv ("TEMP" ), uuid .uuid4 ().hex
363+ )
364+ try :
365+ os .mkdir (self .tmp_app_path )
366+ except OSError :
367+ logger .exception ("cannot make temporary folder %s" , self .tmp_app_path )
368+ path = os .path .join (self .tmp_app_path , "app.jl" )
369+
370+ logger .info ("JuliaRunner start => app is Julia code chunk" )
371+ logger .info ("make a temporary Julia file for execution => %s" , path )
372+ logger .debug ("content of the Dash.jl app" )
373+ logger .debug ("%s" , app )
374+
375+ with open (path , "w" ) as fp :
376+ fp .write (app )
377+
378+ app = path
379+
380+ # try to find the path to the calling script to use as cwd
381+ if not cwd :
382+ for entry in inspect .stack ():
383+ if "/dash/testing/" not in entry [1 ].replace ("\\ " , "/" ):
384+ cwd = os .path .dirname (os .path .realpath (entry [1 ]))
385+ logger .warning ("get cwd from inspect => %s" , cwd )
386+ break
387+ if cwd :
388+ logger .info ("JuliaRunner inferred cwd from the Python call stack: %s" , cwd )
389+
390+ # try copying all valid sub folders (i.e. assets) in cwd to tmp
391+ # note that the R assets folder name can be any valid folder name
392+ assets = [
393+ os .path .join (cwd , _ )
394+ for _ in os .listdir (cwd )
395+ if not _ .startswith ("__" ) and os .path .isdir (os .path .join (cwd , _ ))
396+ ]
397+
398+ for asset in assets :
399+ target = os .path .join (self .tmp_app_path , os .path .basename (asset ))
400+ if os .path .exists (target ):
401+ logger .debug ("delete existing target %s" , target )
402+ shutil .rmtree (target )
403+ logger .debug ("copying %s => %s" , asset , self .tmp_app_path )
404+ shutil .copytree (asset , target )
405+ logger .debug ("copied with %s" , os .listdir (target ))
406+
407+ else :
408+ logger .warning (
409+ "JuliaRunner found no cwd in the Python call stack. "
410+ "You may wish to specify an explicit working directory "
411+ "using something like: "
412+ "dashjl.run_server(app, cwd=os.path.dirname(__file__))"
413+ )
414+
415+ logger .info ("Run Dash.jl app with julia => %s" , app )
416+
417+ args = shlex .split (
418+ "julia {}" .format (os .path .realpath (app )),
419+ posix = not self .is_windows ,
420+ )
421+ logger .debug ("start Dash.jl process with %s" , args )
422+
423+ try :
424+ self .proc = subprocess .Popen (
425+ args ,
426+ stdout = subprocess .PIPE ,
427+ stderr = subprocess .PIPE ,
428+ cwd = self .tmp_app_path if self .tmp_app_path else cwd ,
429+ )
430+ # wait until server is able to answer http request
431+ wait .until (lambda : self .accessible (self .url ), timeout = start_timeout )
432+
433+ except (OSError , ValueError ):
434+ logger .exception ("process server has encountered an error" )
435+ self .started = False
436+ return
437+
438+ self .started = True
0 commit comments