Gradle use-python plugin


2.3.0 (Mar 2, 2021)
Dec 11, 2017
Jun 30, 2021 (Retired)
Vyacheslav Rusakov (xvik)
Vyacheslav Rusakov (xvik)
Greg Brown (yellowsquid)
Jared Rhizor (jrhizor)
Stijn De Haes (stijndehaes)
Source code


Gradle use-python plugin


Plugin does not install python and pip itself and use globally installed python (by default). It's easier to prepare python manually because python have good compatibility (from user perspective) and does not need to be updated often.

The only plugin intention is to simplify python usage from gradle. By default, plugin creates python virtualenv inside the project and installs all modules there so each project has its own python (copy) and could not be affected by other projects or system changes.


  • Install required python modules using pip (per project (virtualenv), os user (--user) or globally)
  • Provides task to call python commands, modules or scripts (PythonTask)
  • Could be used as basement for building plugins for specific python modules (like mkdocs plugin)

Who's using (usage examples)

  • Configuration: python
  • Tasks:
    • checkPython - validate python installation (and create virtualenv if required)
    • pipInstall - install declared pip modules
    • pipUpdates - show the latest available versions for the registered modules
    • pipList - show all installed modules (the same as pipInstall shows after installation)
    • type:PythonTask - call python command/script/module
    • type:PipInstallTask - may be used for custom pip modules installation workflow
Possible pip issue warning (linux/macos)

If pip3 list -o fails with: TypeError: '>' not supported between instances of 'Version' and 'Version' Then simply update installed pip version: python3 -m pip install --upgrade pip

This is a known issue related to incorrectly patched pip packages in some distributions.


buildscript {
    repositories {
    dependencies {
        classpath 'ru.vyarus:gradle-use-python-plugin:2.3.0'
apply plugin: 'ru.vyarus.use-python'


plugins {
    id 'ru.vyarus.use-python' version '2.3.0'


Plugin compiled for java 8, compatible with java 11

Gradle Version
5-6 2.3.0
4.x 1.2.0


Snapshots may be used through JitPack
  • Go to JitPack project page
  • Select Commits section and click Get it on commit you want to use or use master-SNAPSHOT to use the most recent snapshot

For gradle before 6.0 use buildscript block with required commit hash as version:

buildscript {
    repositories {
        maven { url '' }
    dependencies {
        classpath 'ru.vyarus:gradle-use-python-plugin:2450c7e881'
apply plugin: 'ru.vyarus.use-python'

For gradle 6.0 and above:

  • Add to settings.gradle (top most!) with required commit hash as version:

    pluginManagement {
        resolutionStrategy {
            eachPlugin {
                if ( == 'ru.vyarus.use-python') {
        repositories {
            maven { url '' }
  • Use plugin without declaring version:

    plugins {
        id 'ru.vyarus.use-python'

Python & Pip

Make sure python and pip are installed:

python --version  
pip --version

On *nix python usually reference python2. For python3:

python3 --version  
pip3 --version
Windows install

Download and install python manually or use chocolately:

choco install python

In Windows 10 python 3.9 could be installed from Windows Store: just type 'python' in console and windows will open Windows Store's python page. No additional actions required after installation.

Note that windows store python will require minium virtualenv 20.0.11 (or above). (if virtualenv not yet installed then no worry - plugin will install the correct version)

Linux/Macos install

On most *nix distributions python is already installed, but often without pip.

Install pip if required (ubuntu example):

sudo apt-get install python3-pip

Make sure the latest pip installed (required to overcome some older pip problems):

pip3 install -U pip

To install exact pip version:

pip3 install -U pip==20.0.11

Note that on ubuntu pip installed with python3-pip package is 9.0.1, but it did not(!) downgrade module versions (e.g. pip install click 6.6 when click 6.7 is installed will do nothing). Maybe there are other differences, so it's highly recommended to upgrade pip with pip3 install -U pip.

Automatic pip upgrade

As described above, there are different ways of pip installation in linux and, more important, admin permissions are required to upgrade global pip. So it is impossible to upgrade pip from the plugin (in all cases).

But, it is possible inside virtualenv or user (--user) scope. Note that plugin creates virtualenv by default (per project independent python environment).

So, in order to use newer pip simply put it as first dependency:

python {
    pip 'pip:10.0.1'
    pip 'some_module:1.0'

Here project virtualenv will be created with global pip and newer pip version installed inside environment. Packages installation is sequential, so all other packages will be installed with newer pip (each installation is independent pip command).

The same will work for user scope: python.scope = USER

When applying this trick, consider minimal pip version declared in configuration (python.minPipVersion='9' by default) as minimal pip version required for project setup (instead of minimal version required for work).

Automatic python install

Python is assumed to be used as java: install and forget. It perfectly fits user use case: install python once and plugin will replace all manual work on project environment setup.

It is also easy to configure python on CI (like travis).

If you want automatic python installation, try looking on JetBrain's python-envs plugin. But be careful because it has some caveats (for example, on windows python could be installed automatically just once and requires manual un-installation).

Multi-module projects

When used in multi-module project, plugin will create virtualenv inside the root project directory in order to share the same environment for all modules.

See multi-module setup cases

Travis CI configuration

To make plugin work on travis you'll need to install python3 packages:

language: java  
dist: bionic
jdk: openjdk8

    - python3
    - python3-pip
    - python3-setuptools 

  - python3 --version
  - pip3 --version
  - pip3 install -U pip

It will be python 3.6 by default (for bionic).

Appveyour CI configuration

To make plugin work on appveyour you'll need to add python to path:

        - JAVA_HOME: C:\Program Files\Java\jdk1.8.0
          PYTHON: "C:\\Python36-x64"

  - set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%
  - python --version

Now plugin would be able to find python binary.

To use python 3.9 you'll need to switch image:

image: Visual Studio 2019

See available pythons matrix for more info.


Declare required modules (optional):

python.pip 'module1:1.0', 'module2:1.0'


python {
    pip 'module1:1.0'
    pip 'module2:1.0'

Module format is: name:version (will mean name==version in pip notion). Non strict version definition is not allowed (for obvious reasons). Dependencies are installed in declaration order. If duplicate declaration specified then only the latest declaration will be used:

python.pip 'module1:2.0', 'module2:1.0', 'module1:1.0' 

Will install version 1.0 of module1 because it was the latest declaration. "Module overrides" works for all declaration types (see below): the latest declared module version always wins.

Dependencies are installed with pipInstall task which is called before any declared PythonTask.

Note that by default dependencies are installed inside project specific virtualenv (project specific copy of python environment).

Behaviour matrix for possible scope and installVirtualenv configurations:

scope installVirtualenv Behaviour default
GLOBAL ignored packages installed in global scope (pip install name)
USER ignored packages installed in user scope (pip install name --user)
VIRTUALENV_OR_USER true if virtualenv not installed, install it in user scope; create project specific virtualenv and use it default
VIRTUALENV_OR_USER false when virtualenv is not installed install packages in user scope (same as USER); when virtualenv installed create project specific virtualenv and use it
VIRTUALENV true if virtualenv not installed, install it in user scope; create project specific virtualenv and use it
VIRTUALENV false throw error when virtualenv not installed

Note that VIRTUALENV + true and VIRTUALENV_OR_USER + true behaviours are the same. Different scope name here describes behavior for unexpected installVirtualenv=false change (to fail or fallback to user scope).

USER and GLOBAL scopes will ignore local (virtual)environment, even if project-specific environment was created before, with these options global python will be used instead.

Pip module extra features

You can declare modules with extra features in module name to install special version of module (with enabled features):

python.pip 'requests[socks,security]:2.18.4'

IMPORTANT: it is impossible to track if this "variation" of module is installed, so plugin performs up-to-date check for such modules by name only (for example, if 'requests==2.18.4' is already installed). For most cases, this is suitable behaviour because, by default, modules are installed in virtualenv and so you will always have correct module installed. For other cases, you can disable up-to-date checks (delegate all dependencies logic to pip): python.alwaysInstallModules = true

VCS pip modules

You can declare vcs modules: modules installed directly from version control (e.g. git, svn). Format:

  • @vcsVersion part is required: prefer using commit version or tag for reproducible builds
  • -pkgVersion is installed module version. Required to be able to compare declared plugin with installed version.

For example:

python.pip 'git+'

Declares module boson version 0.9, installed from git commit b52727f7170acbedc5a1b4e1df03972bd9bb85e3 (it may be tag name or branch, but prefer not using branch names).

pipInstall will be considered up-to-date if boson==0.9 is already installed. Note that declared module version is completely free: you can set any version (0.10, 1.2, etc.), it is not checked and used only for up-to-date validation.

WARNING: module version part assumed to follow the last dash, so if you specify version like somethinf-12.0-alpha.1 it would be parsed incorrectly (as package somethinf-12.0 version alpha.1)! Don't use dashes in a version!

Vcs module installation is: source checkout and module build (using You may need to specify subdirectory as &subdirectory=pkg_dir (see docs)

To avoid installation problems, package version is not used for actual installation (in spite of the fact that its official convention, it doesnt work in some cases). For example, module above will be installed as (no -0.9):

pip install git+

All pip supported vcs could be used: git, svn, hg, bzr

If up-to-date logic, implemented by pipInstall task, does not suit your needs, you can always disable it with python.alwaysInstallModules = true (pip always called). But this will be slower.

NOTE: since pip 20, compiled vcs module is cached (before it was build on each execution), but it is possible to disable cache (for all modules) with python.usePipCache=false configuration (applies --no-cache-dir pip flag)

Extra pip repositories

To add additional pip repositories (probably self-hosted):

python {
    extraIndexUrls = ["", ""]

or with shortcut method (shortcut may be used multiple times):

python {
    extraIndexUrls "", "" 

Extra urls will be applied as --extra-index-url flag for pip commands supporting it: install, download, list and wheel. By default, it only affects pipInstall and pipList tasks. Applied for all BasePipTask, so if you have custom pip tasks, it would be affected too.

In case of ssl problems (stale or self-signed certificated), mark domains as trusted:

python {
    trustedHosts = [""]


python {
    trustedHosts ""

Applied as --trusted-host option only for pipInstall (because pip install is the only command supporting this option).

NOTE: if, for some reason, you don't want to specify it for all pip tasks, you can configure exact task, for example: pipInstall.extraIndexUrls = ["", ""]

Extra pip install options

It is impossible to support directly all possible pip install options usages directly with api (safe way), so there is a direct configuration for an additional options. For example:

pipInstall.options('--upgrade-strategy', 'only-if-needed')

Shortcut method above may be called multiple times:

pipInstall.options('--a', 'value')
pipInstall.options('--b', 'value')

Or you can use property directly:

pipInstall.options = ['--a', 'value', '--b', 'value']


When you declare any pip modules, plugin will try to use virtualenv in order to install required modules locally (for current project only).

If virtualenv is not installed - it will be installed automatically in --user scope. If you don't want automatic installation then disable it:

python.installVirtualenv = false

Plugin installs exact pip version declared in python.virtualenvVersion (by default, 16.7.9). This way, plugin will always install only known to be working version and avoid side effects of "just released" versions (note that pip 20 is a major rewrite and may still contain side effects).

In any case, plugin checks if virtualenv is already installed and use it to create local environment (if not, then fall back to --user scope by default). Virtualenv usage is driven by declared scope, so if you don't want to use it set:

python.scope = USER // or GLOBAL

With USER (or GLOBAL) scope, virtualenv will not be used, even if it's already created in project (plugin will ignore it and use global python).

If you already use virtualenv in your project (have created manually environment), then simply point plugin to use it:

python.envPath = 'path/to/your/env'

It will automatically change pythonPath configuration accordingly.

NOTE: plugin will not create environment if you don't use any modules. If you still want to use project specific environment (without declared pip modules) then create it manually: python3 -m virtualenv .gradle/python (default location). Plugin will recognize existing env and use it.

IMPORTANT: virtualenv creates local python copy (by default in .gradle/python). Copy is created from global python and later used instead of global python. If you want to change used python version in the environment, then manually remove .gradle/python so it could be created again (from global python).

To copy environment instead of symlinking (default) set (--always-copy):

python.envCopy = true


Pip dependencies could be installed per project, for current user (~/) or globally.

Default behaviour:

  • if virtualenv module installed (or automatically installed, see above): manage pip dependencies per project (env .gradle/python created)
  • if no virtualenv - use user scope (--user pip flag): pip modules are installed only for current user (this avoid permission problems on linux)

To change defaults:

python.scope = VIRTUALENV
  • GLOBAL - install modules globally (this may not work on linux due to permissions)
  • USER - use --user flag to install for current user only
  • VIRTUALENV_OR_USER - default
  • VIRTUALENV - use virtualenv (if module not installed - error thrown)

Note that values may be declared without quotes because it's an enum which values are declared as project ext properties (ext.USER==ru.vyarus.gradle.plugin.python.PythonExtension.Scope.USER).

Complete behaviour matrix see above

Check modules updates

To quick check if new versions are available for the registered pip modules use pipUpdates task:

The following modules could be updated:

 package            version latest type 
 ------------------ ------- ------ -----
 click              6.6     6.7    wheel

Note that it will not show versions for transitive modules, only for modules specified directly in python.pip.

To see all available updates (without filtering):

pipUpdates.all = true

NOTE: If you see an error like

TypeError: '>' not supported between instances of 'Version' and 'SetuptoolsVersion'

then update pip:

pip install -U pip

Call python

Call python command:

task cmd(type: PythonTask) {
    command = "-c print('sample')"

called: python -c print('sample') on win and python -c exec("print('sample')") on *nix (exec applied automatically for compatibility)

Call multi-line command:

task cmd(type: PythonTask) {
    command = "-c \"import sys; print(sys.prefix)\""

called: python -c "import sys; print(sys.prefix)" on win and python -c exec("import sys; print(sys.prefix)") on *nix

NOTE: it is important to wrap script with space in quotes (otherwise parser will incorrectly parse arguments).

Call module:

task mod(type: PythonTask) {
    module = 'sample' 
    command = "mod args"

called: python -m sample mod args

Call script:

task script(type: PythonTask) { 
    command = "path/to/ 1 2"

called: python path/to/ 1 2 (arguments are optional, just for demo)

String command is used for simplicity, but it could be array/collection of args:

task script(type: PythonTask) { 
    command = ['path/to/', '1', '2'] 
Command parsing

When command passed as string it is manually parsed to arguments array (split by space):

  • Spaces in quotes are ignored: "quoted space" or 'quoted space'
  • Escaped spaces are ignored: with\\ space (argument will be used with simple space then - escape removed).
  • Escaped quotes are ignored: "with \\"interrnal quotes\\" inside". But pay attention that it must be 2 symbols \\" and not \" because otherwise it is impossible to detect escape.

To view parsed arguments run gradle with -i flag (enable info logs). In case when command can't be parsed properly (bug in parser or unsupported case) use array of arguments instead of string.

Environment variables

By default, executed python can access system environment variables (same as System.getenv()).

To declare custom (process specific) variables:

task sample(type: PythonTask) {
       command = "-c \"import os;print('variables: '+os.getenv('some', 'null')+' '+os.getenv('foo', 'null'))\""
       environment 'some', 1
       environment 'other', 2
       environment(['foo': 'bar', 'baz': 'bag'])

Map based declaration (environment(['foo': 'bar', 'baz': 'bag'])) does not remove previously declared variables (just add all vars from map), but direct assignment environment = ['foo': 'bar', 'baz': 'bag'] will reset variables.

System variables will be available even after declaring custom variables (of course, custom variables could override global value).

NOTE: environment variable could also be declared in extension to apply for all python commands: python.environment 'some', 1 (if environments declared both globally (through extension) and directly on task, they would be merged)


Python location

On linux, plugin will use python3 if available (and fall back to python if not). To use different binary use:

python {
    pythonBinary = 'python'

This will force python 2 for linux. Also, this may be handy if python binary is named differently.

To use non global python:

python {
    pythonPath = 'path/to/python/binray/'

pythonPath must be set to directory containing python binary (e.g. 'path/to/python/binray/python.exe')

NOTE: pythonPath is ignored when virtualenv used (virtualenv located at python.envPath already exists).

Minimal python and pip versions

To set python version constraint:

python {
    minPythonVersion = '3.2'

Python version format is: major.minor.micro. Constraint may include any number of levels: '3', '3.1', '2.7.5'

The same way pip version could be restricted:

python {
    minPipVersion = '9.0.1'

By default, all installed python modules are printed to console after pip installations using pip list (of course, if at least one module declared for installation). This should simplify problems resolution (show used transitive dependencies versions).

To switch off:

python {
    showInstalledVersions = false

You can always see the list of installed modules with pipList task (exactly the same list as after pipInstall).

NOTE: if global python is used with USER scope and some modules were manually installed in global scope then they will not be shown by pipList (and after pip install). To see all modules:

pipList.all = true

Global modules are hidden by default (for USER scope) because on linux there are a lot of system modules pre-installed.

By default, 'pip install' is not called for modules already installed with correct version. In most situations this is preferred behaviour, but if you need to be sure about dependencies then force installation:

python {
    alwaysInstallModules = true

All configuration options with default values:

python {
   // path to python binary (global by default)
   // python binary name (python or python3 by default)
   // additional environment variables, visible for all python commands
   environment = [:]
   // minimal required python version (m.m.m)
   // minimal required pip version (m.m.m)
   minPipVersion = '9'   
   // show all installed modules versions after pip installation
   showInstalledVersions = true
   // always call module install, even if correct version is already installed
   alwaysInstallModules = false
   // may be used to disable pip cache (--no-cache-dir option)
   usePipCache = true
   // additional pip repositories (--extra-index-url option)
   extraIndexUrls = []
   // trusted hosts for pip install (--trusted-host option)
   trustedHosts = []

    // pip modules installation scope (project local, os user dir, global) 
   // automatically install virtualenv module (if pip modules declared and scope allows)   
   installVirtualenv = true
   // if virtualenv not installed (in --user scope), plugin will install exactly this version
   // (known to be working version) to avoid side effects
   virtualenvVersion = '20.4.2'
   // minimal required virtualenv (v20 is recommended, but by default 16 set to not fail previous
  // setups)
   minVirtualenvVersion = '16'
   // used virtualenv path (if virtualenv used, see 'scope')
   envPath = '.gradle/python'
   // copy virtualenv instead of symlink (when created)
   envCopy = false   

Note that in case of multi-module project envPath is set to '.gradle/python' inside the root project, even if plugin is activated inside module (see multi-module setup).


PythonTask configuration:

Property Description
pythonPath Path to python binary. By default used path declared in global configuration
pythonBinary Python binary name. By default, python3 on linux and python otherwise.
workDir Working directory (important if called script/module do file operations). By default, it's a project root
createWorkDir Automatically create working directory if does not exist. Enabled by default
module Module name to call command on (if command not set module called directly). Useful for derived tasks.
command Python command to execute (string, array, iterable)
logLevel Logging level for python output. By default is LIFECYCLE (visible in console). To hide output use LogLevel.INFO
pythonArgs Extra python arguments applied just after python binary. Useful for declaring common python options (-I, -S, etc.)
extraArgs Extra arguments applied at the end of declared command (usually module arguments). Useful for derived tasks to declare default options
outputPrefix Prefix, applied for each line of python output. By default is '\t' to identify output for called gradle command
environment Process specific environment variables

Also, task provide extra methods:

  • pythonArgs(String... args) to declare extra python arguments (shortcut to append values to pythonArgs property).
  • extraArgs(String... args) to declare extra arguments (shortcut to append values to extraArgs property).
  • environment(String var, Object value) to set custom environment variable (shortcut to append values to environment property)
  • environment(Map<String, Object> vars) to set multiple custom environment variables at once (shortcut to append values to environment property)


Default pip installation task is registered as pipInstall and used to install modules, declared in global configuration. Custom task(s) may be used, if required:

task myPipInst(type: PipInstallTask) {
    pip 'mod:1', 'other:2'


Property Description
pythonPath Path to python binary. By default used path declared in global configuration
pythonBinary Python binary name. By default, python3 on linux and python otherwise.
pythonArgs Extra python arguments applied just after python binary. Useful for declaring common python options (-I, -S, etc.)
environment Process specific environment variables
modules Modules to install. In most cases configured indirectly with pip(..) task methods. By default, modules from global configuration.
userScope Use current user scope (--user flag). Enabled by default to avoid permission problems on *nix (global configuration).
showInstalledVersions Perform pip list after installation. By default use global configuration value
alwaysInstallModules Call pip install module for all declared modules, even if it is already installed with correct version. By default use global configuration value
useCache Can be used to disable pip cache (--no-cache-dir)
extraIndexUrls Additional pip repositories (--extra-index-url)
/ trustedHosts / trusted hosts (--trusted-host) /
/ options / additional pip install options /

And, as shown above, custom methods:

  • pip(String... modules)
  • pip(Iterable<String> modules)
  • options(String... options)

Use as base for specific module plugin

Plugin supposed to be used as base for plugins for specific python modules. With it you don't need to implement modules installation and could use provided abstractions to call python.

Example usage: gradle-mkdocs-plugin.

In your plugin, add plugin as dependency:

dependencies {
    implementation 'ru.vyarus:gradle-use-python-plugin:2.3.0'

And apply plugin: project.plugins.apply(PythonPlugin) (required to register python extension and declare default pipInstall task).

Extended task

The simplest way is to extend PythonTask:

class SomeModuleTask extends PythonTask {
    String getModule() {
        // always call specified commands on module
        return 'somemodule'
    List<String> getExtraArgs() {
        // example of module options configuration with custom extension 
        def res = []
        SomeModuleExtension ext = project.extensions.getByType(SomeModuleExtension)
        if (ext.somOption) {
            res << '--option'
        return res
    // optionally apply extra behaviour
    void run() {
        // before python call              
        // after python call


pyton.pip 'sommemodule:1'

task modCmd(type: SomeModuleTask) {
    command = 'module args'

called: python -m somemodule module arfs --option

In some cases, you can use BasePythonTask which is a super class of PythonTask and provides only automatic pythonPath and pythonBinary properties set from global configuration.

Completely custom task

Plugin provides ru.vyarus.gradle.plugin.python.cmd.Python utility class, which could be used directly in custom task (PythonTask is a wrapper above the utility).

Example usage:

Python python = new Python(project, getPythonPath(), getPythonBinary())

// execute and get command output
String out = python.readOutput(cmd)

// call module (the same as exec() but applies '-m mod' before command)
python.callModule('mod', cmd)

// direct python call

This could be used directly in the completely custom task.

Specific utility for target module could be defined, see ru.vyarus.gradle.plugin.python.cmd.Pip util as an example (simplified):

class Pip {

    private final Python python

    Pip(Project project, String pythonPath, String binary) {
        // configure custom python execution util 
        python = new Python(project, pythonPath, binary)
    // declare module specific commands
    void install(String module) {
        python.callModule('pip', "install $module")

Apply default modules

In your plugin you could apply default modules like this:

afterEvaluate {
    PythonExtension ext = project.extensions.getByType(PythonExtension)
    // delayed default module(s) declaration based on user configuration
    if (!ext.isModuleDeclared('somemodule')) {
        ext.pip 'sommemodule:1'

Or always declare default modules (before configuration):

PythonExtension ext = project.extensions.getByType(PythonExtension)
ext.pip 'sommeodule:1', 'othermodule:2'

User will be able to override default versions by direct module declaration (even downgrade version):

python.pip 'sommodule:0.9'

NOTE: all pip declarations are supported so direct module version could be overridden with VCS declaration and vice-versa (only the declaration order is important).

Hide sensitive data in logged command

By default, plugin always logs executed python commands, but sometimes such commands could contain sensitive data (like passwords).

For example, pip's --extra-index-url may contain password:


In logged command password should be replaced with *****.

To deal with such cases, Python object supports registration of LoggedCommandCleaner object:

python.logCommandCleaner(new CleanerInstance)

As an example see Pip object, which register special cleaner for extra index passwords right in its constructor:

Pip(Python python, boolean userScope, boolean useCache) {
      // do not show passwords when external indexes used with credentials
      python.logCommandCleaner { CliUtils.hidePipCredentials(it) }

See CliUtils.hidePipCredentials for an implementation example (using regexps). Most likely, implementation would be the same in your case.

Might also like