The Inventory is pyinfra’s system for managing target hosts, organizing them into groups, and associating configuration data. It’s the foundation of multi-host deployments and enables flexible, data-driven infrastructure automation.
The Inventory class (defined in src/pyinfra/api/inventory.py) represents a collection of hosts with their associated data and group memberships.
Data: Configuration values associated with hosts and groups
# From src/pyinfra/api/inventory.py:25-58class Inventory: """ Represents a collection of target hosts. Stores and provides access to group data, host data and default data for these hosts. Args: names_data: tuple of (names, data) override_data: dictionary of data overrides **groups: map of group name -> (names, data) """ state: State groups: dict[str, list[Host]] hosts: dict[str, Host] host_data: dict[str, dict] group_data: dict[str, dict] data: dict # Global/default data override_data: dict # Override data
When the inventory is created, it generates Host objects:
# From src/pyinfra/api/inventory.py:60-146def make_hosts_and_groups(self, names, groups) -> None: all_connectors = get_all_connectors() execution_connectors = get_execution_connectors() # Map name -> data name_to_data: dict[str, dict] = defaultdict(dict) # Map name -> group names name_to_group_names = defaultdict(list) # Process groups first for group_name, (group_names, group_data) in groups.items(): self.group_data[group_name] = group_data for name, data in extract_name_data(group_names): name_to_data[name].update(data) name_to_group_names[name].append(group_name) # Process top-level hosts for name, data in extract_name_data(names): name_to_data[name].update(data) # Create Host instances hosts: dict[str, Host] = {} for name, connector_cls in names_connectors: host_groups = name_to_group_names[name] host = Host( name, inventory=self, groups=host_groups, connector_cls=connector_cls, ) hosts[name] = host # Add to groups for group_name in host_groups: if host not in self.groups[group_name]: self.groups[group_name].append(host) self.hosts = hosts
from pyinfra import host# Access data attributesapp_port = host.data.app_portssh_user = host.data.ssh_user# With defaultsapp_port = host.data.get("app_port", 8000)# Check if data existsif hasattr(host.data, "app_port"): print(f"App port: {host.data.app_port}")
from pyinfra.api import Inventoryinventory = Inventory( (["web1", "web2", "db1"], {}), webservers=(["web1", "web2"], {}),)# Get specific hostweb1 = inventory.get_host("web1")# Get all hostsfor host in inventory: print(host.name)# Get host countprint(f"Total hosts: {len(inventory)}")
# Get hosts in a groupwebservers = inventory.get_group("webservers")for host in webservers: print(f"Webserver: {host.name}")# Check if group existsif "databases" in inventory.groups: db_hosts = inventory.get_group("databases")
# Get global dataglobal_data = inventory.get_data()# Get host datahost_data = inventory.get_host_data("web1")# Get group datagroup_data = inventory.get_group_data("webservers")# Get combined group datagroups_data = inventory.get_groups_data(["webservers", "production"])
# From src/pyinfra/api/inventory.py:92-113if name[0] == "@": connector_name = name[1:] arg_string = None if "/" in connector_name: connector_name, arg_string = connector_name.split("/", 1) if connector_name not in get_all_connectors(): raise NoConnectorError(f"Invalid connector: {connector_name}") # Connector can expand to multiple hosts names_data = all_connectors[connector_name].make_names_data(arg_string) # Each connector-generated host gets its own Host instance for sub_name, sub_data, sub_groups in names_data: # Create host with connector data ...
Example: Terraform connector generates hosts from Terraform state:
# Limit to specific hostspyinfra inventory.py deploy.py --limit web1.example.com# Limit to a grouppyinfra inventory.py deploy.py --limit webservers# Multiple limitspyinfra inventory.py deploy.py --limit web1,web2
In code:
from pyinfra.api import State, Inventoryinventory = Inventory((["web1", "web2", "db1"], {}))state = State()state.init(inventory, config)# Limit to specific hostsweb1 = inventory.get_host("web1")state.limit_hosts = [web1]# Operations will only run on web1
# From src/pyinfra/api/state.py:254-280class State: # Hosts we've activated at any time activated_hosts: set[Host] # Active hosts that *haven't* failed yet active_hosts: set[Host] # Hosts that have failed failed_hosts: set[Host]# Get active hosts (not failed)active = inventory.get_active_hosts()# Get all activated hosts (including failed)activated = list(inventory.iter_activated_hosts())
from pyinfra import hostfrom pyinfra.operations import apt# Only run on hosts in production groupif "production" in host.groups: apt.packages( name="Install production packages", packages=["nginx", "postgresql"], )# Only run on specific hostsif host.name == "web1.example.com": apt.packages( name="Install monitoring agent", packages=["datadog-agent"], )