python.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package python
  2. import (
  3. _ "embed"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/google/uuid"
  11. "github.com/langgenius/dify-sandbox/internal/core/runner"
  12. python_dependencies "github.com/langgenius/dify-sandbox/internal/core/runner/python/dependencies"
  13. "github.com/langgenius/dify-sandbox/internal/core/runner/types"
  14. "github.com/langgenius/dify-sandbox/internal/static"
  15. )
  16. type PythonRunner struct {
  17. runner.TempDirRunner
  18. }
  19. //go:embed prescript.py
  20. var python_sandbox_fs []byte
  21. var (
  22. PYTHON_REQUIRED_FS = []string{
  23. "/tmp/sandbox-python/python.so",
  24. }
  25. )
  26. func (p *PythonRunner) Run(
  27. code string,
  28. timeout time.Duration,
  29. stdin []byte,
  30. preload string,
  31. options *types.RunnerOptions,
  32. ) (chan []byte, chan []byte, chan bool, error) {
  33. // initialize the environment
  34. untrusted_code_path, preload_script, err := p.InitializeEnvironment(code, preload, options)
  35. if err != nil {
  36. return nil, nil, nil, err
  37. }
  38. // capture the output
  39. output_handler := runner.NewOutputCaptureRunner()
  40. output_handler.SetTimeout(timeout)
  41. err = p.WithTempDir(PYTHON_REQUIRED_FS, func(root_path string) error {
  42. // cleanup
  43. output_handler.SetAfterExitHook(func() {
  44. os.RemoveAll(root_path)
  45. os.Remove(root_path)
  46. })
  47. // create a new process
  48. cmd := exec.Command(
  49. static.GetDifySandboxGlobalConfigurations().PythonPath,
  50. "-c",
  51. preload_script,
  52. untrusted_code_path,
  53. strconv.Itoa(static.SANDBOX_USER_UID),
  54. strconv.Itoa(static.SANDBOX_GROUP_ID),
  55. options.Json(),
  56. )
  57. cmd.Env = []string{}
  58. err = output_handler.CaptureOutput(cmd)
  59. if err != nil {
  60. return err
  61. }
  62. return nil
  63. })
  64. if err != nil {
  65. return nil, nil, nil, err
  66. }
  67. return output_handler.GetStdout(), output_handler.GetStderr(), output_handler.GetDone(), nil
  68. }
  69. func (p *PythonRunner) InitializeEnvironment(code string, preload string, options *types.RunnerOptions) (string, string, error) {
  70. // create a tmp dir and copy the python script
  71. temp_code_name := strings.ReplaceAll(uuid.New().String(), "-", "_")
  72. temp_code_name = strings.ReplaceAll(temp_code_name, "/", ".")
  73. untrusted_code_path := fmt.Sprintf("/tmp/code/%s.py", temp_code_name)
  74. err := os.MkdirAll("/tmp/code", 0755)
  75. if err != nil {
  76. return "", "", err
  77. }
  78. err = os.WriteFile(untrusted_code_path, []byte(code), 0755)
  79. if err != nil {
  80. return "", "", err
  81. }
  82. preload_script := string(python_sandbox_fs)
  83. if preload != "" {
  84. preload_script = fmt.Sprintf("%s\n%s", preload, preload_script)
  85. }
  86. packages_preload := make([]string, len(options.Dependencies))
  87. for i, dependency := range options.Dependencies {
  88. packages_preload[i] = python_dependencies.GetDependencies(dependency.Name, dependency.Version)
  89. }
  90. if len(packages_preload) != 0 {
  91. preload_script = fmt.Sprintf("%s\n%s", strings.Join(packages_preload, "\n"), preload_script)
  92. }
  93. return untrusted_code_path, preload_script, nil
  94. }