Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
kurtmckee committed Oct 11, 2023
0 parents commit dab4a45
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 0 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: "Test"

on:
push:
branches:
- "main"

jobs:
test-linux:
name: "Test on ${{ matrix.config.os-name }}"
runs-on: "${{ matrix.config.runner }}"
strategy:
matrix:
config:
- os-name: "Linux"
runner: "ubuntu-latest"
test-label: "ci-test-linux"
- os-name: "macOS"
runner: "macos-latest"
test-label: "ci-test-macos"
- os-name: "Windows"
runner: "windows-latest"
test-label: "ci-test-windows"
fail-fast: false

steps:
- name: "Use it!"
id: "finder"
uses: "kurtmckee/setup-python-version-detector@main"

- name: "Print it!"
id: linux
if: "runner.os != 'windows'"
shell: "bash"
run: |
echo "{{ steps.finder.outputs.python-identifiers }}"
7 changes: 7 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Identify Python interpreters
############################

*Robust cache-busting based on Python implementations, architectures, and versions.*

----

40 changes: 40 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: "Identify installed Python interpreters"
description: |
Find installed Python interpreters and output them as "python-identifiers".
This can be useful for cache busting of tox, virtual environments,
and other items that are sensitive to changes in Python minor versions.
Only Python interpreters named "python" in the $PATH will be found.
outputs:
python-paths:
description: |
A string of sorted Python identifiers.
In most cases the identifiers will be paths,
but for system Pythons, 'sysconfig.get_config_var("EXT_SUFFIX")' will be included.
The string will be separated by OS-specific PATH separators;
":" is used on Linux and macOS, and ";" is used on Windows.
value: "${{ steps.final-step.outputs.python-identifiers }}"

runs:
using: "composite"
steps:
- name: "Find Pythons on Linux / macOS"
id: "linux"
if: "runner.os != 'windows'"
shell: "bash"
run: 'bash ./finder.sh > "$GITHUB_OUTPUT"'

- name: "Find Pythons on Windows"
id: "windows"
if: "runner.os == 'windows'"
shell: "powershell"
run: '& .\finder.ps1 > "$env:GITHUB_OUTPUT"'

- name: "Output"
id: "final-step"
shell: "bash"
run: |
echo "${{ steps.linux.outputs.python-identifiers }}${{ steps.windows.outputs.python-identifiers }}" > $GITHUB_OUTPUT
23 changes: 23 additions & 0 deletions finder.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Search paths in $PATH for Python interpreters.
$all_paths = $env:PATH -split ";"

# Find Python interpreters.
$paths = @()
foreach ($path in $all_paths) {
# Only consider paths in RUNNER_TOOL_CACHE.
if ($path.StartsWith($env:RUNNER_TOOL_CACHE)) {
if (Test-Path "$path\python.exe") {
$paths += $path
}
}
}

# Sort the paths, ensure each path is unique, and create the output result.
$result = (
$paths `
| Sort-Object `
| Get-Unique
) -join ";"

# Output path information.
Write-Output "python-identifiers=$result"
50 changes: 50 additions & 0 deletions finder.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# XXX: Display where the script is running from, and anything else in there.
echo "$0" 1>&2
ls -l "$(dirname "$0")" 1>&2

# Search paths in $PATH for Python interpreters.
IFS=: read -r -a all_paths <<< "$PATH"

# Find Python interpreters.
paths=()
for path in "${all_paths[@]}"; do
# Interpreters in RUNNER_TOOL_CACHE have directory names that include:
#
# * The implementation (like "Python" or "PyPy")
# * A version (like "3.10.12")
# * The architecture (like "x64")
#
# In such cases, the path can be used as the identifier.
if [[ "${path/#${RUNNER_TOOL_CACHE}/}" != "${path}" ]]; then
# Check for bin/python first;
# this results in duplicate paths which are later removed.
if [[ -x "${path}/bin/python" ]]; then
paths+=("${path}/bin")
elif [[ -x "${path}/python" ]]; then
paths+=("${path}")
fi
else
# System Pythons (e.g. /usr/bin/python) have nothing unique in the path.
# In such cases, it's necessary to run the executable to get something unique.
if [[ -x "${path}/python" ]]; then
"${path}/python" -V 1>&2
code='import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX"))'
paths+=("${path}/python" "$("${path}/python" -c "${code}")")
fi
fi
done

# Sort the paths, ensure each path is unique, and create the output result.
result="$(
echo "${paths[*]}" \
| tr ' ' '\n' \
| sort \
| uniq \
| tr '\n' ':'
)"

# Trim trailing colons.
result="${result%:}"

# Output path information.
echo "python-identifiers=${result}"
35 changes: 35 additions & 0 deletions identify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import print_function

import platform
import sys


def main():
python_version = platform.python_version()
implementation = platform.python_implementation()
architecture = "/".join(platform.architecture())

# PyPy
if hasattr(sys, "pypy_version_info"):
implementation_version = ".".join(str(v) for v in sys.pypy_version_info[:3])

# CPython
else:
implementation_version, _, _ = sys.version.partition(" ")

# GraalPy
try:
implementation_version = __graalpython__.get_graalvm_version()
except NameError:
pass

output = "{0} [{1} {2}; {3}]".format(
python_version, implementation, implementation_version, architecture
)
print(output)

return 0


if __name__ == "__main__":
sys.exit(main())

0 comments on commit dab4a45

Please sign in to comment.