How to set up and use Python LSTs with the Moderne CLI
Moderne supports Python LSTs, enabling semantically-aware refactoring of Python code. With Python LSTs, recipes can resolve types, understand import graphs, and make precise automated changes across your Python repositories.
In this guide, we'll walk you through how to configure the Moderne CLI to take advantage of Python support.
As of CLI v4.3.0, the CLI parses Python out of the box, so most users don't need any moderne.yml changes to get started. If you're on an older CLI version or you use an explicit build.steps configuration, you'll need to add the Python build step manually.
Prerequisites
This guide assumes that:
- You have installed and configured the Moderne CLI (version
3.57.0or higher) - You are familiar with running Moderne CLI commands (if not, work through our CLI workshop)
- You have Python installed on your machine
Step 1: (Optionally) Configure your Python installation
By default, the CLI automatically detects Python installations in standard locations on your machine.
Discovering installations
You can see all detected Python installations by running:
mod config python installation list
Adding installation locations
If Python is installed in a non-standard location (for example, inside a virtual environment or a user-managed install), you can register it:
mod config python installation edit /path/to/python
Each path should point to the Python executable you want the CLI to use.
To remove manually configured installation paths:
mod config python installation delete
This only removes user-configured paths. Automatically discovered installations remain available.
Selecting a version
To pin the Python version globally:
mod config python version edit 3.12
You can use a major version (3), minor (3.12), or exact patch (3.12.1) depending on how tight you need the match to be. The version must correspond to one of the installations known to the CLI.
To apply a version only to a specific group of repositories, use the --local flag:
mod config python version edit 3.10 --local ./legacy-set
To revert to auto-detection:
mod config python version delete
To see the currently configured version:
mod config python version show
Step 2: (Optionally) Clone a custom list of repositories
If you don't have the repositories you want to work with cloned locally already, you can clone a group of them by defining a repos.csv file that lists them out such as in the following example:
cloneUrl,branch,origin,path
git@github.com:psf/requests.git,main,github.com,psf/requests
git@github.com:pallets/flask.git,main,github.com,pallets/flask
git@github.com:django/django.git,main,github.com,django/django
Check out our documentation on creating a repos.csv file for more detailed information about what's expected in this file.
After creating the CSV, clone the repositories by running the following command:
mod git sync csv . repos.csv --with-sources
Step 3: Build your Python repositories
The next thing you'll need to do is build LSTs for each of your repositories. To build the LSTs, run:
mod build /path/to/your/repos
Presuming everything has been set up correctly, you should see output similar to:
▶ psf/requests@main
Build output will be written to build.log
> Step 1 - build with Python
Selected Python 3.12.0
Processing Python project: .
Started project parsing: /Users/someuser/repos/psf/requests
Discovered 42 files to parse
✓ Built requests-20260213120000000-ast.jar
Cleaned 1 older builds
Step 4: Install recipes
In order to run recipes, you'll need to make sure the recipes are installed on your local machine. Python recipes come from JARs, pip packages, or both:
mod config recipes jar install org.openrewrite:rewrite-python:8.88.0
mod config recipes pip install openrewrite-migrate-python==0.11.0
The Python migration recipes are split across packages that call into each other as they run:
openrewrite-migrate-python(pip) holds the source migrations and theUpgradeToPython3XXcomposites.org.openrewrite.recipe:rewrite-migrate-python(JAR) holds the project file, Dockerfile, CloudFormation, and Mend.whitesourceupdates that those composites delegate to.org.openrewrite:rewrite-python(JAR) is the core language module that both of the above call into for dependency handling.
The CLI resolves these delegates before it runs anything, so a missing package fails the whole run rather than skipping a step. Install all three:
mod config recipes pip install openrewrite-migrate-python==0.11.0
mod config recipes jar install org.openrewrite.recipe:rewrite-migrate-python:0.11.0
mod config recipes jar install org.openrewrite:rewrite-python:8.88.0
If you keep your marketplace in sync with a recipe marketplace CSV, these are already installed for you and you can skip this step.
You can find the specific installation command for any recipe on its page in the recipe catalog.
Step 5: Run recipes
With the LSTs built and recipes installed, you can now run recipes against your Python repositories. You can either specify the full recipe path for running such as in:
mod run . --recipe=org.openrewrite.python.migrate.UpgradeToPython313
Or, you can search for a specific recipe and set it as the active recipe:
mod config recipes search UpgradeToPython313
Then you can run the active recipe by:
mod run . --active-recipe
Step 6: View data tables
Many recipes will also produce useful data tables that you can access via the mod study command such as in:
mod study . --last-recipe-run --data-table SourcesFileResults
Moderne CLI 4.4.3
⏺ Reading organization
Found 1 organization containing 1 repository (1s)
Found recipe run 20260213120000-xYzAb
⏺ Building CSV output for each organization
▶ Python Repos
✓ Data table produced
Done (1s)
⏺ Converting to Excel for each organization
▶ Python Repos
✓ Data table produced
Done (2s)
Data tables for each organization with rows are linked above
Adding the Python build step manually
You only need this step if you're on a CLI version older than v4.3.0, or if you maintain an explicit build.steps list in your moderne.yml file. An explicit list replaces the default pipeline, so it must include - type: python for Python to be parsed. On CLI v4.3.0 and later with the default configuration, Python support is already enabled and you can skip this.
Update the build steps in your moderne.yml file to include Python. This file is located at ~/.moderne/cli/moderne.yml and is created when you first set up the CLI.
If your moderne.yml file already includes a build section, add a - type: python step before the trailing resource step. If it doesn't, add the entire section as shown below:
# Other keys and values...
license:
key: some-license
tenant:
host: https://app.moderne.io
apiHost: https://api.app.moderne.io
skipSsl: false
authorization: Bearer mat-some-token
build:
steps:
- type: maven
- type: gradle
- type: bazel
- type: python
- type: resource
inclusion: |-
**/*
If you maintain an explicit configuration, start from the full default pipeline so you don't drop steps the CLI would otherwise run, such as sbt and javascript.