feat: pyrunner mejorado para fn run Python

Refactoriza la ejecucion de funciones Python en fn run. Extrae la logica
a pyrunner.go con soporte para importar dependencias del registry y
ejecutar con el venv del proyecto. Agrega WalCheckpoint en db.go para
que lectores externos vean datos actualizados tras fn index.
This commit is contained in:
2026-03-29 00:13:46 +01:00
parent cd09ddcc76
commit a4b5651e8c
3 changed files with 468 additions and 43 deletions
+3 -43
View File
@@ -46,7 +46,7 @@ func cmdRun(args []string) {
os.Exit(1)
}
cmd, err := buildCommand(fn, registryRoot, absPath, passArgs)
cmd, err := buildCommand(fn, db, registryRoot, absPath, passArgs)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
@@ -93,12 +93,12 @@ func resolveFunction(db *registry.DB, idOrName string) (*registry.Function, erro
return nil, fmt.Errorf("%s", b.String())
}
func buildCommand(fn *registry.Function, registryRoot, absPath string, args []string) (*exec.Cmd, error) {
func buildCommand(fn *registry.Function, db *registry.DB, registryRoot, absPath string, args []string) (*exec.Cmd, error) {
switch fn.Lang {
case "go":
return buildGoCommand(fn, registryRoot, absPath, args)
case "py":
return buildPyCommand(registryRoot, absPath, args)
return buildPyRunnerCommand(fn, db, registryRoot, args)
case "bash":
return buildBashCommand(absPath, args)
case "ts":
@@ -147,46 +147,6 @@ func buildGoCommand(fn *registry.Function, registryRoot, absPath string, args []
return cmd, nil
}
func buildPyCommand(registryRoot, absPath string, args []string) (*exec.Cmd, error) {
venvPython := filepath.Join(registryRoot, "python", ".venv", "bin", "python3")
pythonBin := "python3"
if _, err := os.Stat(venvPython); err == nil {
pythonBin = venvPython
}
dir := filepath.Dir(absPath)
// If the file is inside a package (has __init__.py), use python -m
// so relative imports work. PYTHONPATH points to python/functions/ or
// the equivalent parent that contains the domain packages.
initPy := filepath.Join(dir, "__init__.py")
if _, err := os.Stat(initPy); err == nil {
// The pythonPath is the well-known python/functions/ directory
// which contains domain packages (metabase/, etc.)
pythonPath := filepath.Join(registryRoot, "python", "functions")
if _, err := os.Stat(pythonPath); os.IsNotExist(err) {
// Fallback: walk up from dir to find the parent of the top package
pythonPath = filepath.Dir(dir)
}
// Build module path: metabase/databases.py → metabase.databases
relToRoot, _ := filepath.Rel(pythonPath, absPath)
modPath := strings.TrimSuffix(relToRoot, ".py")
modPath = strings.ReplaceAll(filepath.ToSlash(modPath), "/", ".")
cmdArgs := append([]string{"-m", modPath}, args...)
cmd := exec.Command(pythonBin, cmdArgs...)
cmd.Dir = pythonPath
cmd.Env = append(os.Environ(), "PYTHONPATH="+pythonPath)
return cmd, nil
}
// Standalone script (no __init__.py)
cmdArgs := append([]string{absPath}, args...)
cmd := exec.Command(pythonBin, cmdArgs...)
cmd.Dir = dir
return cmd, nil
}
func buildBashCommand(absPath string, args []string) (*exec.Cmd, error) {
cmdArgs := append([]string{absPath}, args...)