Quick Start: Copy an existing installer, modify it, register it. Most installers are 100-200 lines.
You want Hatch to install dependencies from a new source:
- A package manager not yet supported (brew, pacman, etc.)
- A custom artifact repository
- Cloud services
- Version control systems beyond what's supported
All installers implement DependencyInstaller and get registered with installer_registry. The orchestrator finds the right installer by calling can_install() on each one.
Core interface (from hatch/installers/installer_base.py):
@property
def installer_type(self) -> str: # "python", "system", etc.
def can_install(self, dependency: Dict[str, Any]) -> bool: # Can handle this?
def install(self, dependency: Dict[str, Any], context: InstallationContext) -> InstallationResult: # Do it
# and so onStart with hatch/installers/system_installer.py - it's straightforward and handles subprocess calls well.
# my_installer.py
class MyInstaller(DependencyInstaller):
@property
def installer_type(self) -> str:
return "my-type" # What goes in dependency["type"]
def can_install(self, dependency: Dict[str, Any]) -> bool:
# Return True if you can handle this dependency
return dependency.get("type") == "my-type"
def install(self, dependency: Dict[str, Any], context: InstallationContext) -> InstallationResult:
# Your installation logic here
name = dependency["name"]
# ... do the work ...
return InstallationResult(
dependency_name=name,
status=InstallationStatus.COMPLETED,
installed_path=some_path,
installed_version=some_version
)Add to the bottom of your installer file:
from .registry import installer_registry
installer_registry.register_installer("my-type", MyInstaller)That's it. The orchestrator will find and use your installer automatically.
Error handling: Catch exceptions and wrap them in InstallationError:
try:
# your installation logic
except Exception as e:
raise InstallationError(f"Failed to install {name}: {e}") from eSubprocess calls: Use the patterns from SystemInstaller._run_subprocess() for external commands.
Progress reporting: The context parameter has a report_progress() method if users need feedback.
Validation: Implement validate_dependency() to check dependency format before attempting installation.
Copy test patterns from tests/test_system_installer.py:
- Mock external calls with
unittest.mock.patch - Test both success and failure cases
- Use
InstallationContextwith a temp directory for isolation
Look at existing installers to understand patterns:
SystemInstaller- subprocess calls to aptPythonInstaller- subprocess calls to pipDockerInstaller- Docker API callsHatchInstaller- file operations for local packages
Don't overthink it: Most installers are simple - check if you can handle it, then do the installation.
Context matters: Use the InstallationContext for target paths, environment info, and progress reporting.
Error messages: Make them actionable. "Permission denied installing X" is better than "Installation failed".