Skip to content

angreal.integrations.flox

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

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.

Flox CLI must be installed before using this integration.

Installation:

Terminal window
# macOS
curl -fsSL https://flox.dev/install | bash
# Linux
curl -fsSL https://flox.dev/install | bash
# Verify installation
flox --version

Creating a Flox Environment:

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

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)

Main class for managing Flox environments.

from angreal.integrations.flox import Flox
flox = Flox(path=".")
Flox(path=None)

Parameters:

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

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

@property
def exists(self) -> bool

Returns:

  • bool: True if .flox/ directory exists

Check if the environment has a manifest file.

@property
def has_manifest(self) -> bool

Returns:

  • bool: True if .flox/env/manifest.toml exists

Get the environment path.

@property
def path(self) -> Path

Returns:

  • Path: Absolute path to the Flox environment

Get a FloxServices manager for this environment.

@property
def services(self) -> FloxServices

Returns:

  • FloxServices: Services manager instance

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()

Restore the original environment.

def deactivate(self) -> None

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

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}")

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

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")

Get Flox version string.

@classmethod
def version() -> str

Returns:

  • str: Flox version (e.g., “1.2.0”)

Raises:

  • RuntimeError: If Flox is not installed

Manages Flox services for an environment.

from angreal.integrations.flox import FloxServices
services = FloxServices(".")
# Or via Flox instance
services = Flox(".").services

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 all services in the environment.

def stop() -> None

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})")

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 services.

def restart(*services: str) -> None

Parameters:

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

Handle for managing started services across sessions.

  • 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

Stop the services tracked by this handle.

def stop() -> None

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 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

Information about a service.

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

Convert to tuple format.

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

Returns:

  • tuple: (name, status, pid)
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
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})")