PEX (Python EXecutable)

repository·main·Indexed 26 days ago

https://github.com/pex-tool/pex

A library for generating self-contained, executable Python environments. PEX packages applications and their dependencies into a single .pex file for easy deployment across platforms. It supports specifying requirements via pip or setuptools, defining entry points through scripts or modules, and building for specific target platforms. Advanced features include the `scie` flag for embedding Python interpreters, `bdist_pex` for setuptools integration, and tools for deploying PEX applications in containers using venv.

Tokens
25.4K
Snippets
24
Records
179
Agent score
86%

What's inside PEX

  1. Overview of PEX and .pex files

    main
    PEX (Python Executable) is a tool and a file format designed for general-purpose Python environment virtualization. It allows you to package Python applications and their dependencies into a single .pex file, providing a deployment solution similar to virtualenv.
  2. Understand PEX files

    main

    PEX files are self-contained, executable Python virtual environments. They are constructed as zip files containing a #!/usr/bin/env python shebang and a special __main__.py file. This structure allows them to be treated as executable Python programs via the Python importer, following the principles outlined in PEP 441.

    Key benefits include:

    • Simplified Deployment: Deployment can be as simple as using scp to move a single file.
    • Portability: A single PEX file can support multiple platforms and Python interpreters, allowing the same file to run on different operating systems (e.g., macOS and Linux) or production environments.
  3. Install PEX

    main

    You can install the pex tool using pip:

    $ pip install pex

    Alternatively, if you want to build the binary from a git clone using uv to keep your Python environment clean:

    $ uv run dev-cmd package
    $ cp dist/pex ~/bin
    $ pip install pex
  4. Configure custom Python distribution URLs for lazy scies

    main

    In restricted network environments, you can redirect where a lazy PEX fetches its Python distribution by using the PEX_BOOTSTRAP_URLS environment variable. This variable should point to a JSON file mapping the expected distribution filename to a custom URL (including file:// paths for local files).

    JSON Format:

    {
      "ptex": {
        "<distribution-filename>": "<internal-or-local-URL>"
      }
    }

    Workflow to generate a configuration file:

    1. Inspect an existing scie to get the correct keys: SCIE=inspect <your_pex_scie> | jq '{ptex:.ptex}'.
    2. Edit the resulting JSON to point to your internal or local URLs.
    3. Run the PEX with the environment variable: PEX_BOOTSTRAP_URLS=your_config.json ./your_pex.
  5. Install and bootstrap the pex utility

    main
    To use pex, first install it via pip. You can then use pex to bootstrap itself into a standalone executable in a directory like ~/bin. This allows you to use pex in or outside of any virtualenv, provided ~/bin is in your $PATH.
  6. Save a .pex file to disk

    main

    By default, PEX commands create ephemeral environments that are garbage collected after execution. To save a standalone executable PEX file to disk, use the -o PATH or --output PATH option.

    Example: Packaging Ansible as a standalone executable.

    Once saved, the .pex file is an executable environment that can be run directly.

    $ pex ansible -c ansible -o ansible.pex
    $ ./ansible.pex --help
  7. Create a lazy PEX scie

    main

    If you want to reduce the size of your PEX file and your deployment machines have internet access, use --scie lazy. Instead of embedding the full Python distribution, the PEX will fetch the required distribution from the internet only when needed. If the same Python distribution has been used by another PEX on that machine previously, the fetch is skipped and the local version is used.

    pex cowsay -c cowsay --inject-args=-t --scie lazy -o cowsay
  8. Deploy a PEX application in a container using `venv`

    main

    For the smallest footprint and lowest latency in containers, use the PEX venv tool.

    1. Build the PEX with --include-tools (or --venv).
    2. Use a multi-stage Docker build to install the PEX as a traditional venv. This pre-compiles Python code and ensures only the final venv remains in the image.

    Note: The container must contain the Python interpreter used to build the PEX.

    FROM python:3.10-slim as deps
    COPY /my-app.pex /
    RUN PEX_TOOLS=1 /usr/local/bin/python3.10 /my-app.pex venv --scope=deps --compile /my-app
    
    FROM python:3.10-slim as srcs
    COPY /my-app.pex /
    RUN PEX_TOOLS=1 /usr/local/bin/python3.10 /my-app.pex venv --scope=srcs --compile /my-app
    
    FROM python:3.10-slim
    COPY --from=deps /my-app /my-app
    COPY --from=srcs /my-app /my-app
    ENTRYPOINT ["/my-app/pex"]
  9. Specify a custom Python interpreter shebang

    main

    You can control the Python interpreter used by the PEX file in two ways:

    1. Using --python: Sets the default interpreter (e.g., PyPy) in the PEX hashbang.
    2. Using --python-shebang: Specifies an explicit, absolute path for the Python shebang line (useful for non-standard locations or interpreters not on $PATH).

    Note: The interpreter can also be manipulated at runtime using the PEX_PYTHON environment variable.

  10. Build PEX using setuptools (bdist_pex)

    main

    PEX provides a bdist_pex command for setuptools to build executables for Python projects following standard naming conventions.

    • Default behavior: Builds an executable using the console script with the same name as the package (e.g., pip-7.2.0.pex). If no console scripts exist, it produces an environment PEX that drops into an interpreter.
    • Build all scripts: Use --bdist-all to build all defined entry_points instead of just the namesake script. This om does not append version numbers or the .pex suffix.
    • Specify output directory: Use --bdist-dir to control where the files are written.
  11. Create a BusyBox scie with multiple commands

    main

    By default, a PEX scie executes the configured entry point. However, you can use the --scie-busybox flag to include a specific set of commands (modules, functions, or console scripts) within the scie. This allows the scie to act as a multi-tool container.

    To use a command from a BusyBox scie, you can either:

    1. Pass the command as the first argument to the scie file.
    2. Set the SCIE_BOOT environment variable.

    If no command is provided, the scie will list available commands and prompt for selection.

  12. Package a WSGI application with Gunicorn

    main

    To run a WSGI app (like Flask or Django) as a PEX, you must bundle Gunicorn as a dependency and set it as the entry point. Since Gunicorn cannot enter the PEX to find the app, the PEX must invoke Gunicorn and pass the app instance name as a runtime argument.

    1. Build the PEX with Gunicorn and your framework (e.g., Flask): pex flask gunicorn myapp -c gunicorn -o ~/service.pex

    2. Run the PEX by passing the app instance and optional config: ./service.pex myapp:appinstance -c /path/to/gunicorn_config.py

    $ pex flask gunicorn myapp -c gunicorn -o ~/service.pex
    
    $ service.pex myapp:appinstance -c /path/to/gunicorn_config.py