Skip to content

angreal.integrations.flox

Cross-language development environment and services management powered by Flox.

Overview

Angreal's Flox integration provides environment activation and services management using Flox, a Nix-based development environment manager. It activates a Flox environment — applying its environment variables to the current process — and manages the declarative background services defined in the environment's manifest.toml.

Prerequisites

Flox CLI must be installed before using this integration.

Installation:

# macOS
curl -fsSL https://flox.dev/install | bash

# Linux
curl -fsSL https://flox.dev/install | bash

# Verify installation
flox --version

Creating a Flox Environment:

cd your-project
flox init
flox install python@3.11 nodejs@20

Functions

flox_required

Decorator that wraps a function to run in a Flox environment with optional services.

flox_required(path=None, services=None)

Parameters: - path (str | Path, optional): Path to Flox environment. Defaults to current directory. - services (List[str], optional): Services to start before execution.

Example:

from angreal.integrations.flox import flox_required
import angreal

@angreal.command(name="test")
@flox_required(".", services=["postgres"])
def run_tests():
    """Run tests with Flox environment and Postgres service."""
    import subprocess
    subprocess.run(["pytest", "-v"])

# Without services
@angreal.command(name="build")
@flox_required(".")
def build():
    """Build in Flox environment."""
    import subprocess
    subprocess.run(["npm", "run", "build"])

The decorator: 1. Activates the Flox environment 2. Starts specified services (if any) 3. Executes the wrapped function 4. Stops services and deactivates environment (even on exception)

Classes

Flox

Main class for managing Flox environments.

from angreal.integrations.flox import Flox

flox = Flox(path=".")

Constructor

Flox(path=None)

Parameters: - path (str | Path, optional): Path to Flox environment. Defaults to current directory.

Properties

exists

Check if the Flox environment exists (.flox/ directory present).

@property
def exists(self) -> bool

Returns: - bool: True if .flox/ directory exists

has_manifest

Check if the environment has a manifest file.

@property
def has_manifest(self) -> bool

Returns: - bool: True if .flox/env/manifest.toml exists

path

Get the environment path.

@property
def path(self) -> Path

Returns: - Path: Absolute path to the Flox environment

services

Get a FloxServices manager for this environment.

@property
def services(self) -> FloxServices

Returns: - FloxServices: Services manager instance

Instance Methods

activate

Activate the Flox environment in the current Python process.

def activate(self) -> None

Modifies os.environ to include Flox environment variables. Changes persist until deactivate() is called.

Raises: - RuntimeError: If the environment does not exist

Example:

flox = Flox(".")
flox.activate()
# Environment variables now include Flox paths
# FLOX_ENV, PATH, etc. are modified
flox.deactivate()

deactivate

Restore the original environment.

def deactivate(self) -> None

Restores all environment variables to their state before activation. Safe to call multiple times.

run

Execute a command within the Flox environment.

def run(command: str, args: List[str] = None) -> tuple[int, str, str]

Parameters: - command (str): Command to execute - args (List[str], optional): Command arguments

Returns: - tuple[int, str, str]: (exit_code, stdout, stderr)

Example:

flox = Flox(".")
exit_code, stdout, stderr = flox.run("python", ["--version"])
print(f"Python version: {stdout}")

Context Manager Support

Flox supports the context manager protocol for automatic activation/deactivation:

with Flox(".") as flox:
    # Environment is activated
    exit_code, stdout, _ = flox.run("node", ["--version"])
    print(f"Node version: {stdout}")
# Environment is automatically deactivated

Class Methods

is_available

Check if Flox CLI is installed.

@classmethod
def is_available() -> bool

Returns: - bool: True if flox command is available in PATH

Example:

if not Flox.is_available():
    print("Please install Flox: curl -fsSL https://flox.dev/install | bash")

version

Get Flox version string.

@classmethod
def version() -> str

Returns: - str: Flox version (e.g., "1.2.0")

Raises: - RuntimeError: If Flox is not installed

FloxServices

Manages Flox services for an environment.

from angreal.integrations.flox import FloxServices

services = FloxServices(".")
# Or via Flox instance
services = Flox(".").services

Methods

start

Start services and return a handle for later cleanup.

def start(*services: str) -> FloxServiceHandle

Parameters: - *services: Service names to start. If empty, starts all services.

Returns: - FloxServiceHandle: Handle for managing started services

Example:

services = FloxServices(".")
handle = services.start("postgres", "redis")
# Services are running...
handle.stop()

stop

Stop all services in the environment.

def stop() -> None
status

Get status of all services.

def status() -> List[ServiceInfo]

Returns: - List[ServiceInfo]: List of service status objects

Example:

for svc in services.status():
    print(f"{svc.name}: {svc.status} (PID: {svc.pid})")

logs

Get logs for a service.

def logs(service: str, follow: bool = False, tail: int = None) -> str

Parameters: - service (str): Service name - follow (bool): Follow log output (default: False) - tail (int, optional): Number of lines from end

Returns: - str: Log output

restart

Restart services.

def restart(*services: str) -> None

Parameters: - *services: Service names to restart. If empty, restarts all.

FloxServiceHandle

Handle for managing started services across sessions.

Properties

  • flox_env_path (Path): Path to the Flox environment
  • services (List[ServiceInfo]): List of service info objects
  • started_at (str): ISO timestamp when services were started

Methods

stop

Stop the services tracked by this handle.

def stop() -> None
save

Save handle to JSON file for later restoration.

def save(path: str = None) -> None

Parameters: - path (str, optional): File path. Defaults to .flox-services.json

Example:

handle = services.start("postgres")
handle.save()  # Saves to .flox-services.json
# Later, in another session...
handle = FloxServiceHandle.load()
handle.stop()

load (classmethod)

Load handle from JSON file.

@classmethod
def load(path: str = None) -> FloxServiceHandle

Parameters: - path (str, optional): File path. Defaults to .flox-services.json

Returns: - FloxServiceHandle: Loaded handle

ServiceInfo

Information about a service.

Attributes

  • name (str): Service name
  • status (str): Status (e.g., "Running", "Stopped")
  • pid (int | None): Process ID if running

Methods

as_tuple

Convert to tuple format.

def as_tuple() -> tuple[str, str, int | None]

Returns: - tuple: (name, status, pid)

Examples

Full Development Workflow

from angreal.integrations.flox import Flox, flox_required
import angreal

# Check Flox availability at module load
if not Flox.is_available():
    print("Warning: Flox not installed. Some commands may not work.")

@angreal.command(name="setup")
def setup():
    """Initialize Flox environment."""
    import subprocess
    subprocess.run(["flox", "init"])
    subprocess.run(["flox", "install", "python@3.11", "nodejs@20", "postgresql"])

@angreal.command(name="test")
@flox_required(".", services=["postgres"])
def test():
    """Run tests with database."""
    import subprocess
    result = subprocess.run(["pytest", "-v", "tests/"])
    return result.returncode

@angreal.command(name="dev")
@flox_required(".", services=["postgres", "redis"])
def dev():
    """Start development server."""
    import subprocess
    subprocess.run(["python", "manage.py", "runserver"])

@angreal.command(name="build")
@flox_required(".")
def build():
    """Build the project."""
    flox = Flox(".")
    exit_code, stdout, stderr = flox.run("npm", ["run", "build"])
    if exit_code != 0:
        print(f"Build failed: {stderr}")
        return 1
    print("Build successful")
    return 0

Service Status Dashboard

from angreal.integrations.flox import Flox
import angreal

@angreal.command(name="status")
def status():
    """Show Flox environment and service status."""
    flox = Flox(".")

    print(f"Flox version: {Flox.version()}")
    print(f"Environment exists: {flox.exists}")
    print(f"Has manifest: {flox.has_manifest}")
    print(f"Path: {flox.path}")
    print()

    print("Services:")
    for svc in flox.services.status():
        pid_info = f"PID {svc.pid}" if svc.pid else "no PID"
        print(f"  {svc.name}: {svc.status} ({pid_info})")

See Also