github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/run_python.go (about) 1 // Copyright 2020 (c) Cognizant Digital Business, Evolutionary AI. All rights reserved. Issued under the Apache 2.0 License. 2 3 package runner 4 5 import ( 6 "context" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 11 "github.com/go-stack/stack" 12 "github.com/jjeffery/kv" 13 ) 14 15 // PythonRun will use the set of test files to start a python process that will return 16 // the console output and/or an error. The caller can specify the max size of the buffer 17 // used to hold the last (keepLines) of lines from the console. If tmpDir is specified 18 // that directory will be used to run the process and will not be removed after the 19 // function completes, in the case it is blank then the function will generate a directory 20 // run the python in it then remove it. 21 // 22 func PythonRun(testFiles map[string]os.FileMode, tmpDir string, keepLines uint) (output []string, err kv.Error) { 23 24 output = []string{} 25 26 // I fthe optional temporary directory is not supplied then we create, 27 // use it and then remove it, this allows callers to load data into the 28 // directory they supply if they wish 29 if len(tmpDir) == 0 { 30 // Create a new TMPDIR because the python pip tends to leave dirt behind 31 // when doing pip builds etc 32 t, errGo := ioutil.TempDir("", "") 33 if errGo != nil { 34 return nil, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 35 } 36 defer func() { 37 os.RemoveAll(t) 38 }() 39 tmpDir = t 40 } 41 42 expectedScript := "" 43 44 for fn, mode := range testFiles { 45 assetFN, errGo := filepath.Abs(fn) 46 if errGo != nil { 47 return nil, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 48 } 49 50 destFN, errGo := filepath.Abs(filepath.Join(tmpDir, filepath.Base(assetFN))) 51 if errGo != nil { 52 return nil, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 53 } 54 55 if _, err := CopyFile(assetFN, destFN); err != nil { 56 return nil, err 57 } 58 59 if errGo = os.Chmod(destFN, mode); errGo != nil { 60 return nil, kv.Wrap(errGo).With("destFN", destFN, "stack", stack.Trace().TrimRuntime()) 61 } 62 63 if mode == 0700 { 64 expectedScript = destFN 65 } 66 } 67 68 // Save the output from the run using the last say 10 lines as a default otherwise 69 // use the callers specified number of lines if they specified any 70 if keepLines == 0 { 71 keepLines = 20 72 } 73 output = make([]string, 0, keepLines) 74 75 // Now setup is done execute the experiment 76 dataC := make(chan *string, 1) 77 go func() { 78 for { 79 select { 80 case line := <-dataC: 81 if line == nil { 82 return 83 } 84 // Push to the back of the stack of lines, then pop from the front 85 output = append(output, *line) 86 if len(output) > int(keepLines) { 87 output = output[1:] 88 } 89 } 90 } 91 }() 92 93 // When running python change directory into the temporary sandbox we are using 94 originalDir, errGo := os.Getwd() 95 if errGo != nil { 96 return nil, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 97 } 98 99 if tmpDir != originalDir { 100 if errGo = os.Chdir(tmpDir); errGo != nil { 101 return nil, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime()) 102 } 103 // Once the python run is done we jump back to the original directory 104 defer os.Chdir(originalDir) 105 } 106 107 return output, CmdRun(context.TODO(), expectedScript, dataC) 108 }