Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/workflows/test_tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,13 @@ jobs:
posargs: 'PyPy'
pytest: false

test_supported_pythons:
test_python_version_glob:
uses: ./.github/workflows/tox.yml
with:
envs: |
- linux: pep8
- linux: py3
fill: true
fill_platforms: linux,macos
- linux: py3*
- macos: py31*
pytest: false

test_libraries:
Expand Down
39 changes: 11 additions & 28 deletions .github/workflows/tox.yml

Large diffs are not rendered by default.

72 changes: 7 additions & 65 deletions docs/source/tox.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ environment.
If the Python version includes a ``t`` suffix, such as ``py313t``, then
a free-threaded Python interpreter will be used.

Additionally, the Python version can include glob syntax (i.e. `py3*`) to expand into all supported versions of Python (respecting ``project.requires-python`` in ``pyproject.toml`` as conforming with PEP621).

For example:
- ``py*`` for all supported versions of Python (from https://endoflife.date)
- ``py3*`` for all supported minor versions of Python 3
- ``py31*`` for all supported versions of Python between ``3.10`` and ``3.20``

libraries
^^^^^^^^^

Expand Down Expand Up @@ -544,71 +551,6 @@ same repository, or when using a non-standard project layout.
envs: |
- linux: py312

fill
^^^^

Automatically add tox environments for each Python version currently supported
by your package. The supported versions are determined by reading the
``requires-python`` field from your package's ``pyproject.toml`` file
(conforming to PEP 621) and cross-referencing with currently maintained Python
versions from https://endoflife.date.

Default is ``false``.

.. code:: yaml

uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2
with:
fill: true
envs: |
- linux: pep8
pytest: false

In the above example, if your package's ``pyproject.toml`` specifies
``requires-python = ">=3.10"``, and Python 3.10, 3.11, 3.12, and 3.13 are
currently maintained, the workflow will automatically add ``py310``, ``py311``,
``py312``, and ``py313`` environments on Linux in addition to the ``pep8``
environment.

fill_platforms
^^^^^^^^^^^^^^

Platforms to use when generating environments with ``fill``. This is a
comma-separated list of platforms. Default is ``linux`` only.

.. code:: yaml

uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2
with:
fill: true
fill_platforms: linux,macos,windows
envs: |
- linux: pep8
pytest: false

This will create tox environments for each supported Python version on all
three platforms.

fill_factors
^^^^^^^^^^^^

Tox factors to add to the automatically generated environments from ``fill``.
This is a comma-separated list of factors. Default is none.

.. code:: yaml

uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2
with:
fill: true
fill_factors: test,cov
envs: |
- linux: pep8
pytest: false

If your package supports Python 3.11 and 3.12, this will generate environments
like ``py311-test-cov`` and ``py312-test-cov`` instead of just ``py311`` and
``py312``.

Secrets
~~~~~~~

Expand Down
123 changes: 0 additions & 123 deletions tools/supported_pythons.py

This file was deleted.

98 changes: 87 additions & 11 deletions tools/tox_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@
# requires-python = "==3.12"
# dependencies = [
# "click==8.2.1",
# "packaging==25.0",
# "pyyaml==6.0.2",
# "requests==2.32.5",
# "tomli==2.4.0",
# ]
# ///
import json
import os
import re
import warnings
from copy import copy

import click
import yaml
from packaging.version import Version


@click.command()
Expand All @@ -36,6 +41,7 @@
@click.option("--runs-on", default="")
@click.option("--default-python", default="")
@click.option("--timeout-minutes", default="360")
@click.option("--supported-pythons", default='["3"]')
def load_tox_targets(
envs,
libraries,
Expand All @@ -58,8 +64,15 @@ def load_tox_targets(
runs_on,
default_python,
timeout_minutes,
supported_pythons,
):
"""Script to load tox targets for GitHub Actions workflow."""

if not supported_pythons:
supported_pythons = ['3']
elif isinstance(supported_pythons, str):
supported_pythons = json.loads(supported_pythons)

# Load envs config
envs = yaml.load(envs.replace("\\n", "\n"), Loader=yaml.BaseLoader)
print(json.dumps(envs, indent=2))
Expand Down Expand Up @@ -111,16 +124,30 @@ def load_tox_targets(
# Create matrix
matrix = {"include": []}
for env in envs:
matrix["include"].append(
get_matrix_item(
env,
global_libraries=global_libraries,
global_string_parameters=string_parameters,
runs_on=default_runs_on,
default_python=default_python,
)
matrix_item = get_matrix_item(
env,
global_libraries=global_libraries,
global_string_parameters=string_parameters,
runs_on=default_runs_on,
default_python=default_python,
)

# check if we need to expand python versions from a glob (i.e. py*, py3*, py31*, etc.)
toxenv = matrix_item["toxenv"]
if toxenv.startswith("py") and "*" in toxenv.split("-")[0]:
toxenvs = expand_python_versions(toxenv, python_versions=supported_pythons)

for expanded_toxenv, python_version in toxenvs:
expanded_matrix_item = copy(matrix_item)
expanded_matrix_item["toxenv"] = expanded_toxenv
expanded_matrix_item["name"] = expanded_matrix_item["name"].replace(
toxenv, expanded_toxenv
)
expanded_matrix_item["python_version"] = python_version
matrix["include"].append(expanded_matrix_item)
else:
matrix["include"].append(matrix_item)

# Output matrix
print(json.dumps(matrix, indent=2))
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
Expand Down Expand Up @@ -188,9 +215,7 @@ def get_matrix_item(env, global_libraries, global_string_parameters, runs_on, de
# Note that we don't include --cov here as if it's provided to pytest twice it breaks cov reporting.
# Lots of users of this specify --cov in their tox.ini so it's been removed for backwards compatibility.
# https://github.com/OpenAstronomy/github-actions-workflows/issues/383
item["pytest_flag"] += (
rf"--cov-report=xml:${{GITHUB_WORKSPACE}}{sep}coverage.xml "
)
item["pytest_flag"] += rf"--cov-report=xml:${{GITHUB_WORKSPACE}}{sep}coverage.xml "

if item["pytest-results-summary"] == "true":
item["pytest_flag"] += rf"--junitxml ${{GITHUB_WORKSPACE}}{sep}results.xml "
Expand Down Expand Up @@ -225,5 +250,56 @@ def get_matrix_item(env, global_libraries, global_string_parameters, runs_on, de
return item


def expand_python_versions(toxenv: str, python_versions: list[Version | str]) -> list[(str, str)]:
"""
expand `py3*` into `py311`, `py312`, `py313`, etc. based on currently-supported Python versions

:param version_glob: can be `py*`, `py3*`, `py30*`, `py31*` etc.
"""

python_versions = [Version(version) for version in python_versions]

toxenv_factors = toxenv.split("-")
py_version_glob = toxenv_factors[0]
if not py_version_glob.startswith("py"):
raise ValueError(
f'input "{py_version_glob}" is not a Python version Tox factor (must start with `py`)'
)

if "*" not in py_version_glob:
return [py_version_glob]

if not py_version_glob.endswith("*"):
raise NotImplementedError(
"Python version glob must end with a `*`; suffixes such as `t` are not yet supported"
)

major_version = py_version_glob[2]
if major_version != "*":
python_versions = [
python_version
for python_version in python_versions
if python_version.major == int(major_version)
]

minor_version = py_version_glob[3:]
if minor_version_specifier := minor_version.split("*")[0] != "*":
minor_version_base = int(minor_version_specifier) * 10
python_versions = [
python_version
for python_version in python_versions
if minor_version_base <= python_version.minor < minor_version_base + 10
]

return [
(
f"py{python_version.major}{python_version.minor}"
+ (f"-{'-'.join(toxenv_factors[1:])}" if len(toxenv_factors) > 1 else ""),
str(python_version),
)
for python_version in python_versions
]


if __name__ == "__main__":
load_tox_targets()
1 change: 0 additions & 1 deletion update_scripts_in_yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,3 @@ def base64_encode_into(script, yml_file, env_var):
base64_encode_into('set_env.py', 'tox.yml', 'SET_ENV_SCRIPT')
base64_encode_into('set_env.py', 'publish.yml', 'SET_ENV_SCRIPT')
base64_encode_into('set_env.py', 'publish_pure_python.yml', 'SET_ENV_SCRIPT')
base64_encode_into('supported_pythons.py', 'tox.yml', 'SUPPORTED_PYTHONS_SCRIPT')
Loading