@@ -221,6 +221,10 @@ def stop(self):
221
221
try :
222
222
logger .info ("proc.terminate with pid %s" , self .proc .pid )
223
223
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 )
224
228
if utils .PY3 :
225
229
# pylint:disable=no-member
226
230
_except = subprocess .TimeoutExpired
@@ -285,6 +289,24 @@ def start(self, app, start_timeout=2, cwd=None):
285
289
break
286
290
if cwd :
287
291
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
+
288
310
else :
289
311
logger .warning (
290
312
"RRunner found no cwd in the Python call stack. "
@@ -293,23 +315,6 @@ def start(self, app, start_timeout=2, cwd=None):
293
315
"dashr.run_server(app, cwd=os.path.dirname(__file__))"
294
316
)
295
317
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
-
313
318
logger .info ("Run dashR app with Rscript => %s" , app )
314
319
315
320
args = shlex .split (
@@ -334,3 +339,100 @@ def start(self, app, start_timeout=2, cwd=None):
334
339
return
335
340
336
341
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