diff --git a/internal/python/plan.go b/internal/python/plan.go index 7db83e0f..cb81648b 100644 --- a/internal/python/plan.go +++ b/internal/python/plan.go @@ -53,6 +53,11 @@ func DetermineFramework(ctx *pythonPlanContext) types.PythonFramework { return fw.Unwrap() } + if HasDependencyWithFile(ctx, "sanic") { + *fw = optional.Some(types.PythonFrameworkSanic) + return fw.Unwrap() + } + *fw = optional.Some(types.PythonFrameworkNone) return fw.Unwrap() } @@ -66,7 +71,7 @@ func DetermineEntry(ctx *pythonPlanContext) string { return entry } - for _, file := range []string{"main.py", "app.py", "manage.py"} { + for _, file := range []string{"main.py", "app.py", "manage.py", "server.py"} { if utils.HasFile(src, file) { *et = optional.Some(file) return et.Unwrap() @@ -261,6 +266,24 @@ func DetermineWsgi(ctx *pythonPlanContext) string { return "" } + if framework == types.PythonFrameworkSanic { + entryFile := DetermineEntry(ctx) + + re := regexp.MustCompile(`(\w+)\s*=\s*Sanic\([^)]*\)`) + content, err := afero.ReadFile(src, entryFile) + if err != nil { + return "" + } + + match := re.FindStringSubmatch(string(content)) + if len(match) > 1 { + entryWithoutExt := strings.TrimSuffix(entryFile, ".py") + *wa = optional.Some(entryWithoutExt + ":" + match[1]) + return wa.Unwrap() + } + return "" + } + return "" } @@ -535,6 +558,8 @@ func determineStartCmd(ctx *pythonPlanContext) string { if framework == types.PythonFrameworkFastapi { commandSegment = append(commandSegment, "uvicorn", wsgi, "--host 0.0.0.0", "--port "+wsgilistenedPort) + } else if framework == types.PythonFrameworkSanic { + commandSegment = append(commandSegment, "sanic", wsgi, "--host 0.0.0.0", "--port "+wsgilistenedPort) } else { commandSegment = append(commandSegment, "gunicorn", "--bind :"+wsgilistenedPort, wsgi) } diff --git a/pkg/types/plan.go b/pkg/types/plan.go index 9eec0a6f..2d7a5936 100644 --- a/pkg/types/plan.go +++ b/pkg/types/plan.go @@ -80,6 +80,7 @@ const ( PythonFrameworkFlask PythonFramework = "flask" PythonFrameworkDjango PythonFramework = "django" PythonFrameworkFastapi PythonFramework = "fastapi" + PythonFrameworkSanic PythonFramework = "sanic" PythonFrameworkNone PythonFramework = "none" ) diff --git a/tests/python-sanic/requirements.txt b/tests/python-sanic/requirements.txt new file mode 100644 index 00000000..fe6e6292 --- /dev/null +++ b/tests/python-sanic/requirements.txt @@ -0,0 +1 @@ +sanic diff --git a/tests/python-sanic/server.py b/tests/python-sanic/server.py new file mode 100644 index 00000000..5b73dcde --- /dev/null +++ b/tests/python-sanic/server.py @@ -0,0 +1,12 @@ +from sanic import Sanic +from sanic.response import text + +app = Sanic("proxied_example") +app.config.FORWARDED_SECRET = "YOUR SECRET" + +@app.get("/zeabur") +def index(request): + return text("zeabur") + +if __name__ == "__main__": + app.run(host="127.0.0.1", port=8000, workers=8, access_log=False)