Skip to content

.github: Ensure python3 -m pip is used in GitHub Actions#30657

Closed
cclauss wants to merge 2 commits intoArduPilot:masterfrom
cclauss:pre-commit-enforce-python3--m-pip
Closed

.github: Ensure python3 -m pip is used in GitHub Actions#30657
cclauss wants to merge 2 commits intoArduPilot:masterfrom
cclauss:pre-commit-enforce-python3--m-pip

Conversation

@cclauss
Copy link
Contributor

@cclauss cclauss commented Jul 15, 2025

Ensure that "python3 -m pip install " is used consistantly in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line. Raise an error if any non-compliant files are found.

% pytest # Improved format

Use 'python3 -m pip install' instead of 'pip install' (3 errors).
.github/workflows/test.yml:29: pip install -r requirements.txt
.github/workflows/test.yml:91: pip install lxml
.github/workflows/pylint.yml:30: pip install requests 'pylint<=3.3.7'

How was this tested?

  1. localhost testing
  2. This is a test that contains tests.
  3. Create test_python3_minus_m_pip_in_gh_actions.py pymavlink#1095

This will run automatically once one of these pull requests has been merged.

@peterbarker
Copy link
Contributor

Errr... does this introduce a top-level "test" directory? That doesn't seem right

@cclauss cclauss force-pushed the pre-commit-enforce-python3--m-pip branch from b6596d9 to 89e9101 Compare July 15, 2025 05:56
@khancyr
Copy link
Contributor

khancyr commented Jul 15, 2025

why not a simple grep script like :

#!/bin/bash

# Exit if any command fails
set -e

# Fail if `pip` is used instead of `python3 -m pip`
bad_files=$(grep -rEn '(^|\s)(pip3?|pip3?)\s+(install|list|uninstall|freeze)' --include='*.yml' --include='*.py' --include='*.sh' . | grep -v 'python3\s\+-m\s\+pip')

if [[ -n "$bad_files" ]]; then
    echo "❌ Detected direct use of 'pip' instead of 'python3 -m pip':"
    echo "$bad_files"
    echo "Please use 'python3 -m pip' instead of 'pip'."
    exit 1
fi

exit 0

And register is as part of pre-commit :

- id: check-pip-usage
  name: Check for pip usage
  entry: .pre-commit-hooks/check-pip-usage.sh
  language: script
  files: \.(py|sh|yml)$

@amilcarlucas
Copy link
Contributor

I like the idea of adding pytests to the ardupilot codebase. :)
@peterbarker what directory do you suggest then ?

@cclauss
Copy link
Contributor Author

cclauss commented Jul 15, 2025

I moved this script from test/ to the pre-existing tests/ to track with pytest discovery rules and #30598.

I distrust bash scripts (especially with regex) as I find it difficult to see bugs in them and write tests. This is particularly true when scripts must run across multiple operating systems. Would the script above only process GitHub Actions, and would it pass the four tests of test_process_file()?

The idea of adding to pre-commit is a good one, but let's land it first and make sure everyone is happy with it.

cclauss added a commit to cclauss/pymavlink that referenced this pull request Jul 16, 2025
Like:
* ArduPilot/ardupilot#30657

Ensure that "python3 -m pip install " is used consistently in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line.  Raise an error if any non-compliant files are found.

### How was this tested?
1. localhost testing
2. This is a test that contains tests.
cclauss added a commit to cclauss/pymavlink that referenced this pull request Jul 16, 2025
Like:
* ArduPilot/ardupilot#30657

Ensure that "python3 -m pip install " is used consistently in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line.  Raise an error if any non-compliant files are found.

### How was this tested?
1. localhost testing
2. This is a test that contains tests.
cclauss added a commit to cclauss/pymavlink that referenced this pull request Jul 16, 2025
Like:
* ArduPilot/ardupilot#30657

Ensure that "python3 -m pip install " is used consistently in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line.  Raise an error if any non-compliant files are found.

### How was this tested?
1. localhost testing
2. This is a test that contains tests.
cclauss added a commit to cclauss/pymavlink that referenced this pull request Jul 16, 2025
Like:
* ArduPilot/ardupilot#30657

Ensure that "python3 -m pip install " is used consistently in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line.  Raise an error if any non-compliant files are found.

### How was this tested?
1. localhost testing
2. This is a test that contains tests.
cclauss added a commit to cclauss/pymavlink that referenced this pull request Jul 16, 2025
Like:
* ArduPilot/ardupilot#30657

Ensure that "python3 -m pip install " is used consistently in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line.  Raise an error if any non-compliant files are found.

### How was this tested?
1. localhost testing
2. This is a test that contains tests.
cclauss added a commit to cclauss/pymavlink that referenced this pull request Jul 16, 2025
Like:
* ArduPilot/ardupilot#30657

Ensure that "python3 -m pip install " is used consistently in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line.  Raise an error if any non-compliant files are found.

### How was this tested?
1. localhost testing
2. This is a test that contains tests.
cclauss added a commit to cclauss/pymavlink that referenced this pull request Jul 16, 2025
Like:
* ArduPilot/ardupilot#30657

Ensure that "python3 -m pip install " is used consistently in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain " pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name, line number, and the line.  Raise an error if any non-compliant files are found.

### How was this tested?
1. localhost testing
2. This is a test that contains tests.
@cclauss cclauss force-pushed the pre-commit-enforce-python3--m-pip branch from 89e9101 to 57baed8 Compare July 16, 2025 05:18
Ensure that "python3 -m pip install " is used consistantly in GitHub Actions.

Find all .yml files in the .github/workflows directory and find all lines that contain
" pip install " and ensure that they also contain "python3 -m pip install ".

If there is any line that has the first but not the second, then print the file name,
line number, and the line.  Raise an error if any non-compliant files are found.
@cclauss cclauss force-pushed the pre-commit-enforce-python3--m-pip branch from 57baed8 to bf018dd Compare July 16, 2025 05:22
Copy link
Contributor

@peterbarker peterbarker left a comment

Choose a reason for hiding this comment

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

Again, not really comfortable using the top-level tests directory for this content.

As covered on the DevCall, we were a bit surprised it existed, and its current use is for the gtest suite which is a rather different animal to these python tests.

Consider Tools/scripts/python_tests instead.

line number, and the line. Raise an error if any non-compliant files are found.
"""

from __future__ import annotations
Copy link
Contributor

Choose a reason for hiding this comment

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

What versions of Python will this work back to?

Copy link
Contributor

Choose a reason for hiding this comment

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

... actually, it would be nice to have a linting step which picked up any use of Python features not available in whatever our minimum version of Python is

Copy link
Contributor Author

Choose a reason for hiding this comment

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

https://docs.python.org/3/library/__future__.html#module-contents says Python 3.7b1

ruff check uses target-python to apply or relax lint rules.

"python3 -m pip install ".
"""
with file_path.open() as in_file:
for line_number, line in enumerate(in_file, start=1):
Copy link
Contributor

Choose a reason for hiding this comment

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

Why skip the first line?

Copy link
Contributor Author

@cclauss cclauss Jul 18, 2025

Choose a reason for hiding this comment

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

start=1 does not skip line 1. It starts line_number at 1 so that the line_number of the first line is 1, not 0.



def test_pip_install_without_python3_minus_m_prefix() -> None:
workflows_dir = Path(".github/workflows")
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to apply this across the entire codebase, actually... don't want them slipping into environment install files, for example.

Copy link
Contributor Author

@cclauss cclauss Jul 18, 2025

Choose a reason for hiding this comment

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

I am becoming less and less interested in this rule because it actively encourages people to be lazy about using virtual environments and to pollute their system Pythons with dependencies that might span multiple projects. This can lead to dependency conflicts that are difficult to debug and crashes that cannot be replicated.

Applying this rule inside GHAs bugs me less because they are already virtual (Docker) environments, but doing this on developers' machines seems irresponsible because it will make system modifications that will be extremely difficult for them to undo.

Comment on lines +38 to +41
errors.extend(
f"{yml_file}:{line_number}: {line}"
for line_number, line in process_file(yml_file)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's simplify this - just because you can doesn't mean you should. The indenting here makes this particularly bad as a construct!

Suggested change
errors.extend(
f"{yml_file}:{line_number}: {line}"
for line_number, line in process_file(yml_file)
)
for line_number, line in process_file(yml_file):
errors.append(f"{yml_file}:{line_number}: {line}")

Copy link
Contributor Author

@cclauss cclauss Jul 18, 2025

Choose a reason for hiding this comment

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

The proposed solution reduces function call overhead from once per failing line to once per failing file.

Runtime performance is an important consideration for code that we may choose to add as a pre-commit hook.

The indenting is done automatically, not manually.

% ruff rule FURB113 # repeated-append
[ ... ]
Why is this bad?

Consecutive calls to append can be less efficient than batching them into a single extend. Each append resizes the list individually, whereas an extend can resize the list once for all elements.
[ ... ]

@cclauss
Copy link
Contributor Author

cclauss commented Jul 18, 2025

Please re-raise the issue of using /tests on #30361 and/or #30666 which is where that discussion belongs.

@cclauss cclauss requested a review from peterbarker July 18, 2025 07:08
Co-authored-by: Peter Barker <pb-gh@barker.dropbear.id.au>
@cclauss
Copy link
Contributor Author

cclauss commented Jul 25, 2025

Closing in favor of the bash script approach recommended at #30657 (comment)

@cclauss cclauss closed this Jul 25, 2025
@cclauss cclauss deleted the pre-commit-enforce-python3--m-pip branch July 25, 2025 12:15
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.

4 participants