Skip to content

meta,api: lazy load fact and operation modules#1609

Merged
Fizzadar merged 10 commits intopyinfra-dev:3.xfrom
Dexmachi:cleanup_api_imports
Mar 30, 2026
Merged

meta,api: lazy load fact and operation modules#1609
Fizzadar merged 10 commits intopyinfra-dev:3.xfrom
Dexmachi:cleanup_api_imports

Conversation

@Dexmachi
Copy link
Copy Markdown
Contributor

@Dexmachi Dexmachi commented Mar 16, 2026

What

This PR introduces lazy loading inside of operations/init.py (including freebsd) and facts/init.py by leveraging PEP 562 module-level getattr.

Instead of eagerly globbing and importing all available facts and operations upfront, modules are now only imported on-demand when explicitly requested. To ensure compatibility with test runners like pytest and avoid ModuleNotFoundError during test discovery, the dynamic imports are properly anchored using package.

Note: I originally experimented with lazy loading api/init.py as well, but reverted it as it acts as the public contract for the library and making it lazy broke several downstream tests due to circular import complexities.

Why

The current implementation using glob and from . import * forces a massive I/O and CPU overhead whenever Pyinfra is used in API mode, loading dozens of modules that the user might never use.

By making this lazy:

  • The I/O from importing facts/operations only occurs for the specific modules actually utilized in a run.

  • Startup time and memory footprint are significantly reduced, especially for users wrapping Pyinfra as an execution engine.

  • IDE autocompletion and static type checkers are preserved because all is still dynamically generated without executing the module bodies.

Addition

Now tests/test_operations.py skips execution if not inside a debian based distro (I was having issues in Arch only to discover this was the issue)

Added: ansi_color in api/utils.py, this was made in order to remove the click dependency from inside the API.

It should be noted that this should futurally be refactored in order to separate lib and interface

  • Pull request is based on the default branch (3.x at this time)
  • [N/A] Pull request includes tests for any new/updated operations/facts -- (N/A - architecture revision)
  • [N/A] Pull request includes documentation for any new/updated operations/facts -- (N/A - architecture revision)
  • Tests pass (see scripts/dev-test.sh)
  • Type checking & code style passes (see scripts/dev-lint.sh)

@Dexmachi Dexmachi changed the title perf: Lazy load facts and operations to drastically reduce import time perf: Lazy load facts and operations to optimize Mar 16, 2026
@Dexmachi
Copy link
Copy Markdown
Contributor Author

this does fix this issue, as seen in the following files:

pyinfra_fact_import_new.txt
pyinfra_op_import_new.txt

As it is possible to see, only the bare minimum for each of these was imported.

I would still advise to try and lazify the imports to inside specific functions instead of importing everything in the start of the file, as this would heavily impact import startup.

@Dexmachi
Copy link
Copy Markdown
Contributor Author

Dexmachi commented Mar 16, 2026

sorry for the late show

either way, it is working perfectly:
image

the main issue is with the syntax

from pyinfra import operations

operations.server.shell
image

it does not load them in static evaluation for calling stuff like operations.server.shell, but this is was an issue before, so yeah

Copy link
Copy Markdown
Member

@Fizzadar Fizzadar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import improvements are 🔥, awesome stuff!

Other bits should be broken out or removed though please.

Comment thread src/pyinfra/api/facts.py
Comment thread src/pyinfra/facts/util/__init__.py Outdated
Comment thread src/pyinfra/facts/__init__.py Outdated
Comment thread src/pyinfra/operations/freebsd/__init__.py Outdated
Comment thread tests/test_operations.py Outdated
@Dexmachi
Copy link
Copy Markdown
Contributor Author

Dexmachi commented Mar 17, 2026

The import improvements are 🔥, awesome stuff!

Other bits should be broken out or removed though please.

Points taken!

I already fixed what was asked to be fixed (very simple shtuff altogether, so easy fix), however, I'm in an area that blocks port 22, so I'll take a while to push!

all tests (except debian based ones) are passing, and the code is linted

Copy link
Copy Markdown
Member

@Fizzadar Fizzadar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very awesome @Dexmachi, thank you for working on this 🙏

@Fizzadar Fizzadar changed the title perf: Lazy load facts and operations to optimize meta,api: lazy load fact and operation modules Mar 30, 2026
@Fizzadar Fizzadar merged commit 922da20 into pyinfra-dev:3.x Mar 30, 2026
26 checks passed
@Dexmachi
Copy link
Copy Markdown
Contributor Author

This is very awesome @Dexmachi, thank you for working on this 🙏

Its an honor to be a part of this!

@Dexmachi Dexmachi deleted the cleanup_api_imports branch March 30, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants