---
component: python
version: "0.10"
slug: python/python-agent
canonical_url: "https://docs.gradle.com/develocity/python/0.10/python-agent/"
title: "Develocity Python Agent User Manual (beta)"
description: "How to install and configure the Develocity Python agent."
keywords: []
status: current
---

<!-- llms-index: https://docs.gradle.com/develocity/llms.txt -->

# Develocity Python Agent User Manual (beta)

<a id="preamble"></a>

Beta Feature

The Develocity Python agent enables integration with [Develocity](https://gradle.com).

> [!NOTE]
> Python support isn’t enabled by default with Develocity. If you want to use Python with Develocity, contact your customer success representative.

<a id="getting-set-up"></a>

## Getting Set Up

<a id="setup"></a>

### Setup

The Develocity Python agent is a Python package, but it isn’t publicly available on PyPI. To install it in your virtual environment, you can use the command line below.

```shell
pip install https://develocity-python-pkgs.gradle.com/develocity_agent-0.10-py3-none-any.whl
```

(function() { // Wait for DOM to be ready function ready(fn) { if (document.readyState !== 'loading') { fn(); } else { document.addEventListener('DOMContentLoaded', fn); } } ready(function() { // Find all collapsible togglers const togglers = document.querySelectorAll('.collapsible-toggler'); togglers.forEach(function(toggler) { toggler.addEventListener('click', function(e) { e.preventDefault(); const targetId = this.getAttribute('data-target'); const targetElement = document.getElementById(targetId); if (targetElement) { const wasHidden = targetElement.classList.contains('hidden'); // Toggle visibility with smooth animation if (wasHidden) { targetElement.classList.remove('hidden'); this.setAttribute('aria-expanded', 'true'); // Update icon to point down const icon = this.querySelector('.toggle-icon'); if (icon) icon.textContent = '▼'; // Add a brief highlight to show what expanded const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark'; const highlightColor = isDarkMode ? 'var(--background-subtle)' : 'var(--panel-background)'; targetElement.style.background = highlightColor; targetElement.style.transition = 'background-color 0.3s ease'; setTimeout(() => { targetElement.style.background = ''; }, 300); } else { targetElement.classList.add('hidden'); this.setAttribute('aria-expanded', 'false'); // Update icon to point right const icon = this.querySelector('.toggle-icon'); if (icon) icon.textContent = '▶'; } // Optional: Scroll to keep the toggler in view if content is large if (wasHidden && targetElement.offsetHeight > window.innerHeight / 2) { setTimeout(() => { toggler.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 100); } } }); // Add keyboard support (Enter and Space) toggler.addEventListener('keydown', function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); this.click(); } }); // Set initial aria-expanded state and add accessibility attributes const targetId = toggler.getAttribute('data-target'); const targetElement = document.getElementById(targetId); if (targetElement) { const isHidden = targetElement.classList.contains('hidden'); toggler.setAttribute('aria-expanded', isHidden ? 'false' : 'true'); toggler.setAttribute('role', 'button'); toggler.setAttribute('tabindex', '0'); toggler.setAttribute('aria-controls', targetId); // Add a title for better UX const actionText = isHidden ? 'Click to expand' : 'Click to collapse'; toggler.setAttribute('title', toggler.textContent.trim() + ' - ' + actionText); // Debug: Add a data attribute to verify state toggler.setAttribute('data-debug-state', isHidden ? 'collapsed' : 'expanded'); // Set initial icon state const icon = toggler.querySelector('.toggle-icon'); if (icon) { icon.textContent = isHidden ? '▶' : '▼'; } } }); }); })(); .collapsible-toggler { background: none; border: none; color: var(--heading-font-color); cursor: pointer; text-decoration: none; padding: 2px 4px; font: inherit; display: inline-flex; align-items: center; gap: 6px; border-radius: 3px; transition: all 0.2s ease; position: relative; } .collapsible-toggler:hover { color: var(--body-font-color); background-color: var(--panel-background); } .collapsible-toggler:focus { outline: 2px solid var(--heading-font-color); outline-offset: 2px; } /\* Dark mode support \*/ :root\[data-theme="dark"\] .collapsible-toggler { color: var(--text); } :root\[data-theme="dark"\] .collapsible-toggler:hover { color: var(--text); background-color: var(--background-subtle); } :root\[data-theme="dark"\] .collapsible-toggler:focus { outline-color: var(--text); } /\* Style the toggle icon \*/ .toggle-icon { font-size: 0.8em; transition: all 0.2s ease; display: inline-block; margin-left: 4px; } .toggle-text { margin-right: 4px; } .collapsible-content.hidden { display: none; } .collapsible-content { margin-top: 0.5em; transition: opacity 0.2s ease; } /\* Add subtle animation when showing content \*/ .collapsible-content:not(.hidden) { animation: slideIn 0.2s ease-out; } @keyframes slideIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } } /\* Additional visual cue - add a subtle border when hovering over collapsible sections \*/ .collapsible-toggler:hover + .collapsible-content, .collapsible-content:hover { border-left: 2px solid var(--panel-border-color); padding-left: 8px; transition: all 0.2s ease; } /\* Add a subtle indicator that content is collapsible \*/ .collapsible-content { position: relative; } .collapsible-content:not(.hidden)::before { content: ''; position: absolute; left: -8px; top: 0; bottom: 0; width: 2px; background: linear-gradient(to bottom, var(--panel-border-color), transparent); border-radius: 1px; } /\* Add visual feedback on hover \*/ .collapsible-toggler:hover::after { color: var(--body-font-color); transform: scale(1.1); } /\* Dark mode support for visual elements \*/ :root\[data-theme="dark"\] .collapsible-toggler:hover + .collapsible-content, :root\[data-theme="dark"\] .collapsible-content:hover { border-left-color: var(--border); } :root\[data-theme="dark"\] .collapsible-content:not(.hidden)::before { background: linear-gradient(to bottom, var(--border), transparent); } :root\[data-theme="dark"\] .collapsible-toggler:hover::after { color: var(--text); }

Alternatively, you can add the Develocity Python agent directly in your `requirements.txt` file (or equivalent):

**requirements.txt:**

```
https://develocity-python-pkgs.gradle.com/develocity_agent-0.10-py3-none-any.whl
```

> [!TIP]
> To instrument Poetry runs, you can add the Develocity Python agent to Poetry’s own environment with the following command: Note that this won’t instrument the tools executed by Poetry, only the Poetry commands themselves. To instrument the tools executed by Poetry, install the Develocity Python agent in your requirements.txt file (or equivalent).

> [!TIP]
> Python support needs to be enabled in your license to use the Develocity Python agent. Reach out to us to enable Python support.

After installing the agent, you can use it in your Python runs.

Publishing a Build Scan to [Develocity at gradle.com](https://gradle.com/scans/gradle/) isn’t supported. The minimum configuration required is the Develocity server URL.

The agent is configured through a file named `.develocity.py` which should be located at root of your Python project. The configuration file must define a function called `develocity_configuration` which takes as parameter the current configuration and returns the updated configuration. Following is an example of minimal configuration:

**.develocity.py:**

```
def develocity_configuration(configuration):
    configuration.develocity_url = 'https://develocity.example.com'
    return configuration
```

<a id="authenticating-with-develocity"></a>

### Authenticating With Develocity

Develocity installations may be configured to require Build Scan publishing to be authenticated. Additionally, installations may be configured to only allow certain users to publish Build Scans.

> [!WARNING]
> Develocity access keys should be treated with the same secrecy as passwords. They’re used to authorize access to Develocity from a build.

<a id="access-key-configuration"></a>

#### Access Key Configuration

<a id="creating-access-keys"></a>

##### Creating Access Keys

To create a new access key, sign in to Develocity and access “My settings” via the user menu at the top right of the page. From there, use the “Access keys” section to generate an access key.

> [!NOTE]
> Develocity server 2025.1+ supports access key expiration.

> [!WARNING]
> After upgrading to Develocity server 2025.1+ all existing access keys will be updated to have an expiration date.

The access key may be specified in the `.develocity.py` configuration file or via the `DEVELOCITY_ACCESS_KEY` environment variable.

<a id="configuring-through-develocity-py"></a>

###### Configuring Through `.develocity.py`

The access key may be specified in the `.develocity.py` configuration file. Below is an example of configuration. Refer to the [Configuration reference](#configuration-reference) for more information.

**.develocity.py:**

```
from develocity.config import from_properties_file, in_gradle_user_home

def develocity_configuration(config):
    config.develocity_url = 'https://develocity.example.com'
    config.access_key = from_properties_file(in_gradle_user_home())
    return config
```

<a id="configuring-through-environment-variables"></a>

###### Configuring Through Environment Variables

The environment variable value format is `«server host name»=«access key»`.

```shell
export DEVELOCITY_ACCESS_KEY=develocity.example.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq && \
  pip list
```

The server host name is specified in order to prevent the access key being transmitted to a different server than intended. In the rare case that you require access keys for multiple servers, you can specify multiple entries separated by semicolons.

```shell
export DEVELOCITY_ACCESS_KEY=develocity1.example.com=7w5kbqqjea4vonghohvuyra5bnvszop4asbqee3m3sm6dbjdudtq;develocity2.example.com=9y4agfiubqqjea4vonghohvuyra5bnvszop4asbqee3m3sm67w5k && \
  pip list
```

<a id="short-lived-access-tokens"></a>

#### Short-Lived Access Tokens

Develocity access keys are long-lived, creating risks if they’re leaked. To avoid this, users can use short-lived access tokens to authenticate with Develocity. Access tokens can be used wherever an access key would be used. Access tokens are only valid for the Develocity instance that created them.

> [!NOTE]
> Develocity server version 2024.1+ supports access tokens.

> [!WARNING]
> Changing a Develocity instance’s hostname will cause all existing access tokens to become invalid.

To create an access token:

1.  Get an access key or access token for the user you want to create a token for.
    
2.  Decide which permissions the new access token should have.
    
3.  If project-level access control is enabled, decide which projects the new access token should be able to access.
    
4.  Decide how long the new access token should live.
    
5.  Make a POST request to `/api/auth/token`, optionally with the following parameters. The response will be the access token.
    
    1.  A `permissions=` query parameter with the [config values](https://docs.gradle.com/develocity/2026.1/administration/access-control/permissions-and-roles/#permissions) of each permission you want to grant. By default, all permissions for the credential used to authenticate the token request are granted to the created token.
        
    2.  If project-level access control is enabled, a `projectIds=` query parameter with the ID of each project you want to grant access to. By default, all projects for the credential used to authenticate the token request are granted to the created token.
        
    3.  An `expiresInHours=` query parameter with the token’s intended lifetime in hours, with a maximum of 24. The default is two hours, or the remaining lifetime of the credential used to authenticate the request, whichever is smaller.
        
    

The requested permissions and project ids can be specified as comma-separated lists or repeated parameters. For example, `?projectIds=a,b&projectIds=c` is valid and will request projects `a`, `b`, and `c`.

> [!NOTE]
> If project-level access control isn’t enabled, all access tokens will be granted the “Access all data without an associated project” permission even if it isn’t explicitly requested.

If the user creating the token doesn’t have one of the requested permissions or projects, Develocity will respond with a _403 Forbidden_ error. If an access token is used to authenticate the creation request, its permissions and projects will be used for this check instead of the user’s. The request will also error if the requested lifetime would cause the new access token to expire after the one used to authenticate the request. Together, this means you cannot create an access token with more access or a later expiration than the credentials used to authenticate the request.

> [!NOTE]
> See the [Develocity API documentation](https://docs.gradle.com/develocity/2026.1/reference/develocity-api/) for more details on the `/api/auth/token` endpoint.

Here is an example using CURL to create an access token:

```shell
curl -X POST https://develocity.example.com/api/auth/token?permissions=publishScan,writeCache,accessDataWithoutAssociatedProject&projectIds=project-a,project-b&expiresInHours=1 \
  -H "Authorization: Bearer 7asejatf24zun43yshqufp7qi4ovcefxpykbwzqbzilcpwzb52ja"

eyJraWQiOiJ0ZXN0LWtleSIsImFsZyI6IlJTMjU2IiwidHlwIjoiSldUIn0.eyJpc19hbm9ueW1vdXMiOmZhbHNlLCJwZXJtaXNzaW9ucyI6WyJSRUFEX1ZFUlNJT04iLCJFWFBPUlRfREFUQSIsIkFDQ0VTU19EQVRBX1dJVEhPVVRfQVNTT0NJQVRFRF9QUk9KRUNUIl0sInByb2plY3RzIjp7ImEiOjEsImIiOjJ9LCJ1c2VyX2lkIjoic29tZS1pZCIsInVzZXJuYW1lIjoidGVzdCIsImZpcnN0X25hbWUiOiJhIiwibGFzdF9uYW1lIjoidXNlciIsImVtYWlsIjoiYkBncmFkbGUuY29tIiwic3ViIjoidGVzdCIsImV4cCI6NzIwMCwibmJmIjowLCJpYXQiOjAsImF1ZCI6ImV4YW1wbGUuZ3JhZGxlLmNvbSIsImlzcyI6ImV4YW1wbGUuZ3JhZGxlLmNvbSIsInRva2VuX3R5cGUiOiJhY2Nlc3NfdG9rZW4ifQ.H1_NEG1xuleP-WIAY_uvSmdd2o7i_-Ko3qhlo04zvCgrElJe7_F5jNuqsyDfnb5hvKlOe5UKG_7QPTgY9-3pFQ
```

The resulting token would have the following permissions:

*   “Publish Build Scans”
    
*   “Read and write Build Cache data”
    
*   “Access all data without an associated project”
    

And it would have access to these projects:

*   “project-a”
    
*   “project-b”
    

The token would only be usable for one hour.

<a id="gradle-integration"></a>

### Capturing Build Scan Data When Running Python Through Gradle

_(python agent 0.10.0+, Gradle Develocity Plugin 4.0+)_

The Gradle Develocity plugin exposes a convenience API for producing Build Scans when running Python through a Gradle task.

To use it, set up your Python project as if it weren’t running through Gradle, following the [setup instructions](#setup).

Then, use the `develocity.integrations` API to inject the relevant Gradle environment into the task running Python:

<a id="tabs-1"></a>

*   <a id="tabs-1-kotlin"></a>
    
    Kotlin
    
*   <a id="tabs-1-groovy"></a>
    
    Groovy
    

<a id="tabs-1-kotlin--panel"></a>

```kotlin
tasks.register<Exec>("pyTest") { // Replace with your own Python task

    val pythonAgentEnvironment = develocity.integration.python.environment.get()
    environment.putAll(pythonAgentEnvironment)

    commandLine("pytest")
}
```

<a id="tabs-1-groovy--panel"></a>

```groovy
tasks.register('pyTest', Exec) { // Replace with your own Python task

    def pythonAgentEnvironment = develocity.integration.python.environment.get()
    environment.putAll(npmAgentEnvironment)

    commandLine 'pytest'
}
```

The environment will contain at least the following values:

*   `GRADLE_USER_HOME`, with the value of the Gradle user home as seen by that invocation of the Gradle Build Tool. For more information, see the [Gradle documentation](https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home).
    

The environment may contain more variables, used for internal purposes. Variables other than the ones listed above should are subject to change, and shouldn’t be depended on.

> [!TIP]
> Each Python invocation instrumented this way will generate its own Build Scan. Complex projects with complex workflows comprising many Python invocations managed by Gradle can result in many Python Build Scans per Gradle run. If this results in what you believe is unnecessary noise, consider only using this feature on tasks that you deem relevant.

<a id="projects-using-pygradle"></a>

#### Projects Using pygradle

In projects using [pygradle](https://github.com/linkedin/pygradle), you might want to configure this integration globally. To do that, modify pygradle’s `python` code block in your build root, as follows:

**build.gradle.kts:**

```
python {
  pythonEnvironment = develocity.integration.python.environment.get()
}
```

<a id="using-build-scans"></a>

## Using Build Scans

Build Scans are a record of what happened during a build, captured and visualized by Develocity.

Build Scans are an important tool for developing and maintaining Python projects. They provide insights into exactly what your builds are doing, helping you identify problems with the build environment, performance, and more. They can also help you understand and improve the build more generally, and make collaborating with others easier.

<a id="img-build-scans"></a>

![Build Scans Can Be Published to Develocity](https://docs.gradle.com/develocity/python/0.10/python-agent/../_images/build-scan-service-overview.svg)

Build Scans Can Be Published to Develocity

<a id="configuring-project-identifier"></a>

### Configuring Project Identifier

The documentation at the [Project-level access control](https://docs.gradle.com/develocity/2026.1/administration/access-control/project-level-access-control/) provides detailed information regarding project-level access control.

<a id="configuring-through-develocity-py-2"></a>

#### Configuring Through `.develocity.py`

The Develocity configuration object contains an attribute named `project_id`, a string, which is used to store the project identifier.

**.develocity.py:**

```
def develocity_configuration(current):
    current.project_id = "myProject"
    return current
```

<a id="capturing-scripts-output"></a>

### Capturing Scripts Output

The output generated during the Python run, excluding the output produced by forked processes, is captured and displayed in Build Scans.

<a id="extending-build-scans"></a>

### Extending Build Scans

You can include extra custom information in your Build Scans through tags, links, and values. This is a powerful mechanism for capturing and sharing information important to your build and development process.

This information can be anything you like. You can tag all builds run by your continuous integration tool with a `CI` tag. You can capture the name of the environment that the build published to as a value. You can link to the source revision for the build in an online tool such as GitHub. The possibilities are endless.

You can see how the custom data appears in the following figures:

<a id="img-scan-with-custom-data"></a>

![Example of a Build Scan Overview](https://docs.gradle.com/develocity/python/0.10/python-agent/../_images/scan-with-custom-data.png)

Example of a Build Scan Overview

Develocity allows listing and searching across all the Build Scans in the system. You can find and filter Build Scans by tags and custom values, in addition to project name, outcome and other properties. In the following figure, for example, we’re filtering for all Python Build Scans that have the tag "CI" and a git branch name of "release":

<a id="img-filtered-list-view"></a>

![A Filtered List of Build Scans in Develocity](https://docs.gradle.com/develocity/python/0.10/python-agent/../_images/build-scan-filtered-list.png)

A Filtered List of Build Scans in Develocity

<a id="adding-tags"></a>

#### Adding Tags

Tags are typically used to indicate the type or class of a build, or a key characteristic. They’re prominent in the user interface and quickly inform a user about the nature of a build. A build can have zero or more tags.

Tags can be added to a Build Scan by configuring them in `.develocity.py` or by setting environment variables.

Note that the order in which you declare the tags doesn’t affect the Build Scan view. They’re displayed in alphabetical order, with any all-caps labels displayed before the rest.

<a id="configuring-through-develocity-py-3"></a>

##### Configuring Through `.develocity.py`

The Develocity configuration object contains an attribute named `tags`, a set of strings, which contains the tags to add to the Build Scan.

**.develocity.py:**

```
def develocity_configuration(configuration):
    configuration.tags.add('CI')
    return configuration
```

<a id="configuring-through-environment-variables-2"></a>

##### Configuring Through Environment Variables

To add a tag, add an environment variable prefixed with `DEVELOCITY_TAG_` and assign the string `true` to it.

**Add the DEVELOCITY_TAG_<tag> environment variable:**

```
export DEVELOCITY_TAG_CI=true
```

You can see the effect of a custom tag in [figure 2](#img-scan-with-custom-data).

> [!NOTE]
> The Develocity Python agent imposes limits on captured tags: Maximum tag count: 50 Maximum tag length: 200 characters

<a id="adding-links"></a>

#### Adding Links

Builds rarely live in isolation. Where does the project source live? Is there online documentation for the project? Where can you find the project’s issue tracker? If these exist and have a URL, you can add them to the Build Scan.

Tags can be added to a Build Scan by configuring them in `.develocity.py` or by setting environment variables.

<a id="configuring-through-develocity-py-4"></a>

##### Configuring Through `.develocity.py`

The Develocity configuration object contains an attribute named `links`, a dictionary of strings to strings, which contains the links to add to the Build Scan.

**.develocity.py:**

```
def develocity_configuration(configuration):
    configuration.links['VCS'] = 'https://github.com/myorg/my-super-project/tree/my-new-feature'
    return configuration
```

<a id="configuring-through-environment-variables-3"></a>

##### Configuring Through Environment Variables

To add a link, add an environment variable prefixed with `DEVELOCITY_LINK_`. What comes after the prefix will be the name associated with the link, while the value of the environment variable will determine the URL it points to.

**Add the DEVELOCITY_LINK_<name>=<URL> environment variable:**

```
export DEVELOCITY_LINK_VCS=https://github.com/myorg/my-super-project/tree/my-new-feature
```

The <name> is a string identifier that you choose, and that means something to you.

You can see the effect of a custom link in [figure 2](#img-scan-with-custom-data), which shows how a label for a given VCS (here GitHub) becomes a hyperlink that anyone viewing the Build Scan can follow.

> [!NOTE]
> The Develocity Python agent imposes limits on captured links: The link must use the http, https, or mailto scheme Maximum link count: 20 Maximum link label length: 100 characters Maximum link URL length: 100,000 characters

<a id="adding-custom-values"></a>

#### Adding Custom Values

Some information just isn’t useful without context. What does "1G" mean? You might guess that it represents 1 gigabyte, but of what? It’s only when you attach the label "Max\_heap\_size\_for\_build" that it makes sense. The same applies to git commit IDs, for example, which could be interpreted as some other checksum without a suitable label.

Custom values are designed for these cases that require context. They’re standard key-value pairs, in which the key is a string label of your choosing and the values are also strings, often evaluated from the build environment.

Custom values can be added to a Build Scan by configuring them in `.develocity.py` or by setting environment variables.

<a id="configuring-through-develocity-py-5"></a>

##### Configuring Through `.develocity.py`

The Develocity configuration object contains an attribute named `custom_values`, a dictionary of strings to strings, which contains the custom values to add to the Build Scan.

**.develocity.py:**

```
def develocity_configuration(configuration):
    configuration.custom_values['CIBuildType'] = 'QA_Build'
    return configuration
```

<a id="configuring-through-environment-variables-4"></a>

##### Configuring Through Environment Variables

To add a custom value, add an environment variable prefixed with `DEVELOCITY_VALUE_`. What comes after the prefix will be the key, while the value of the environment variable will determine what the custom value is.

**Add the DEVELOCITY_VALUE_<name>=<value> environment variable:**

```
export DEVELOCITY_VALUE_CIBuildType=QA_Build
```

As with tags, you can filter Build Scans by custom values in Develocity.

> [!NOTE]
> The Develocity Python agent imposes limits on captured custom values: Maximum custom value count: 1,000 Maximum custom value key length: 1,000 characters Maximum custom value value length: 100,000 characters

<a id="viewing-test-results"></a>

## Viewing Test Results

The Develocity build agent will capture test execution results and publish them to the Develocity server. After your Build Scan® is ready, you can view and analyze the results.

As of Develocity 2025.1, only test results produced with pytest will be collected and displayed in Develocity.

<a id="the-test-overview-page"></a>

### The Test Overview Page

The Test overview page shows a high-level overview of all the tests that were executed as part of the build. The test results are displayed hierarchically: they’re grouped by _process_, _test file_ and finally _test case_. Next to each _process_, _test file_ and _test case_, the outcome and the total time are displayed.

![Example of a Test Overview Page](https://docs.gradle.com/develocity/python/0.10/python-agent/../_images/test-overview.png)

Example of a Test Overview Page

<a id="test-outcomes"></a>

#### Test Outcomes

Every _process_, _test file_ and _test case_ has an outcome. For a _process_, the outcome is either `SUCCESS`, indicating that the _process_ was successful, `FLAKY`, which means that at least one _test file_ was flaky, or `FAILED`, if a _test file_ has failed.

For _test files_, the outcome can be `SUCCESS`, meaning that all executed _test cases_ were successful, `FAILED`, indicating that at least one _test case_ failed, `FLAKY`, meaning that at least one _test case_ was successful only after retry, or `SKIPPED`, meaning that the _test file_ wasn’t executed.

For _test cases_, the outcome can be `SKIPPED`, meaning that the _test case_ wasn’t executed, `SUCCESS`, meaning that the _test case_ was successful, `FAILED` indicating that the _test case_ was failed, or `FLAKY`, meaning that the test case was successful only after retry.

<a id="test-timings"></a>

#### Test Timings

On a _process_, the _total time_ corresponds to the total amount of wall clock time the process took to execute.

On a test file, the _total time_ corresponds to the total amount of wall clock time the test file took to execute.

On a test case, the _total time_ corresponds to the total amount of wall clock time the test case took to execute. Clicking on a _test file_ will open the details for this _test file_, which will display the results of the _test cases_ which ran as part of this _test file_.

<a id="test-file-details-page"></a>

### Test File Details Page

The Test file details page displays the results of the _test cases_ which ran as part of the selected _test file_.

![Example of a Test File Details Page](https://docs.gradle.com/develocity/python/0.10/python-agent/../_images/test-file-details.png)

Example of a Test File Details Page

Errors that may have happened during the setup or cleanup phase of the _test file_ execution are reported in this page.

The file’s overall outcome is broken down into _Test file setup/cleanup_ and _Test execution_ outcomes. _Test file setup/cleanup_ tracks whether a failure occurred in the file’s setup / teardown callbacks, and _Test execution_ whether it was a _test case_ that caused the _test file_ to fail.

At the bottom of the page, the _test cases_ that were executed as part of this _test file_ are listed, along with the time and outcome of each execution. A _test case_ may be run more than once in case of retries.

Clicking on a _test case_ will open the details for this _test case_, which will display the results of the executions of this _test case_.

<a id="test-case-details-page"></a>

### Test Case Details Page

The Test case details page displays the results of the executions of the selected _test case_.

![Example of a Test Case Details Page](https://docs.gradle.com/develocity/python/0.10/python-agent/../_images/test-case-details.png)

Example of a Test Case Details Page

The total time and outcome of each execution of the selected _test case_ are displayed on the page. If an execution failed with an exception, then the exception is displayed under the corresponding execution.

<a id="known-limitations"></a>

### Known Limitations

<a id="test-results-are-only-available-with-pytest"></a>

#### Test Results Are Only Available With pytest

As of Develocity 2025.1, the only test framework for which test results are collected and displayed is pytest.

* * *

If you have any questions or need any assistance contact the Develocity support team or your customer success representative.

<a id="captured-information"></a>

## Appendix A: Captured Information

The Develocity Python agent captures information while the Python interpreter is running and transmits it to Develocity after it has completed.

Most of the information captured can be considered to be build data. This includes the name of the projects in your build and more general environmental information, including your Python version, operating system, hardware, country, timezone and other things of this nature.

Notably, the actual source code being built and the output artifacts _aren’t_ captured. However, error messages emitted by compilers or errors in tests may reveal aspects of the source code.

<a id="listing"></a>

### Listing

The list below details the notable information captured by the Develocity Python agent and transmitted in a Build Scan.

*   Environment
    
    *   Username ([https://docs.python.org/3/library/getpass.html#getpass.getuser](https://docs.python.org/3/library/getpass.html#getpass.getuser))
        
    *   Public hostname ([https://docs.python.org/3/library/socket.html#socket.gethostname](https://docs.python.org/3/library/socket.html#socket.gethostname))
        
    *   Process
        
        *   Python version
            
        *   <a id="toggler-locale-content"></a>Locale ▶
            
            <a id="locale-content"></a>
            
            *   Language
                
            *   Country
                
            *   Variant
                
            *   Timezone
                
            
        
    *   Operating System ([https://docs.python.org/3/library/platform.html#platform.uname](https://docs.python.org/3/library/platform.html#platform.uname))
        
        *   Platform
            
        *   Release
            
        *   Architecture
            
        
    *   Hardware
        
        *   Number of processors ([https://docs.python.org/3/library/os.html#os.cpu\_count](https://docs.python.org/3/library/os.html#os.cpu_count))
            
        
    *   <a id="toggler-git-content"></a>Git ▶ \[<a id="_footnoteref_1"></a>[1](#_footnotedef_1 "View footnote.")\]
        
        <a id="git-content"></a>
        
        *   Git repository path
            
        *   Committer and author email addresses
            
        *   Timestamps of commits
            
        
    
*   Build
    
    *   Requested command
        
    *   Console output (including exception messages and stack traces)
        
    *   <a id="toggler-build-tests-content"></a>Executed tests ▶
        
        <a id="build-tests-content"></a>
        
        *   Test framework (e.g. pytest)
            
        *   Test names (e.g. test file and test case name), outcome and duration
            
        *   Console output
            
        *   Failure error messages and stack traces
            
        
    

<a id="access"></a>

### Access

Build Scans published to a Develocity installation are viewable by all users that can reach the server and have the required roles, should Identity Access Management (IAM) be turned on. Develocity provides a search interface for discovering and finding individual Build Scans.

<a id="configuration-reference"></a>

## Appendix B: Configuration Reference

<a id="configuration-file"></a>

### Configuration File

All aspects of the Develocity Python agent can be configured with a configuration file, named `.develocity.py` and located at the root of your Python project. The configuration file must define a function called `develocity_configuration` which takes as parameter the current configuration and returns the updated configuration. The configuration object has the following attributes:

*   `develocity_url` (required): The URL of the Develocity server that the Build Scan will be uploaded to.
    

`access_key` (optional): A function (or another callable object) that takes the hostname of the Develocity server as an argument and returns the access key to use for that server, if available. If you need to read the access keys from a [properties file and/or retrieve them from the same location where the Develocity Gradle plugin persists them, consider using one of the provided](https://en.wikipedia.org/wiki/.properties) [helpers](#helpers). The access key can also be specified as a string directly, but this is deprecated and, anyway, not recommended since the configuration file is meant to be committed to a VCS.

*   `project_name` (optional): The project name to associate with the Build Scan. If not provided, the project name will be extracted from `pyproject.toml` (if available), or from the name of the directory containing the configuration file (if any), or left blank.
    
*   `custom_values` (optional): A dictionary of string to string mappings that will be added to the Build Scan.
    
*   `tags` (optional): A set of strings that will be added as tags to the Build Scan.
    
*   `links` (optional): A dictionary of string (labels) to string (URLs) mappings that will be added as hyperlinks to the Build Scan.
    
*   `project_id` (optional): The project identifier.
    
*   `allow_insecure_protocol` (optional): A boolean value that indicates whether to allow insecure connections to the Develocity server. This is useful for testing purposes only and shouldn’t be used in production environments. Defaults to `False`.
    

If the configuration file isn’t present and the Develocity server URL isn’t defined through an environment variable either (see below), the agent won’t upload a Build Scan. If the configuration file is present, but doesn’t define a `develocity_configuration` function, or the function is defined but doesn’t have the expected signature, the agent won’t upload a Build Scan.

<a id="helpers"></a>

#### Helpers

The Develocity Python agent provides some helpers to retrieve the access keys from the same location as the Develocity Gradle plugin. These helpers can be imported from the `develocity.config` package.

<a id="from-properties-file"></a>

**from_properties_file** — An implementation of the function expected by the `access_key` configuration property that takes a function that provides a path to a [properties file](https://en.wikipedia.org/wiki/.properties) and returns the value associated with the relevant hostname, if available. The following example shows how to use this helper to retrieve the access keys from a known location:

```python
from develocity.config import from_properties_file
from pathlib import Path
import os

def in_user_home():
    return os.path.join(Path.home(), '.develocity-keys.properties')

def develocity_configuration(config):
    config.develocity_url = 'https://develocity.example.com'
    config.access_key = from_properties_file(in_user_home)
    return config
```

**in_gradle_user_home** — A function that resolves the path to the Develocity Gradle plugin user home directory and returns the path to the access keys file. It can be used in conjunction with the [`fromPropertiesFile`](#from-properties-file) helper to retrieve the access keys from the same location as the Develocity Gradle plugin, as in the following example:

```python
from develocity.config import from_properties_file, in_gradle_user_home

def develocity_configuration(config):
    config.develocity_url = 'https://develocity.example.com'
    config.access_key = from_properties_file(in_gradle_user_home())
    return config
```

<a id="determining-the-project-name"></a>

#### Determining the Project Name

The project name is determined as follows:

*   If a project name is configured in `.develocity.py` under `project_name`, this value is used.
    
*   Otherwise, if a `pyproject.toml` is found in the project directory, the value `project.name` is used if available, otherwise the value `tool.poetry.name` is used.
    
*   If no project name is found in the configuration file or `pyproject.toml`, the name of the directory containing the configuration file is used.
    
*   Otherwise, the project name is left blank and will be displayed as `(N/A)` in Develocity.
    

<a id="environment-variables"></a>

### Environment Variables

Both the Develocity server URL and Develocity access key can be configured through environment variables. When the Develocity server URL, Develocity access key, `allow_insecure_protocol` are configured through both environment variables and the configuration file, the values set through environment variables take precedence.

**DEVELOCITY_ACCESS_KEY** — A semi-colon-separated list of `<host>=<key>`. When connecting to `<host>`, `<key>` will be provided as an authentication token to the configured Develocity server.

**DEVELOCITY_URL** — The URL of the Develocity server that the Build Scan will be uploaded to.

**DEVELOCITY_ALLOW_INSECURE_PROTOCOL** — A boolean value that indicates whether to allow insecure connections to the Develocity server. This is useful for testing purposes only and shouldn’t be used in production environments.

<a id="compatibility-with-python-and-develocity"></a>

## Appendix C: Compatibility With Python and Develocity

Compatibility between versions of Python, Develocity, and the Develocity Python agent can be found in the [compatibility matrix](https://docs.gradle.com/develocity/2026.1/miscellaneous/compatibility/).

<a id="known-issues"></a>

## Appendix D: Known Issues

<a id="windows-compatibility"></a>

### Windows Compatibility

_(python agent <0.9.1)_

Build Scans generated on Windows machines aren’t supported. While a Build Scan can be produced on a Windows machine, it may be missing information and console output may not look as expected.

The workaround is to use the Develocity Python agent `0.9.1` or later.

<a id="python-tools-supported-by-develocity"></a>

## Appendix E: Python Tools Supported by Develocity

The Develocity Python agent supports the following tools:

*   autopep8
    
*   django
    
*   flake8
    
*   isort
    
*   mypy
    
*   pip
    
*   poetry
    
*   prospector
    
*   pycodestyle
    
*   pylint
    
*   pyright
    
*   pytest
    
*   pytype
    
*   venv
    

<a id="verifying-the-signature-of-the-agent-package"></a>

## Appendix F: Verifying the Signature of the Agent Package

The agent package is published alongside its signature. The public key is published to [https://keys.openpgp.org](https://keys.openpgp.org). You can verify the signature as follows:

```shell
curl -OL https://develocity-python-pkgs.gradle.com/develocity_agent-0.10-py3-none-any.whl && \
  curl -OL https://develocity-python-pkgs.gradle.com/develocity_agent-0.10-py3-none-any.whl.asc && \
  gpg --keyserver keys.openpgp.org --recv-key  7B79ADD11F8A779FE90FD3D0893A028475557671 && \
  gpg --verify develocity_agent-0.10-py3-none-any.whl.asc develocity_agent-0.10-py3-none-any.whl
```

The output of the last command should look similar to the following:

**Output:**

```
gpg: Signature made Thu Sep 28 16:17:46 2023 CEST
gpg:                using RSA key 893A028475557671
gpg: Good signature from "Gradle Inc. <info@gradle.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7B79 ADD1 1F8A 779F E90F  D3D0 893A 0284 7555 7671
```

This verifies that the artifact was signed with the private key that corresponds to the imported public key. The warning is emitted because you haven’t explicitly trusted the imported key (therefore `[unknown]`). One way of establishing trust is to verify the fingerprint over a secure channel. Contact technical support should you want to do so.

<a id="footnotes"></a>

* * *

<a id="_footnotedef_1"></a>[1](#_footnoteref_1). Not captured when publishing to [our free Build Scan hosting service](https://gradle.com/scans/gradle/) or any of our [sponsored open source instances](https://gradle.com/oss-sponsored-by-develocity/).