Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python-package #52

Merged
merged 40 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ca17037
CLI command for starting wsgi server
mliezun Dec 23, 2024
8a153b5
CLI command for ASGI server
mliezun Dec 23, 2024
da5eadd
Go mod tidy
mliezun Dec 23, 2024
3e12d98
Python build packages
mliezun Dec 23, 2024
bf1fe65
Fix cibuildwheel
mliezun Dec 23, 2024
7675ae3
CIBW_BUILD_VERBOSITY
mliezun Dec 23, 2024
e038f6a
Use dummy.c to generate platform package
mliezun Dec 23, 2024
9031c4c
Only write dummy.c after compiling go
mliezun Dec 23, 2024
0e702cb
Actualizar python-build.yml
mliezun Dec 24, 2024
194343b
python-build.yml
mliezun Dec 24, 2024
cd6017d
Publish pypi package
mliezun Dec 24, 2024
7eb1433
Use cibuildwheel
mliezun Dec 24, 2024
270f88b
Use CIBW_BUILD var
mliezun Dec 24, 2024
6d57387
manylinux_2_34
mliezun Dec 24, 2024
c00e9c0
manylinux_2_28
mliezun Dec 24, 2024
550351b
Build using quay.io/pypa/manylinux2014_x86_64
mliezun Dec 24, 2024
5fe9347
Use container image
mliezun Dec 24, 2024
587a7a1
Yum accept installs
mliezun Dec 24, 2024
7da453d
Fix gvm install
mliezun Dec 24, 2024
c8fb03f
source /github/home/.gvm/scripts/gvm
mliezun Dec 24, 2024
56e9776
source as .
mliezun Dec 24, 2024
7e984cc
Fix gvm
mliezun Dec 24, 2024
a3ca00a
update ci
mliezun Dec 24, 2024
459badb
Easier installation of go
mliezun Dec 24, 2024
c76d248
export path
mliezun Dec 24, 2024
089a11d
go version
mliezun Dec 24, 2024
eae3da3
Install nodejs
mliezun Dec 24, 2024
a6e141c
Manually checkout repo
mliezun Dec 24, 2024
ad1753d
fix git clone
mliezun Dec 24, 2024
c2785a9
fetch
mliezun Dec 24, 2024
02151ce
Fix go module tests
mliezun Dec 24, 2024
2637673
fix last commit sha
mliezun Dec 24, 2024
acd9e85
Fix pkg config path
mliezun Dec 24, 2024
1bd3860
Fix pkg config path again
mliezun Dec 24, 2024
65849be
Fix pkg config
mliezun Dec 24, 2024
eb395c1
fix build again
mliezun Dec 24, 2024
5e54d4c
ubuntu 20.04
mliezun Dec 24, 2024
7b77831
Upload from wheelhouse
mliezun Dec 24, 2024
9127d5a
Update python build ci
mliezun Dec 24, 2024
a859686
add newline
mliezun Dec 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/python-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Python Build Package
on:
push:
tags: [ 'v*.*.*' ]

permissions:
contents: write

jobs:
build:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
env:
GOEXPERIMENT: cgocheck2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: false
- name: Install Xcaddy
run: go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
- name: Set up Python ${{ matrix.python-version }}
run: |
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update -yyqq
sudo apt-get install -yyqq software-properties-common
sudo add-apt-repository -y ppa:deadsnakes/ppa
sudo apt-get install -yyqq python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv
sudo mv /usr/lib/x86_64-linux-gnu/pkgconfig/python-${{ matrix.python-version }}-embed.pc /usr/lib/x86_64-linux-gnu/pkgconfig/python3-embed.pc
- name: Run module tests
run: go test -race -v ./...
- name: Build the CLI
run: |
cd cmd/cli
CGO_ENABLED=1 go build -o caddysnake-cli main.go
touch dummy.c
python -m pip install cibuildwheel
export CIBW_BUILD=cp$(echo ${{ matrix.python-version }} | tr -d .)-manylinux_x86_64
echo $CIBW_BUILD
CIBW_BUILD_VERBOSITY=1 python -m cibuildwheel
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: cmd/cli/wheelhouse
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ __pycache__

# Local python builds
Python-3.12*
cmd/cli/*.egg-info
cmd/cli/dist
cmd/cli/caddysnake-cli
cmd/cli/dummy.*


# macOS
Expand Down
2 changes: 2 additions & 0 deletions caddysnake.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ func (f *CaddySnake) Provision(ctx caddy.Context) error {
if err != nil {
return err
}
} else {
return errors.New("asgi or wsgi app needs to be specified")
}
return nil
}
Expand Down
12 changes: 12 additions & 0 deletions cmd/cli/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import os
import sys


def main():
binary_path = os.path.join(os.path.dirname(__file__), "caddysnake-cli")

if not os.path.exists(binary_path):
print(f"caddysnake-cli binary file not found at {binary_path}")
sys.exit(1)

os.execv(binary_path, [binary_path] + sys.argv[1:])
206 changes: 206 additions & 0 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package main

import (
"encoding/json"
"errors"
"log"
"os"
"strconv"
"time"

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig"
caddycmd "github.com/caddyserver/caddy/v2/cmd"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/certmagic"
caddysnake "github.com/mliezun/caddy-snake"
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"

// plug in Caddy modules here

"github.com/caddyserver/caddy/v2/modules/caddyhttp/encode"
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode/gzip"
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/encode/zstd"
)

func main() {
caddycmd.RegisterCommand(caddycmd.Command{
Name: "wsgi-server",
Usage: "[--domain <example.com>] [--app <module>] [--listen <addr>] [--debug] [--access-logs]",
Short: "Spins up a Python wsgi server",
Long: `
A Python WSGI server designed for synchronous apps and frameworks (no async/await).

You can specify a custom socket address using the '--listen' option.

Providing a domain name with the '--domain' flag enables HTTPS and sets the listener to the appropriate secure port.
Ensure DNS A/AAAA records are correctly set up if using a public domain for secure connections.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("domain", "d", "", "Domain name at which to serve the files")
cmd.Flags().StringP("app", "a", "", "App module to be imported")
cmd.Flags().StringP("listen", "l", "", "The address to which to bind the listener")
cmd.Flags().Bool("debug", false, "Enable debug logs")
cmd.Flags().Bool("access-logs", false, "Enable access logs")
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdWsgiServer)
},
})

caddycmd.RegisterCommand(caddycmd.Command{
Name: "asgi-server",
Usage: "[--domain <example.com>] [--app <module>] [--listen <addr>] [--debug] [--access-logs]",
Short: "Spins up a Python asgi server",
Long: `
A Python ASGI server designed for asynchronous apps and frameworks (use of async/await).

You can specify a custom socket address using the '--listen' option.

Providing a domain name with the '--domain' flag enables HTTPS and sets the listener to the appropriate secure port.
Ensure DNS A/AAAA records are correctly set up if using a public domain for secure connections.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("domain", "d", "", "Domain name at which to serve the files")
cmd.Flags().StringP("app", "a", "", "App module to be imported")
cmd.Flags().StringP("listen", "l", "", "The address to which to bind the listener")
cmd.Flags().Bool("debug", false, "Enable debug logs")
cmd.Flags().Bool("access-logs", false, "Enable access logs")
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdAsgiServer)
},
})
caddycmd.Main()
}

func cmdWsgiServer(fs caddycmd.Flags) (int, error) {
return pythonServer("wsgi", fs)
}

func cmdAsgiServer(fs caddycmd.Flags) (int, error) {
return pythonServer("asgi", fs)
}

// pythonServer is inspired on the php-server command of the Frankenphp project (MIT License)
func pythonServer(server_type string, fs caddycmd.Flags) (int, error) {
caddy.TrapSignals()

domain := fs.String("domain")
app := fs.String("app")
listen := fs.String("listen")
debug := fs.Bool("debug")
accessLogs := fs.Bool("access-logs")

if app == "" {
return caddy.ExitCodeFailedStartup, errors.New("--app is required")
}

gzip, err := caddy.GetModule("http.encoders.gzip")
if err != nil {
return caddy.ExitCodeFailedStartup, err
}

zstd, err := caddy.GetModule("http.encoders.zstd")
if err != nil {
return caddy.ExitCodeFailedStartup, err
}

encodings := caddy.ModuleMap{
"zstd": caddyconfig.JSON(zstd.New(), nil),
"gzip": caddyconfig.JSON(gzip.New(), nil),
}
prefer := []string{"zstd", "gzip"}

pythonHandler := caddysnake.CaddySnake{}
if server_type == "wsgi" {
pythonHandler.ModuleWsgi = app
} else {
pythonHandler.ModuleAsgi = app
}
if venv := os.Getenv("VIRTUAL_ENV"); venv != "" {
pythonHandler.VenvPath = venv
}

mainRoute := caddyhttp.Route{
MatcherSetsRaw: []caddy.ModuleMap{
{
"path": caddyconfig.JSON(caddyhttp.MatchPath{"/*"}, nil),
},
},
HandlersRaw: []json.RawMessage{
caddyconfig.JSONModuleObject(encode.Encode{
EncodingsRaw: encodings,
Prefer: prefer,
}, "handler", "encode", nil),
caddyconfig.JSONModuleObject(pythonHandler, "handler", "python", nil),
},
}

subroute := caddyhttp.Subroute{
Routes: caddyhttp.RouteList{mainRoute},
}

route := caddyhttp.Route{
HandlersRaw: []json.RawMessage{caddyconfig.JSONModuleObject(subroute, "handler", "subroute", nil)},
}

if domain != "" {
route.MatcherSetsRaw = []caddy.ModuleMap{
{
"host": caddyconfig.JSON(caddyhttp.MatchHost{domain}, nil),
},
}
}

server := &caddyhttp.Server{
ReadHeaderTimeout: caddy.Duration(10 * time.Second),
IdleTimeout: caddy.Duration(30 * time.Second),
MaxHeaderBytes: 1024 * 10,
Routes: caddyhttp.RouteList{route},
}
if listen == "" {
if domain == "" {
listen = ":9080"
} else {
listen = ":" + strconv.Itoa(certmagic.HTTPSPort)
}
}
server.Listen = []string{listen}

if accessLogs {
server.Logs = &caddyhttp.ServerLogConfig{}
}

httpApp := caddyhttp.App{
Servers: map[string]*caddyhttp.Server{"srv0": server},
}

var f bool
cfg := &caddy.Config{
Admin: &caddy.AdminConfig{
Disabled: false,
Config: &caddy.ConfigSettings{
Persist: &f,
},
},
AppsRaw: caddy.ModuleMap{
"http": caddyconfig.JSON(httpApp, nil),
},
}

if debug {
cfg.Logging = &caddy.Logging{
Logs: map[string]*caddy.CustomLog{
"default": {
BaseLog: caddy.BaseLog{Level: zapcore.DebugLevel.CapitalString()},
},
},
}
}

if err := caddy.Run(cfg); err != nil {
return caddy.ExitCodeFailedStartup, err
}

log.Printf("Serving Python app on %s", listen)

select {}
}
49 changes: 49 additions & 0 deletions cmd/cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "caddysnake"
version = "0.1.4"
description = "Python WSGI/ASGI server powered by Caddy"
authors = [
{name = "Miguel Liezun", email = "liezun.js@gmail.com"},
]
readme = "README.md"
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Go",
]

[project.urls]
Homepage = "https://github.com/mliezun/caddy-snake"
Repository = "https://github.com/mliezun/caddy-snake.git"
# Documentation = "https://caddy-snake.readthedocs.io/"

[tool.setuptools]
packages = ["caddysnake"]
include-package-data = true
ext-modules = [
{name = "caddysnake.dummy", sources = ["dummy.c"]}
]

[tool.setuptools.package-dir]
caddysnake = "."

[tool.setuptools.package-data]
caddysnake = ["caddysnake-cli"]

[project.scripts]
caddysnake = "caddysnake.cli:main"

[tool.cibuildwheel]
before-all = "uname -a"
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ go 1.21.6

require (
github.com/caddyserver/caddy/v2 v2.7.6
github.com/caddyserver/certmagic v0.20.0
github.com/gorilla/websocket v1.4.1
github.com/spf13/cobra v1.7.0
go.uber.org/zap v1.26.0
)

Expand All @@ -18,7 +20,6 @@ require (
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/caddyserver/certmagic v0.20.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chzyer/readline v1.5.1 // indirect
Expand Down Expand Up @@ -83,7 +84,6 @@ require (
github.com/smallstep/nosql v0.6.0 // indirect
github.com/smallstep/truststore v0.12.1 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 // indirect
Expand Down