Skip to content

Add scripts for generating type stubs reflecting runtime module layout#1519

Open
matejmatuska wants to merge 6 commits into
oamg:mainfrom
matejmatuska:gen_stubs_v2
Open

Add scripts for generating type stubs reflecting runtime module layout#1519
matejmatuska wants to merge 6 commits into
oamg:mainfrom
matejmatuska:gen_stubs_v2

Conversation

@matejmatuska
Copy link
Copy Markdown
Member

Due to the various dynamic import mechanism that the leapp framework uses to import leapp entities, development tools such as language servers or type checkers work very unreliably if at all.

This PR adds scripts for generation of type stubs for leapp framework components, namely: actor libs, common libs, models, tags, topics (no actors, they are never imported). These generated type stub files are stored in a typings directory in a layout which simulates the import hierarchy at runtime, i.e. a common library defined at repos/system_upgrade/common/libraries/grub.py has a type stub at typings/leapp/libraries/common/grub.pyi.

When tools such as the Pylance language server (the VS code extension for Python) or pyright (upstream of Pylance) are pointed at the type stub directory, they prefer it over the real source code. These stubs are then used as a source of information which brings (more like fixes) several features:

  • imports are resolved correctly
  • autocompletion
    • when autocompleting something it can be auto-imported
  • type information (mostly for models) and docstrings on mouse hover or other such places
  • better type checking (mostly for models)
  • method signature help

There are also some limitations, however no regressions, I believe:

  • go-to-definition and go-to-references or similar navigation features navigate to the type stub file instead of the real file
  • the content of the typings directory is currently static, i.e. you have to live with some errors until you regenerate it again
    • generation takes about 5-6s on my machine
    • this could be further sped up by only regenerating what changed based on e.g. git rev

Usage

  1. If you use pyright or pylance (VS code extension) the included pyrightconfig.json should get picked up by that and the typings directory will be used automatically. If you use anything else e.g. jedi, mypy, etc. You might need to point to the typings directory, but seems like the name is standard.
  2. Run make stubgen to generate.
  3. Enjoy

Technical notes

All of the stubs are generated using mypy.stubgen, except for the model ones. Those are generated using a custom script with the ast module, because of how models are implemented (metaclasses and class properties). This allows offering much better type information because the scripts converts the leapp field types to actual standard python type hints (whereas mypy only generates _typeshed.Incomplete). Also, docstrings for the fields are preserved (though due to them not being standardized the tools might not show them).

There is also a .stub_tmp directory which is a temporary dictionary to where the direct outputs of the generators are put before they are assembled. The assembly is a process in which multiple stubs are combined to a single __init__.py for a flat module hierarchy and import statements are inserted, this applies to models, tags and topics. Then all the stubs are moved to the correct places.

Other solutions considered

I tried to run the stub generation tool (mypy.stubgen) after actually loading the upgrade leapp repositories via leapp, however due to how the imports work and the different dynamic import schemes used for models, libraries and tags and topics, this requires a lot of workarounds and usage of the lower level stubgen apis such as InspectionStubGenerator.
It is also slower due to the loading of repositories.

Examples

  • Autocompletion:
image
  • Imports are resolved (note the warning is that it could not be resolved from source, i.e. it is resolved from the type stub file)
image
  • Signature hint with types and docstring:
image
  • In VS code:
2026-02-25T18:25:42,728273978+01:00

@github-actions
Copy link
Copy Markdown

Thank you for contributing to the Leapp project!

Please note that every PR needs to comply with the leapp-repository contribution and development guidelines and must pass all tests in order to be mergeable.
If you want to request a review or rebuild a package in copr, you can use following commands as a comment:

  • review please @oamg/developers to notify leapp developers of the review request
  • /packit copr-build to submit a public copr build using packit

Packit will automatically schedule regression tests for this PR's build and latest upstream leapp build.
However, here are additional useful commands for packit:

  • /packit test to re-run manually the default tests
  • /packit retest-failed to re-run failed tests manually
  • /packit test oamg/leapp#42 to run tests with leapp builds for the leapp PR#42 (default is latest upstream - main - build)

Note that first time contributors cannot run tests automatically - they need to be started by a reviewer.

It is possible to schedule specific on-demand tests as well. Currently 2 test sets are supported, beaker-minimal and kernel-rt, both can be used to be run on all upgrade paths or just a couple of specific ones.
To launch on-demand tests with packit:

  • /packit test --labels kernel-rt to schedule kernel-rt tests set for all upgrade paths
  • /packit test --labels beaker-minimal-8.10to9.4,kernel-rt-8.10to9.4 to schedule kernel-rt and beaker-minimal test sets for 8.10->9.4 upgrade path

See other labels for particular jobs defined in the .packit.yaml file.

Please open ticket in case you experience technical problem with the CI. (RH internal only)

Note: In case there are problems with tests not being triggered automatically on new PR/commit or pending for a long time, please contact leapp-infra.

@matejmatuska matejmatuska requested a review from MichalHe April 10, 2026 16:18
@matejmatuska matejmatuska added the enhancement New feature or request label Apr 10, 2026
@matejmatuska matejmatuska changed the title Add scripts for generating type stubs faking runtime import hierarchy Add scripts for generating type stubs reflecting runtime module layout Apr 10, 2026
Comment thread utils/stubgen/gen_stubs.py Outdated
Comment thread utils/stubgen/gen_stubs.py Outdated
Copy link
Copy Markdown
Member

@pirat89 pirat89 left a comment

Choose a reason for hiding this comment

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

just preventive block - to no forget to fix commits before the merge :-D

@PeterMocary PeterMocary self-requested a review April 21, 2026 15:06
@matejmatuska
Copy link
Copy Markdown
Member Author

matejmatuska commented Apr 21, 2026

Just to clarify, this doesn't make mypy work very good. mypy is not very good at merging definitions from the source file with definitions in the type stub file (pyright is quite good at that). Furthermore the leapp-repository codebase as well as the leapp framework codebase have almost no type annotations.

It is possible to make it work relatively OK, however there is a lot of noise.

That said it works quite nice with pyright, which was the primary goal of the work. We can expand in the future.

@matejmatuska matejmatuska added the documentation Docs/comments with no functional change label Apr 21, 2026
:glob:

setup-devel-env
setup-devel-tools
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@matejmatuska thematically it should be be part of setup-devel-env

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah I was thinking about that, but I am not sure if it fits there, the dev env is means something a little different there.

@@ -0,0 +1,19 @@
# Making development tools work
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should use a different title. What about something like: Tweaks for type checking and editors?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah could be that.

@pirat89
Copy link
Copy Markdown
Member

pirat89 commented Apr 22, 2026

Just to clarify, this doesn't make mypy work very good. mypy is not very good at merging definitions from the source file with definitions in the type stub file (pyright is quite good at that). Furthermore the leapp-repository codebase as well as the leapp framework codebase have almost no type annotations.

It is possible to make it work relatively OK, however there is a lot of noise.

That said it works quite nice with pyright, which was the primary goal of the work. We can expand in the future.

good to know!

@pirat89 pirat89 added this to the 8.10/9.9 milestone Apr 27, 2026
Enum fields where the choices are defined via constants rather than
literals are now handled as Incomplete, rather than Literal[] which is
invalid.

Also, contants defined in a model, such as enum members now have type
Literal[<the literal value>] rather than Any.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Docs/comments with no functional change enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants