Python Applications¶
Complete guide to packaging Python applications with FlavorPack, including dependencies, virtual environments, and Python-specific optimizations.
๐ค AI-Generated Content
This documentation was generated with AI assistance and is still being audited. Some, or potentially a lot, of this information may be inaccurate. Learn more.
Prerequisites
Before packaging Python apps, ensure you have:
- FlavorPack installed from source
- Helpers built (
make build-helpers) - A Python project with valid
pyproject.toml
See System Requirements for detailed version information.
Feature Coverage
This guide covers current functionality and items under evaluation.
โ What Works Today:
- Basic dependency packaging from
pyproject.toml - Standard entry points and scripts (
[project.scripts]) - Automatic dependency resolution via UV
- Simple package structure
๐ Exploratory Items:
- Python version selection
- Build environment customization
- Runtime optimizations
- Platform-specific builds
- Advanced dependency configuration
Features marked with ๐ are under evaluation.
Overview¶
FlavorPack provides first-class support for Python applications. This guide covers what works today and what's under evaluation.
What Works Today¶
Basic Python Packaging โ ¶
FlavorPack can package any Python application with a valid pyproject.toml:
[project]
name = "myapp"
version = "1.0.0"
dependencies = [
"requests>=2.28.0",
"click>=8.0",
"pydantic>=2.0"
]
[project.scripts]
myapp = "myapp.cli:main"
[tool.flavor]
entry_point = "myapp.cli:main"
This configuration will:
- โ
Install all dependencies from
[project.dependencies] - โ
Create the entry point specified in
[tool.flavor].entry_point - โ
Extract CLI scripts from
[project.scripts] - โ
Bundle everything into a self-contained
.psppackage
Supported Python Versions โ ¶
FlavorPack itself requires Python 3.11 or higher to run the packaging tools.
Build Environment Python:
Packaged applications currently use whatever Python version is available in your build environment. This Python runtime gets embedded into the package.
| Your Build Environment | Packaged Python Version |
|---|---|
| Python 3.12 | โ Package includes Python 3.12 |
| Python 3.11 | โ Package includes Python 3.11 |
| Python 3.10 or older | โ FlavorPack won't run |
Current Limitation
Python version selection is under evaluation. You cannot specify a different Python version than what's in your build environment.
For example, if you build on Python 3.12, your package will use Python 3.12 - you cannot target Python 3.11.
Exploratory: Support for specifying target Python versions via manifest configuration is under evaluation.
Dependency Management โ ¶
FlavorPack automatically handles dependencies defined in pyproject.toml:
[project]
dependencies = [
"requests>=2.28.0", # Version constraints work
"click>=8.0,<9.0", # Range constraints work
"pydantic==2.1.0", # Exact versions work
]
Platform-Specific Dependencies โ :
[project]
dependencies = [
"pywin32>=300; sys_platform == 'win32'",
"pyobjc>=9.0; sys_platform == 'darwin'",
]
Entry Points โ ¶
FlavorPack supports standard Python entry points:
[project.scripts]
myapp = "myapp.cli:main"
admin = "myapp.admin:cli"
[tool.flavor]
entry_point = "myapp.cli:main" # Main entry point for the package
The [tool.flavor].entry_point is required and specifies which function runs when you execute the .psp file.
Exploratory Python Features¶
The following features are under evaluation.
Python Version Selection ๐¶
Exploratory Feature
Automatic Python version selection is under evaluation. Availability may change or be removed. See the notes below for details.
Current Workaround: Packages use the Python version from your build environment. If you build on Python 3.12, your package will use Python 3.12.
Dependency Management¶
Basic Dependencies¶
Optional Dependencies¶
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=22.0",
"mypy>=1.0"
]
docs = [
"mkdocs>=1.4",
"mkdocs-material>=9.0"
]
api = [
"fastapi>=0.100.0",
"uvicorn>=0.23.0"
]
FlavorPack automatically includes all dependencies from your pyproject.toml file when building packages.
Platform-Specific Dependencies¶
[project]
dependencies = [
"pywin32>=300; sys_platform == 'win32'",
"pyobjc>=9.0; sys_platform == 'darwin'",
"python-xlib>=0.30; sys_platform == 'linux'"
]
Local and Git Dependencies¶
[project]
dependencies = [
# From Git repository
"mypackage @ git+https://github.com/user/[email protected]",
"private @ git+ssh://[email protected]/company/private.git",
# From local path
"locallib @ file:///absolute/path/to/package",
"relativelib @ file://./libs/mylib",
# From URL
"archive @ https://example.com/package-1.0.tar.gz"
]
Virtual Environment Configuration¶
Build Environment¶
Exploratory Feature
FlavorPack creates a basic isolated virtual environment during build. Advanced configuration options (custom venv path, build-time environment variables, pre-install commands) are under evaluation. Availability may change or be removed.
See the notes below for details.
Current Behavior: FlavorPack automatically creates a virtual environment and installs dependencies using UV.
Current Workaround: Use standard Python packaging tooling (uv, setuptools) in your project's development environment before packaging.
Entry Points¶
Script Entry Points¶
[project.scripts]
# Simple entry point
myapp = "myapp.cli:main"
# Multiple entry points
myapp-server = "myapp.server:run"
myapp-worker = "myapp.worker:start"
myapp-admin = "myapp.admin:cli"
Console Scripts¶
[project.scripts]
# CLI tool with click
mycli = "myapp.cli:cli"
[tool.flavor]
# Primary entry point for package
entry_point = "myapp.cli:cli"
GUI Entry Points¶
[project.gui-scripts]
# GUI applications (no console window on Windows)
myapp-gui = "myapp.gui:main"
Module Structure¶
Recommended Project Structure¶
myproject/
โโโ pyproject.toml
โโโ README.md
โโโ LICENSE
โโโ src/
โ โโโ myapp/
โ โโโ __init__.py
โ โโโ __main__.py # For python -m myapp
โ โโโ cli.py # CLI entry point
โ โโโ core/ # Core functionality
โ โ โโโ __init__.py
โ โ โโโ logic.py
โ โโโ utils/ # Utilities
โ โ โโโ __init__.py
โ โ โโโ helpers.py
โ โโโ data/ # Package data
โ โโโ config.yaml
โโโ tests/
โ โโโ __init__.py
โ โโโ test_core.py
โโโ docs/
โโโ index.md
Package Discovery¶
[tool.setuptools.packages.find]
where = ["src"]
include = ["myapp*"]
exclude = ["tests*", "docs*"]
[tool.setuptools.package-data]
myapp = ["data/*.yaml", "data/*.json"]
Handling Package Data¶
Including Data Files¶
Use standard Python packaging configuration (for example, tool.setuptools.package-data) to ensure data files are included in your package.
Accessing Data at Runtime¶
import importlib.resources as resources
from pathlib import Path
def load_config():
"""Load configuration from package data."""
# Python 3.9+
with resources.files("myapp.data").joinpath("config.yaml").open() as f:
return yaml.safe_load(f)
def get_data_path():
"""Get path to data directory."""
# For extracted packages
if hasattr(sys, '_MEIPASS'):
# PyInstaller compatibility
return Path(sys._MEIPASS) / "data"
elif os.environ.get('FLAVOR_WORKENV'):
# FlavorPack work environment
return Path(os.environ['FLAVOR_WORKENV']) / "data"
else:
# Development
return Path(__file__).parent / "data"
C Extensions and Binary Dependencies¶
Building with C Extensions¶
[tool.flavor.build]
# Ensure build tools are available
build_requires = [
"setuptools>=65.0",
"wheel",
"cython>=0.29"
]
Platform-specific build flags are handled by your build environment and compiler toolchain; FlavorPack does not define manifest settings for them yet.
Common Binary Packages¶
[project]
dependencies = [
# Scientific computing
"numpy>=1.24.0",
"scipy>=1.10.0",
"pandas>=2.0.0",
# Machine learning
"scikit-learn>=1.3.0",
"tensorflow>=2.13.0",
"torch>=2.0.0",
# Database drivers
"psycopg2-binary>=2.9.0",
"mysqlclient>=2.2.0",
"cx-Oracle>=8.3.0"
]
Optimization Techniques¶
Current Behavior: FlavorPack packages all dependencies and Python code as-is.
Practical tips:
- Pre-compile bytecode in your project before packaging
- Minimize dependencies in your pyproject.toml
- Remove large, unused assets before packaging
Environment Variables¶
Runtime Environment¶
[tool.flavor.execution.runtime.env]
# Clear all host environment variables, then selectively pass through
unset = ["*"]
# Pass through essential host variables
pass = ["HOME", "USER", "TERM", "PATH"]
# Set application-specific environment variables
set = {
PYTHONPATH = "$FLAVOR_WORKENV/lib",
MY_APP_CONFIG = "$FLAVOR_WORKENV/config",
DEBUG = "0"
}
Configuration via Environment¶
import os
from pathlib import Path
class Config:
"""Application configuration from environment."""
# FlavorPack provides these
WORKENV = Path(os.environ.get('FLAVOR_WORKENV', '.'))
PACKAGE_VERSION = os.environ.get('FLAVOR_PACKAGE_VERSION', 'dev')
PACKAGE_NAME = os.environ.get('FLAVOR_PACKAGE_NAME', 'unknown')
# Custom configuration
DEBUG = os.environ.get('DEBUG', '0') == '1'
CONFIG_PATH = Path(os.environ.get('CONFIG_PATH', WORKENV / 'config'))
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
Logging Configuration¶
Setup Logging¶
import logging
import sys
from pathlib import Path
def setup_logging():
"""Configure logging for packaged application."""
log_dir = Path(os.environ.get('FLAVOR_WORKENV', '.')) / 'logs'
log_dir.mkdir(exist_ok=True)
logging.basicConfig(
level=os.environ.get('LOG_LEVEL', 'INFO'),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler(log_dir / 'app.log')
]
)
Structured Logging¶
import structlog
logger = structlog.get_logger()
# Use structured logging
logger.info("application_started",
version=os.environ.get('FLAVOR_PACKAGE_VERSION'),
workenv=os.environ.get('FLAVOR_WORKENV'))
Async Applications¶
AsyncIO Support¶
import asyncio
import signal
async def main():
"""Async main entry point."""
# Your async code here
await asyncio.sleep(1)
print("Async application running")
def run():
"""Entry point for packaged app."""
# Handle signals properly
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
for sig in (signal.SIGTERM, signal.SIGINT):
signal.signal(sig, lambda s, f: loop.stop())
try:
loop.run_until_complete(main())
finally:
loop.close()
Web Applications¶
[project]
dependencies = [
"fastapi>=0.100.0",
"uvicorn>=0.23.0",
"httpx>=0.24.0"
]
[tool.flavor]
entry_point = "myapp.server:run"
Common Patterns¶
CLI Applications¶
# myapp/cli.py
import click
import sys
@click.command()
@click.option('--config', help='Configuration file')
@click.option('--verbose', is_flag=True, help='Verbose output')
def main(config, verbose):
"""Main CLI entry point."""
if verbose:
logging.basicConfig(level=logging.DEBUG)
# Your CLI logic here
click.echo(f"Running with config: {config}")
if __name__ == "__main__":
sys.exit(main())
Service Applications¶
# myapp/service.py
import time
import signal
import sys
class Service:
def __init__(self):
self.running = True
signal.signal(signal.SIGTERM, self.stop)
signal.signal(signal.SIGINT, self.stop)
def stop(self, signum, frame):
"""Handle shutdown signal."""
self.running = False
def run(self):
"""Run service loop."""
while self.running:
# Service logic here
time.sleep(1)
print("Service stopped")
def main():
"""Service entry point."""
service = Service()
service.run()
return 0
Plugin Systems¶
# myapp/plugins.py
import importlib
import pkgutil
from pathlib import Path
def load_plugins():
"""Load plugins from package."""
plugins = []
# Load from packaged plugins
plugin_dir = Path(os.environ.get('FLAVOR_WORKENV', '.')) / 'plugins'
if plugin_dir.exists():
for finder, name, ispkg in pkgutil.iter_modules([str(plugin_dir)]):
module = importlib.import_module(f"plugins.{name}")
if hasattr(module, 'Plugin'):
plugins.append(module.Plugin())
return plugins
Troubleshooting Python Packages¶
Import Errors¶
# Debug import issues
import sys
print("Python path:", sys.path)
print("Executable:", sys.executable)
print("Version:", sys.version)
print("Work environment:", os.environ.get('FLAVOR_WORKENV'))
Dependency Conflicts¶
# Check installed packages
flavor inspect package.psp --show-deps
# Verify compatibility
uv sync --frozen
# Force reinstall
flavor pack --manifest pyproject.toml --force-reinstall
Performance Issues¶
# Profile startup time
import time
import atexit
start_time = time.time()
def show_runtime():
print(f"Runtime: {time.time() - start_time:.2f} seconds")
atexit.register(show_runtime)
Best Practices¶
1. Version Management¶
[project]
# Use semantic versioning
version = "1.2.3"
# Or dynamic version from file
dynamic = ["version"]
[tool.setuptools.dynamic]
version = {file = "VERSION"}
2. Dependency Pinning¶
# Development: flexible versions
[project]
dependencies = [
"requests>=2.28,<3.0",
"click>=8.0"
]
# Production: pin exact versions
[tool.flavor.build]
requirements_file = "requirements.lock"
3. Security¶
# Don't hardcode secrets
API_KEY = os.environ.get('API_KEY')
if not API_KEY:
raise ValueError("API_KEY environment variable required")
# Use secure defaults
DEBUG = os.environ.get('DEBUG', 'false').lower() == 'true'
4. Error Handling¶
def main():
"""Robust entry point."""
try:
# Application logic
return run_app()
except KeyboardInterrupt:
print("\nInterrupted by user")
return 130
except Exception as e:
logging.exception("Unhandled error")
if os.environ.get('DEBUG'):
raise
return 1
Examples¶
Minimal Package¶
Data Science Package¶
[project]
name = "ml-model"
version = "1.0.0"
dependencies = [
"numpy>=1.24.0",
"pandas>=2.0.0",
"scikit-learn>=1.3.0",
"joblib>=1.3.0"
]
[tool.flavor]
entry_point = "ml_model.predict:main"
Web API Package¶
[project]
name = "api-server"
version = "1.0.0"
dependencies = [
"fastapi>=0.100.0",
"uvicorn[standard]>=0.23.0",
"pydantic>=2.0.0",
"sqlalchemy>=2.0.0"
]
[tool.flavor]
entry_point = "api.main:run"
Related Pages¶
Configuration:
- ๐ Package Configuration - Full configuration reference
- ๐ Manifest Reference - pyproject.toml specification
- ๐ Package Signing - Add cryptographic signatures
- ๐ Platform Support - Multi-platform packaging
Workflow:
- ๐๏ธ Building Packages - General packaging guide
- ๐ฆ CLI Reference -
flavor packcommand details - โ Verification - Verify package integrity
Examples:
- ๐ป CLI Tool Example - Package a CLI application
- ๐ Web App Example - Package a Flask/FastAPI app
Help:
- ๐ Troubleshooting - Common issues and solutions
- ๐ FAQ - Frequently asked questions