Python bindings for the eggress proxy — pproxy-compatible URI translation, configuration, and service management
Project description
eggress Python bindings
Python bindings for the eggress proxy framework, powered by PyO3 and the Rust embed API.
Installation (local development)
pip install maturin
cd crates/eggress-python
maturin build --target x86_64-apple-darwin # adjust target for your platform
pip install --force-reinstall target/wheels/eggress-*.whl
Note:
maturin developinstalls the native extension to the wrong module path (_eggress/_eggress.soinstead ofeggress/_eggress.so). Usematurin build+pip installinstead.
Quick start
from eggress import EggressService
with EggressService.from_toml("""
version = 1
[[listeners]]
name = "socks"
bind = "127.0.0.1:0"
protocols = ["socks5"]
""").start() as handle:
addr = handle.bound_addresses["socks"]
print(f"SOCKS5 listening on {addr}")
print(handle.metrics_text())
Async usage
import asyncio
from eggress import EggressService
async def main():
async with await EggressService.from_toml(TOML).astart() as handle:
print("Listening on", await handle.bound_addresses)
asyncio.run(main())
API
EggressConfig.from_toml(toml)/EggressConfig.from_file(path)— parse configEggressService(config)/EggressService.from_toml(toml)— create serviceservice.start()— start proxy, returnsEggressHandlehandle.bound_addresses— dict of listener name -> addresshandle.status()— generation, readiness, uptime, connectionshandle.metrics_text()— Prometheus metricshandle.reload_toml(toml)— hot-reload confighandle.shutdown()— graceful shutdown (idempotent; safe to call twice)- Context manager support:
with service.start() as handle: ...
Always use explicit lifecycle management. Prefer context managers or
explicit handle.shutdown() in a finally block. Do not rely on Python
garbage collection to shut down the service — object destruction is a
best-effort fallback, not the lifecycle API.
pproxy Compatibility
Eggress provides a pproxy compatibility subset validated against pproxy==2.7.9
(111 capabilities classified as drop_in per the strict compatibility manifest):
- URI Translation:
translate_pproxy_args()converts pproxy CLI arguments to eggress TOML - Same Protocols: HTTP, SOCKS4/4a, SOCKS5, Shadowsocks (AEAD), Trojan
- Same Schedulers: Round-robin, least-connections, first-available
- Enhanced Features: Hot-reload, structured errors, context managers
See docs/python/PPROXY_EMBEDDED_USAGE_PATTERNS.md for migration guidance.
pproxy drop-in API
from eggress import PPProxyService, start_pproxy, check_pproxy_args
# Check compatibility before starting
report = check_pproxy_args(["-l", "socks5://:1080", "-r", "http://proxy:8080"])
print(f"Tier: {report.tier}, OK: {report.ok}")
# Start from pproxy args
with start_pproxy(["-l", "socks5://127.0.0.1:0"]) as handle:
print(handle.bound_addresses)
# Start from local URI
with PPProxyService.from_uri("socks5://127.0.0.1:0") as handle:
print(handle.bound_addresses)
# Start from TOML
with PPProxyService.from_toml(toml_str) as handle:
print(handle.bound_addresses)
API Contract (Phase C1)
A machine-readable contract of the pproxy 2.7.9 public API is maintained at
python/compat/pproxy_api_contract.json. It inventories 105 symbols across
4 modules with full signatures, class hierarchies, and async classifications.
Classification summary
| Tier | Count | Examples |
|---|---|---|
| adapted_target | 3 | Connection, Server, Rule → PPProxyService |
| intentional_non_parity | 1 | DIRECT sentinel |
| internal_observed | 87 | Protocol classes, cipher classes, server internals |
Validation
# Regenerate the contract
python3.11 python/compat/extract_api.py
# Run 56 contract validation tests
python3.11 -m pytest tests/compat/test_pproxy_api_contract.py -v
# Run 46 behavioral probes
python3.11 python/compat/behavioral_probes.py
Namespace strategy
Eggress does not install a top-level pproxy module. The pproxy
compatibility layer is provided through eggress.pproxy and eggress.start_pproxy().
See docs/python/PPROXY_NAMESPACE_STRATEGY.md for the full decision record.
Migrating from pproxy
from eggress import start_pproxy
# Same arguments you'd pass to pproxy
with start_pproxy(["-l", "socks5://:1080", "-r", "http://proxy:8080"]) as handle:
print(handle.bound_addresses)
Or inspect the translation first:
from eggress import translate_pproxy_args
result = translate_pproxy_args(["-l", "socks5://:1080", "-r", "http://proxy:8080"])
print(result.toml) # generated eggress TOML
print(result.warnings) # partial-behavior notes
print(result.unsupported) # unsupported features
Error model
| Exception | Meaning |
|---|---|
EggressError |
Base exception |
ConfigError |
TOML parsing or validation error |
StartupError |
Listener bind or readiness timeout |
ReloadError |
Config reload failure |
ShutdownError |
Runtime shutdown error |
UnsupportedFeatureError |
Feature not supported |
InternalError |
Unexpected internal error |
Limitations
- GIL is released for blocking Rust calls
- Requires Python >= 3.9
- Listener bind changes require restart (not reloadable)
- No logging initialization unless configured in TOML
Non-parity with pproxy
- Shadowsocks TCP uses standard SIP003 AEAD framing (wire-compatible with
shadowsocks-rust/ssserver/sslocal); single-hop upstream only - No inbound Shadowsocks or Trojan listeners (upstream-only) — inbound Shadowsocks listener is available in the Rust binary; Python bindings expose the embed API which omits this for now
- No legacy stream ciphers (aes-ctr, aes-cfb, rc4-md5, etc.)
- No SSH, Unix socket, or transparent proxy (redir) transport
- No pproxy daemon mode (
--daemon) - No
-ul/-urstandalone UDP relay (uses SOCKS5 UDP ASSOCIATE) - Multiple remotes default to round-robin (matches pproxy behavior)
- Direct fallback requires explicit config
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file eggress-1.0.1-cp312-cp312-manylinux_2_34_x86_64.whl.
File metadata
- Download URL: eggress-1.0.1-cp312-cp312-manylinux_2_34_x86_64.whl
- Upload date:
- Size: 5.5 MB
- Tags: CPython 3.12, manylinux: glibc 2.34+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
842f78580557c30e08cbc4f44555bb48b0feef0db44b4d929201f91d59df930c
|
|
| MD5 |
733b229baf693bb955b3a9bf765d06b2
|
|
| BLAKE2b-256 |
98b32d8ffbeac46f01bd841c48c4dfc129f41ab5054bdea7111e5ffc479cbced
|
Provenance
The following attestation bundles were made for eggress-1.0.1-cp312-cp312-manylinux_2_34_x86_64.whl:
Publisher:
publish-python.yml on eggstack/eggress
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
eggress-1.0.1-cp312-cp312-manylinux_2_34_x86_64.whl -
Subject digest:
842f78580557c30e08cbc4f44555bb48b0feef0db44b4d929201f91d59df930c - Sigstore transparency entry: 2283767822
- Sigstore integration time:
-
Permalink:
eggstack/eggress@a4d5f373609c89887192f477aab02250e334ba09 -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/eggstack
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@a4d5f373609c89887192f477aab02250e334ba09 -
Trigger Event:
push
-
Statement type: