From 7809457f66b0b6c04a7c030ede51d39e83cd1bcd Mon Sep 17 00:00:00 2001 From: zacharyburnett Date: Mon, 8 Jun 2026 10:14:19 -0400 Subject: [PATCH] allow user to specify list of base Python versions, to bypass retrieving from https://endoflife.date --- .github/workflows/tox.yml | 17 +++++++++------ docs/source/tox.rst | 45 ++++++++++++++++++++++++++++++++++----- tools/tox_matrix.py | 17 +++++++++------ 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index e0e1488d..33b0ec76 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -7,6 +7,11 @@ on: description: Array of tox environments to test required: true type: string + python-versions: + description: use these versions (i.e. `3.11 3.12 3.14`) for Python version glob expansion (`py*`), instead of retrieving active versions from https://endoflife.date + required: false + default: '' + type: string libraries: description: Additional packages to install required: false @@ -133,7 +138,6 @@ on: required: false jobs: - envs: name: Load tox environments runs-on: ubuntu-latest @@ -148,16 +152,16 @@ jobs: persist-credentials: false - run: echo $TOX_MATRIX_SCRIPT | base64 --decode > tox_matrix.py env: - TOX_MATRIX_SCRIPT: IyAvLy8gc2NyaXB0CiMgcmVxdWlyZXMtcHl0aG9uID0gIj09My4xMiIKIyBkZXBlbmRlbmNpZXMgPSBbCiMgICAgICJjbGljaz09OC4yLjEiLAojICAgICAicGFja2FnaW5nPT0yNS4wIiwKIyAgICAgInB5eWFtbD09Ni4wLjIiLAojIF0KIyAvLy8KaW1wb3J0IGpzb24KaW1wb3J0IG9zCmltcG9ydCByZQppbXBvcnQgd2FybmluZ3MKZnJvbSBjb3B5IGltcG9ydCBjb3B5CgppbXBvcnQgY2xpY2sKaW1wb3J0IHlhbWwKZnJvbSBwYWNrYWdpbmcudmVyc2lvbiBpbXBvcnQgVmVyc2lvbgoKCkBjbGljay5jb21tYW5kKCkKQGNsaWNrLm9wdGlvbigiLS1lbnZzIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1saWJyYXJpZXMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXBvc2FyZ3MiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXRveGRlcHMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXRveGFyZ3MiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXB5dGVzdCIsIGRlZmF1bHQ9InRydWUiKQpAY2xpY2sub3B0aW9uKCItLXB5dGVzdC1yZXN1bHRzLXN1bW1hcnkiLCBkZWZhdWx0PSJmYWxzZSIpCkBjbGljay5vcHRpb24oIi0tY292ZXJhZ2UiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWNvbmRhIiwgZGVmYXVsdD0iYXV0byIpCkBjbGljay5vcHRpb24oIi0tc2V0ZW52IiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1kaXNwbGF5IiwgZGVmYXVsdD0iZmFsc2UiKQpAY2xpY2sub3B0aW9uKCItLWNhY2hlLXBhdGgiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWNhY2hlLWtleSIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tY2FjaGUtcmVzdG9yZS1rZXlzIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1wYXRoIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1hcmNoaXZlIiwgZGVmYXVsdD0idHJ1ZSIpCkBjbGljay5vcHRpb24oIi0tYXJ0aWZhY3QtaW5jbHVkZS1oaWRkZW4tZmlsZXMiLCBkZWZhdWx0PSJmYWxzZSIpCkBjbGljay5vcHRpb24oIi0tYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiLCBkZWZhdWx0PSJ3YXJuIikKQGNsaWNrLm9wdGlvbigiLS1ydW5zLW9uIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1kZWZhdWx0LXB5dGhvbiIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tdGltZW91dC1taW51dGVzIiwgZGVmYXVsdD0iMzYwIikKQGNsaWNrLm9wdGlvbigiLS1zdXBwb3J0ZWQtcHl0aG9ucyIsIGRlZmF1bHQ9J1siMyJdJykKZGVmIGxvYWRfdG94X3RhcmdldHMoCiAgICBlbnZzLAogICAgbGlicmFyaWVzLAogICAgcG9zYXJncywKICAgIHRveGRlcHMsCiAgICB0b3hhcmdzLAogICAgcHl0ZXN0LAogICAgcHl0ZXN0X3Jlc3VsdHNfc3VtbWFyeSwKICAgIGNvdmVyYWdlLAogICAgY29uZGEsCiAgICBzZXRlbnYsCiAgICBkaXNwbGF5LAogICAgY2FjaGVfcGF0aCwKICAgIGNhY2hlX2tleSwKICAgIGNhY2hlX3Jlc3RvcmVfa2V5cywKICAgIGFydGlmYWN0X3BhdGgsCiAgICBhcnRpZmFjdF9hcmNoaXZlLAogICAgYXJ0aWZhY3RfaW5jbHVkZV9oaWRkZW5fZmlsZXMsCiAgICBhcnRpZmFjdF9pZl9ub19maWxlc19mb3VuZCwKICAgIHJ1bnNfb24sCiAgICBkZWZhdWx0X3B5dGhvbiwKICAgIHRpbWVvdXRfbWludXRlcywKICAgIHN1cHBvcnRlZF9weXRob25zLAopOgogICAgIiIiU2NyaXB0IHRvIGxvYWQgdG94IHRhcmdldHMgZm9yIEdpdEh1YiBBY3Rpb25zIHdvcmtmbG93LiIiIgoKICAgIGlmIG5vdCBzdXBwb3J0ZWRfcHl0aG9uczoKICAgICAgICBzdXBwb3J0ZWRfcHl0aG9ucyA9IFsnMyddCiAgICBlbGlmIGlzaW5zdGFuY2Uoc3VwcG9ydGVkX3B5dGhvbnMsIHN0cik6CiAgICAgICAgc3VwcG9ydGVkX3B5dGhvbnMgPSBqc29uLmxvYWRzKHN1cHBvcnRlZF9weXRob25zKQoKICAgICMgTG9hZCBlbnZzIGNvbmZpZwogICAgZW52cyA9IHlhbWwubG9hZChlbnZzLnJlcGxhY2UoIlxcbiIsICJcbiIpLCBMb2FkZXI9eWFtbC5CYXNlTG9hZGVyKQogICAgcHJpbnQoanNvbi5kdW1wcyhlbnZzLCBpbmRlbnQ9MikpCgogICAgIyBMb2FkIGdsb2JhbCBsaWJyYXJpZXMgY29uZmlnCiAgICBnbG9iYWxfbGlicmFyaWVzID0gewogICAgICAgICJicmV3IjogW10sCiAgICAgICAgImJyZXctY2FzayI6IFtdLAogICAgICAgICJhcHQiOiBbXSwKICAgICAgICAiY2hvY28iOiBbXSwKICAgIH0KICAgIGxpYnJhcmllcyA9IHlhbWwubG9hZChsaWJyYXJpZXMsIExvYWRlcj15YW1sLkJhc2VMb2FkZXIpCiAgICBpZiBsaWJyYXJpZXMgaXMgbm90IE5vbmU6CiAgICAgICAgZ2xvYmFsX2xpYnJhcmllcy51cGRhdGUobGlicmFyaWVzKQogICAgcHJpbnQoanNvbi5kdW1wcyhnbG9iYWxfbGlicmFyaWVzLCBpbmRlbnQ9MikpCgogICAgIyBEZWZhdWx0IGltYWdlcyB0byB1c2UgZm9yIHJ1bm5lcnMKICAgIGRlZmF1bHRfcnVuc19vbiA9IHsKICAgICAgICAibGludXgiOiAidWJ1bnR1LWxhdGVzdCIsCiAgICAgICAgIm1hY29zIjogIm1hY29zLWxhdGVzdCIsCiAgICAgICAgIndpbmRvd3MiOiAid2luZG93cy1sYXRlc3QiLAogICAgfQogICAgY3VzdG9tX3J1bnNfb24gPSB5YW1sLmxvYWQocnVuc19vbiwgTG9hZGVyPXlhbWwuQmFzZUxvYWRlcikKICAgIGlmIGlzaW5zdGFuY2UoY3VzdG9tX3J1bnNfb24sIGRpY3QpOgogICAgICAgIGRlZmF1bHRfcnVuc19vbi51cGRhdGUoY3VzdG9tX3J1bnNfb24pCiAgICBwcmludChqc29uLmR1bXBzKGRlZmF1bHRfcnVuc19vbiwgaW5kZW50PTIpKQoKICAgICMgRGVmYXVsdCBzdHJpbmcgcGFyYW1ldGVycyB3aGljaCBjYW4gYmUgb3ZlcndyaXR0ZW4gYnkgZWFjaCBlbnYKICAgIHN0cmluZ19wYXJhbWV0ZXJzID0gewogICAgICAgICJwb3NhcmdzIjogcG9zYXJncywKICAgICAgICAidG94ZGVwcyI6IHRveGRlcHMsCiAgICAgICAgInRveGFyZ3MiOiB0b3hhcmdzLAogICAgICAgICJweXRlc3QiOiBweXRlc3QsCiAgICAgICAgInB5dGVzdC1yZXN1bHRzLXN1bW1hcnkiOiBweXRlc3RfcmVzdWx0c19zdW1tYXJ5LAogICAgICAgICJjb3ZlcmFnZSI6IGNvdmVyYWdlLAogICAgICAgICJjb25kYSI6IGNvbmRhLAogICAgICAgICJzZXRlbnYiOiBzZXRlbnYsCiAgICAgICAgImRpc3BsYXkiOiBkaXNwbGF5LAogICAgICAgICJjYWNoZS1wYXRoIjogY2FjaGVfcGF0aCwKICAgICAgICAiY2FjaGUta2V5IjogY2FjaGVfa2V5LAogICAgICAgICJjYWNoZS1yZXN0b3JlLWtleXMiOiBjYWNoZV9yZXN0b3JlX2tleXMsCiAgICAgICAgImFydGlmYWN0LXBhdGgiOiBhcnRpZmFjdF9wYXRoLAogICAgICAgICJhcnRpZmFjdC1hcmNoaXZlIjogYXJ0aWZhY3RfYXJjaGl2ZSwKICAgICAgICAiYXJ0aWZhY3QtaW5jbHVkZS1oaWRkZW4tZmlsZXMiOiBhcnRpZmFjdF9pbmNsdWRlX2hpZGRlbl9maWxlcywKICAgICAgICAiYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiOiBhcnRpZmFjdF9pZl9ub19maWxlc19mb3VuZCwKICAgICAgICAidGltZW91dC1taW51dGVzIjogdGltZW91dF9taW51dGVzLAogICAgfQoKICAgICMgQ3JlYXRlIG1hdHJpeAogICAgbWF0cml4ID0geyJpbmNsdWRlIjogW119CiAgICBmb3IgZW52IGluIGVudnM6CiAgICAgICAgbWF0cml4X2l0ZW0gPSBnZXRfbWF0cml4X2l0ZW0oCiAgICAgICAgICAgIGVudiwKICAgICAgICAgICAgZ2xvYmFsX2xpYnJhcmllcz1nbG9iYWxfbGlicmFyaWVzLAogICAgICAgICAgICBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnM9c3RyaW5nX3BhcmFtZXRlcnMsCiAgICAgICAgICAgIHJ1bnNfb249ZGVmYXVsdF9ydW5zX29uLAogICAgICAgICAgICBkZWZhdWx0X3B5dGhvbj1kZWZhdWx0X3B5dGhvbiwKICAgICAgICApCgogICAgICAgICMgY2hlY2sgaWYgd2UgbmVlZCB0byBleHBhbmQgcHl0aG9uIHZlcnNpb25zIGZyb20gYSBnbG9iIChpLmUuIHB5KiwgcHkzKiwgcHkzMSosIGV0Yy4pCiAgICAgICAgdG94ZW52ID0gbWF0cml4X2l0ZW1bInRveGVudiJdCiAgICAgICAgaWYgdG94ZW52LnN0YXJ0c3dpdGgoInB5IikgYW5kICIqIiBpbiB0b3hlbnYuc3BsaXQoIi0iKVswXToKICAgICAgICAgICAgdG94ZW52cyA9IGV4cGFuZF9weXRob25fdmVyc2lvbnModG94ZW52LCBweXRob25fdmVyc2lvbnM9c3VwcG9ydGVkX3B5dGhvbnMpCgogICAgICAgICAgICBmb3IgZXhwYW5kZWRfdG94ZW52LCBweXRob25fdmVyc2lvbiBpbiB0b3hlbnZzOgogICAgICAgICAgICAgICAgZXhwYW5kZWRfbWF0cml4X2l0ZW0gPSBjb3B5KG1hdHJpeF9pdGVtKQogICAgICAgICAgICAgICAgZXhwYW5kZWRfbWF0cml4X2l0ZW1bInRveGVudiJdID0gZXhwYW5kZWRfdG94ZW52CiAgICAgICAgICAgICAgICBleHBhbmRlZF9tYXRyaXhfaXRlbVsibmFtZSJdID0gZXhwYW5kZWRfbWF0cml4X2l0ZW1bIm5hbWUiXS5yZXBsYWNlKAogICAgICAgICAgICAgICAgICAgIHRveGVudiwgZXhwYW5kZWRfdG94ZW52CiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBleHBhbmRlZF9tYXRyaXhfaXRlbVsicHl0aG9uX3ZlcnNpb24iXSA9IHB5dGhvbl92ZXJzaW9uCiAgICAgICAgICAgICAgICBtYXRyaXhbImluY2x1ZGUiXS5hcHBlbmQoZXhwYW5kZWRfbWF0cml4X2l0ZW0pCiAgICAgICAgZWxzZToKICAgICAgICAgICAgbWF0cml4WyJpbmNsdWRlIl0uYXBwZW5kKG1hdHJpeF9pdGVtKQoKICAgICMgT3V0cHV0IG1hdHJpeAogICAgcHJpbnQoanNvbi5kdW1wcyhtYXRyaXgsIGluZGVudD0yKSkKICAgIHdpdGggb3Blbihvcy5lbnZpcm9uWyJHSVRIVUJfT1VUUFVUIl0sICJhIikgYXMgZjoKICAgICAgICBmLndyaXRlKGYibWF0cml4PXtqc29uLmR1bXBzKG1hdHJpeCl9XG4iKQoKCmRlZiBnZXRfbWF0cml4X2l0ZW0oZW52LCBnbG9iYWxfbGlicmFyaWVzLCBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnMsIHJ1bnNfb24sIGRlZmF1bHRfcHl0aG9uKToKCiAgICAjIGRlZmluZSBzcGVjIGZvciBlYWNoIG1hdHJpeCBpbmNsdWRlICgrIGdsb2JhbF9zdHJpbmdfcGFyYW1ldGVycykKICAgIGl0ZW0gPSB7CiAgICAgICAgIm9zIjogTm9uZSwKICAgICAgICAidG94ZW52IjogTm9uZSwKICAgICAgICAicHl0aG9uX3ZlcnNpb24iOiBOb25lLAogICAgICAgICJuYW1lIjogTm9uZSwKICAgICAgICAicHl0ZXN0X2ZsYWciOiBOb25lLAogICAgICAgICJsaWJyYXJpZXNfYnJldyI6IE5vbmUsCiAgICAgICAgImxpYnJhcmllc19icmV3X2Nhc2siOiBOb25lLAogICAgICAgICJsaWJyYXJpZXNfYXB0IjogTm9uZSwKICAgICAgICAibGlicmFyaWVzX2Nob2NvIjogTm9uZSwKICAgICAgICAiY2FjaGUtcGF0aCI6IE5vbmUsCiAgICAgICAgImNhY2hlLWtleSI6IE5vbmUsCiAgICAgICAgImNhY2hlLXJlc3RvcmUta2V5cyI6IE5vbmUsCiAgICAgICAgImFydGlmYWN0LW5hbWUiOiBOb25lLAogICAgICAgICJhcnRpZmFjdC1wYXRoIjogTm9uZSwKICAgICAgICAiYXJ0aWZhY3QtYXJjaGl2ZSI6IE5vbmUsCiAgICAgICAgImFydGlmYWN0LWluY2x1ZGUtaGlkZGVuLWZpbGVzIjogTm9uZSwKICAgICAgICAiYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiOiBOb25lLAogICAgICAgICJ0aW1lb3V0LW1pbnV0ZXMiOiBOb25lLAogICAgfQogICAgZm9yIHN0cmluZ19wYXJhbSwgZGVmYXVsdCBpbiBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnMuaXRlbXMoKToKICAgICAgICBlbnZfdmFsdWUgPSBlbnYuZ2V0KHN0cmluZ19wYXJhbSkKICAgICAgICBpdGVtW3N0cmluZ19wYXJhbV0gPSBkZWZhdWx0IGlmIGVudl92YWx1ZSBpcyBOb25lIGVsc2UgZW52X3ZhbHVlCgogICAgIyBzZXQgb3MgYW5kIHRveGVudgogICAgZm9yIGssIHYgaW4gcnVuc19vbi5pdGVtcygpOgogICAgICAgIGlmIGsgaW4gZW52OgogICAgICAgICAgICBwbGF0Zm9ybSA9IGsKICAgICAgICAgICAgaXRlbVsib3MiXSA9IGVudi5nZXQoInJ1bnMtb24iLCB2KQogICAgICAgICAgICBpdGVtWyJ0b3hlbnYiXSA9IGVudltrXQogICAgYXNzZXJ0IGl0ZW1bIm9zIl0gaXMgbm90IE5vbmUgYW5kIGl0ZW1bInRveGVudiJdIGlzIG5vdCBOb25lCgogICAgIyBzZXQgcHl0aG9uX3ZlcnNpb24KICAgIHB5dGhvbl92ZXJzaW9uID0gZW52LmdldCgicHl0aG9uLXZlcnNpb24iKQogICAgbSA9IHJlLnNlYXJjaCgiXnB5KDJ8MykoWzAtOV0rdD8pIiwgaXRlbVsidG94ZW52Il0pCiAgICBpZiBweXRob25fdmVyc2lvbiBpcyBub3QgTm9uZToKICAgICAgICBpdGVtWyJweXRob25fdmVyc2lvbiJdID0gcHl0aG9uX3ZlcnNpb24KICAgIGVsaWYgbSBpcyBub3QgTm9uZToKICAgICAgICBtYWpvciwgbWlub3IgPSBtLmdyb3VwcygpCiAgICAgICAgaXRlbVsicHl0aG9uX3ZlcnNpb24iXSA9IGYie21ham9yfS57bWlub3J9IgogICAgZWxzZToKICAgICAgICBpdGVtWyJweXRob25fdmVyc2lvbiJdID0gZW52LmdldCgiZGVmYXVsdF9weXRob24iKSBvciBkZWZhdWx0X3B5dGhvbgoKICAgICMgc2V0IG5hbWUKICAgIGl0ZW1bIm5hbWUiXSA9IGVudi5nZXQoIm5hbWUiKSBvciBmIntpdGVtWyd0b3hlbnYnXX0gKHtpdGVtWydvcyddfSkiCgogICAgIyBzZXQgYXJ0aWZhY3QtbmFtZSAocmVwbGFjZSBpbnZhbGlkIHBhdGggY2hhcmFjdGVycykKICAgIGl0ZW1bImFydGlmYWN0LW5hbWUiXSA9IHJlLnN1YihyIltcXCAvOjw+fCo/XCInXSIsICItIiwgaXRlbVsibmFtZSJdKQogICAgaXRlbVsiYXJ0aWZhY3QtbmFtZSJdID0gcmUuc3ViKHIiLSsiLCAiLSIsIGl0ZW1bImFydGlmYWN0LW5hbWUiXSkKCiAgICAjIHNldCBweXRlc3RfZmxhZwogICAgaXRlbVsicHl0ZXN0X2ZsYWciXSA9ICIiCiAgICBzZXAgPSByIlxcIiBpZiBwbGF0Zm9ybSA9PSAid2luZG93cyIgZWxzZSAiLyIKICAgIGlmIGl0ZW1bInB5dGVzdCJdID09ICJ0cnVlIjoKICAgICAgICBpZiBpdGVtWyJweXRlc3QtcmVzdWx0cy1zdW1tYXJ5Il0gPT0gInRydWUiOgogICAgICAgICAgICBpdGVtWyJweXRlc3RfZmxhZyJdICs9IHJmIi0tanVuaXR4bWwgJHt7R0lUSFVCX1dPUktTUEFDRX19e3NlcH1yZXN1bHRzLnhtbCAiCgogICAgIyBzZXQgbGlicmFyaWVzCiAgICBlbnZfbGlicmFyaWVzID0gZW52LmdldCgibGlicmFyaWVzIikKICAgIGlmIGlzaW5zdGFuY2UoZW52X2xpYnJhcmllcywgc3RyKSBhbmQgbGVuKGVudl9saWJyYXJpZXMuc3RyaXAoKSkgPT0gMDoKICAgICAgICBlbnZfbGlicmFyaWVzID0ge30gICMgbm8gbGlicmFyaWVzIHJlcXVlc3RlZCBmb3IgZW52aXJvbm1lbnQKICAgIGxpYnJhcmllcyA9IGdsb2JhbF9saWJyYXJpZXMgaWYgZW52X2xpYnJhcmllcyBpcyBOb25lIGVsc2UgZW52X2xpYnJhcmllcwogICAgZm9yIG1hbmFnZXIgaW4gWyJicmV3IiwgImJyZXdfY2FzayIsICJhcHQiLCAiY2hvY28iXToKICAgICAgICBpdGVtW2YibGlicmFyaWVzX3ttYW5hZ2VyfSJdID0gIiAiLmpvaW4obGlicmFyaWVzLmdldChtYW5hZ2VyLCBbXSkpCgogICAgaWYgaXRlbVsiY29uZGEiXToKICAgICAgICB3YXJuaW5ncy53YXJuKCJgY29uZGFgIHBhcmFtZXRlciBpcyBkZXByZWNhdGVkIikKCiAgICAgICAgIyBzZXQgImF1dG8iIGNvbmRhIHZhbHVlCiAgICAgICAgaWYgaXRlbVsiY29uZGEiXSA9PSAiYXV0byI6CiAgICAgICAgICAgIGl0ZW1bImNvbmRhIl0gPSAidHJ1ZSIgaWYgImNvbmRhIiBpbiBpdGVtWyJ0b3hlbnYiXSBlbHNlICJmYWxzZSIKCiAgICAgICAgIyBpbmplY3QgdG94ZGVwcyBmb3IgY29uZGEKICAgICAgICBpZiBpdGVtWyJjb25kYSJdID09ICJ0cnVlIiBhbmQgInRveC1jb25kYSIgbm90IGluIGl0ZW1bInRveGRlcHMiXS5sb3dlcigpOgogICAgICAgICAgICBpdGVtWyJ0b3hkZXBzIl0gPSAoInRveC1jb25kYSAiICsgaXRlbVsidG94ZGVwcyJdKS5zdHJpcCgpCgogICAgIyBtYWtlIHRpbWVvdXQtbWludXRlcyBhIG51bWJlcgogICAgaXRlbVsidGltZW91dC1taW51dGVzIl0gPSBpbnQoaXRlbVsidGltZW91dC1taW51dGVzIl0pCgogICAgIyB2ZXJpZnkgdmFsdWVzCiAgICBhc3NlcnQgaXRlbVsicHl0ZXN0Il0gaW4geyJ0cnVlIiwgImZhbHNlIn0KICAgIGFzc2VydCBpdGVtWyJjb25kYSJdIGluIHsidHJ1ZSIsICJmYWxzZSJ9CiAgICBhc3NlcnQgaXRlbVsiZGlzcGxheSJdIGluIHsidHJ1ZSIsICJmYWxzZSJ9CgogICAgcmV0dXJuIGl0ZW0KCgpkZWYgZXhwYW5kX3B5dGhvbl92ZXJzaW9ucyh0b3hlbnY6IHN0ciwgcHl0aG9uX3ZlcnNpb25zOiBsaXN0W1ZlcnNpb24gfCBzdHJdKSAtPiBsaXN0WyhzdHIsIHN0cildOgogICAgIiIiCiAgICBleHBhbmQgYHB5MypgIGludG8gYHB5MzExYCwgYHB5MzEyYCwgYHB5MzEzYCwgZXRjLiBiYXNlZCBvbiBjdXJyZW50bHktc3VwcG9ydGVkIFB5dGhvbiB2ZXJzaW9ucwoKICAgIDpwYXJhbSB2ZXJzaW9uX2dsb2I6IGNhbiBiZSBgcHkqYCwgYHB5MypgLCBgcHkzMCpgLCBgcHkzMSpgIGV0Yy4KICAgICIiIgoKICAgIHB5dGhvbl92ZXJzaW9ucyA9IFtWZXJzaW9uKHZlcnNpb24pIGZvciB2ZXJzaW9uIGluIHB5dGhvbl92ZXJzaW9uc10KCiAgICB0b3hlbnZfZmFjdG9ycyA9IHRveGVudi5zcGxpdCgiLSIpCiAgICBweV92ZXJzaW9uX2dsb2IgPSB0b3hlbnZfZmFjdG9yc1swXQogICAgaWYgbm90IHB5X3ZlcnNpb25fZ2xvYi5zdGFydHN3aXRoKCJweSIpOgogICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoCiAgICAgICAgICAgIGYnaW5wdXQgIntweV92ZXJzaW9uX2dsb2J9IiBpcyBub3QgYSBQeXRob24gdmVyc2lvbiBUb3ggZmFjdG9yIChtdXN0IHN0YXJ0IHdpdGggYHB5YCknCiAgICAgICAgKQoKICAgIGlmICIqIiBub3QgaW4gcHlfdmVyc2lvbl9nbG9iOgogICAgICAgIHJldHVybiBbcHlfdmVyc2lvbl9nbG9iXQoKICAgIGlmIG5vdCBweV92ZXJzaW9uX2dsb2IuZW5kc3dpdGgoIioiKToKICAgICAgICByYWlzZSBOb3RJbXBsZW1lbnRlZEVycm9yKAogICAgICAgICAgICAiUHl0aG9uIHZlcnNpb24gZ2xvYiBtdXN0IGVuZCB3aXRoIGEgYCpgOyBzdWZmaXhlcyBzdWNoIGFzIGB0YCBhcmUgbm90IHlldCBzdXBwb3J0ZWQiCiAgICAgICAgKQoKICAgIG1ham9yX3ZlcnNpb24gPSBweV92ZXJzaW9uX2dsb2JbMl0KICAgIGlmIG1ham9yX3ZlcnNpb24gIT0gIioiOgogICAgICAgIHB5dGhvbl92ZXJzaW9ucyA9IFsKICAgICAgICAgICAgcHl0aG9uX3ZlcnNpb24KICAgICAgICAgICAgZm9yIHB5dGhvbl92ZXJzaW9uIGluIHB5dGhvbl92ZXJzaW9ucwogICAgICAgICAgICBpZiBweXRob25fdmVyc2lvbi5tYWpvciA9PSBpbnQobWFqb3JfdmVyc2lvbikKICAgICAgICBdCgogICAgICAgIG1pbm9yX3ZlcnNpb24gPSBweV92ZXJzaW9uX2dsb2JbMzpdCiAgICAgICAgaWYgbWlub3JfdmVyc2lvbl9zcGVjaWZpZXIgOj0gbWlub3JfdmVyc2lvbi5zcGxpdCgiKiIpWzBdICE9ICIqIjoKICAgICAgICAgICAgbWlub3JfdmVyc2lvbl9iYXNlID0gaW50KG1pbm9yX3ZlcnNpb25fc3BlY2lmaWVyKSAqIDEwCiAgICAgICAgICAgIHB5dGhvbl92ZXJzaW9ucyA9IFsKICAgICAgICAgICAgICAgIHB5dGhvbl92ZXJzaW9uCiAgICAgICAgICAgICAgICBmb3IgcHl0aG9uX3ZlcnNpb24gaW4gcHl0aG9uX3ZlcnNpb25zCiAgICAgICAgICAgICAgICBpZiBtaW5vcl92ZXJzaW9uX2Jhc2UgPD0gcHl0aG9uX3ZlcnNpb24ubWlub3IgPCBtaW5vcl92ZXJzaW9uX2Jhc2UgKyAxMAogICAgICAgICAgICBdCgogICAgcmV0dXJuIFsKICAgICAgICAoCiAgICAgICAgICAgIGYicHl7cHl0aG9uX3ZlcnNpb24ubWFqb3J9e3B5dGhvbl92ZXJzaW9uLm1pbm9yfSIKICAgICAgICAgICAgKyAoZiIteyctJy5qb2luKHRveGVudl9mYWN0b3JzWzE6XSl9IiBpZiBsZW4odG94ZW52X2ZhY3RvcnMpID4gMSBlbHNlICIiKSwKICAgICAgICAgICAgc3RyKHB5dGhvbl92ZXJzaW9uKSwKICAgICAgICApCiAgICAgICAgZm9yIHB5dGhvbl92ZXJzaW9uIGluIHB5dGhvbl92ZXJzaW9ucwogICAgXQoKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBsb2FkX3RveF90YXJnZXRzKCkK + TOX_MATRIX_SCRIPT: IyAvLy8gc2NyaXB0CiMgcmVxdWlyZXMtcHl0aG9uID0gIj09My4xMiIKIyBkZXBlbmRlbmNpZXMgPSBbCiMgICAgICJjbGljaz09OC4yLjEiLAojICAgICAicGFja2FnaW5nPT0yNS4wIiwKIyAgICAgInB5eWFtbD09Ni4wLjIiLAojIF0KIyAvLy8KaW1wb3J0IGpzb24KaW1wb3J0IG9zCmltcG9ydCByZQppbXBvcnQgd2FybmluZ3MKZnJvbSBjb3B5IGltcG9ydCBjb3B5CgppbXBvcnQgY2xpY2sKaW1wb3J0IHlhbWwKZnJvbSBwYWNrYWdpbmcudmVyc2lvbiBpbXBvcnQgVmVyc2lvbgoKCkBjbGljay5jb21tYW5kKCkKQGNsaWNrLm9wdGlvbigiLS1lbnZzIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1saWJyYXJpZXMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXBvc2FyZ3MiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXRveGRlcHMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXRveGFyZ3MiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXB5dGVzdCIsIGRlZmF1bHQ9InRydWUiKQpAY2xpY2sub3B0aW9uKCItLXB5dGVzdC1yZXN1bHRzLXN1bW1hcnkiLCBkZWZhdWx0PSJmYWxzZSIpCkBjbGljay5vcHRpb24oIi0tY292ZXJhZ2UiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWNvbmRhIiwgZGVmYXVsdD0iYXV0byIpCkBjbGljay5vcHRpb24oIi0tc2V0ZW52IiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1kaXNwbGF5IiwgZGVmYXVsdD0iZmFsc2UiKQpAY2xpY2sub3B0aW9uKCItLWNhY2hlLXBhdGgiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWNhY2hlLWtleSIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tY2FjaGUtcmVzdG9yZS1rZXlzIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1wYXRoIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1hcmNoaXZlIiwgZGVmYXVsdD0idHJ1ZSIpCkBjbGljay5vcHRpb24oIi0tYXJ0aWZhY3QtaW5jbHVkZS1oaWRkZW4tZmlsZXMiLCBkZWZhdWx0PSJmYWxzZSIpCkBjbGljay5vcHRpb24oIi0tYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiLCBkZWZhdWx0PSJ3YXJuIikKQGNsaWNrLm9wdGlvbigiLS1ydW5zLW9uIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1kZWZhdWx0LXB5dGhvbiIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tdGltZW91dC1taW51dGVzIiwgZGVmYXVsdD0iMzYwIikKQGNsaWNrLm9wdGlvbigiLS1weXRob24tdmVyc2lvbnMiLCBkZWZhdWx0PSdbIjMiXScpCmRlZiBsb2FkX3RveF90YXJnZXRzKAogICAgZW52cywKICAgIGxpYnJhcmllcywKICAgIHBvc2FyZ3MsCiAgICB0b3hkZXBzLAogICAgdG94YXJncywKICAgIHB5dGVzdCwKICAgIHB5dGVzdF9yZXN1bHRzX3N1bW1hcnksCiAgICBjb3ZlcmFnZSwKICAgIGNvbmRhLAogICAgc2V0ZW52LAogICAgZGlzcGxheSwKICAgIGNhY2hlX3BhdGgsCiAgICBjYWNoZV9rZXksCiAgICBjYWNoZV9yZXN0b3JlX2tleXMsCiAgICBhcnRpZmFjdF9wYXRoLAogICAgYXJ0aWZhY3RfYXJjaGl2ZSwKICAgIGFydGlmYWN0X2luY2x1ZGVfaGlkZGVuX2ZpbGVzLAogICAgYXJ0aWZhY3RfaWZfbm9fZmlsZXNfZm91bmQsCiAgICBydW5zX29uLAogICAgZGVmYXVsdF9weXRob24sCiAgICB0aW1lb3V0X21pbnV0ZXMsCiAgICBweXRob25fdmVyc2lvbnMsCik6CiAgICAiIiJTY3JpcHQgdG8gbG9hZCB0b3ggdGFyZ2V0cyBmb3IgR2l0SHViIEFjdGlvbnMgd29ya2Zsb3cuIiIiCgogICAgaWYgbm90IHB5dGhvbl92ZXJzaW9uczoKICAgICAgICBweXRob25fdmVyc2lvbnM9IFsnMyddCiAgICBlbGlmIGlzaW5zdGFuY2UocHl0aG9uX3ZlcnNpb25zLCBzdHIpOgogICAgICAgIHRyeToKICAgICAgICAgICAgcHl0aG9uX3ZlcnNpb25zPSBqc29uLmxvYWRzKHB5dGhvbl92ZXJzaW9ucykKICAgICAgICBleGNlcHQganNvbi5kZWNvZGVyLkpTT05EZWNvZGVFcnJvcjoKICAgICAgICAgICAgcHl0aG9uX3ZlcnNpb25zPSBweXRob25fdmVyc2lvbnMuc3BsaXQoKQoKICAgICMgTG9hZCBlbnZzIGNvbmZpZwogICAgZW52cyA9IHlhbWwubG9hZChlbnZzLnJlcGxhY2UoIlxcbiIsICJcbiIpLCBMb2FkZXI9eWFtbC5CYXNlTG9hZGVyKQogICAgcHJpbnQoanNvbi5kdW1wcyhlbnZzLCBpbmRlbnQ9MikpCgogICAgIyBMb2FkIGdsb2JhbCBsaWJyYXJpZXMgY29uZmlnCiAgICBnbG9iYWxfbGlicmFyaWVzID0gewogICAgICAgICJicmV3IjogW10sCiAgICAgICAgImJyZXctY2FzayI6IFtdLAogICAgICAgICJhcHQiOiBbXSwKICAgICAgICAiY2hvY28iOiBbXSwKICAgIH0KICAgIGxpYnJhcmllcyA9IHlhbWwubG9hZChsaWJyYXJpZXMsIExvYWRlcj15YW1sLkJhc2VMb2FkZXIpCiAgICBpZiBsaWJyYXJpZXMgaXMgbm90IE5vbmU6CiAgICAgICAgZ2xvYmFsX2xpYnJhcmllcy51cGRhdGUobGlicmFyaWVzKQogICAgcHJpbnQoanNvbi5kdW1wcyhnbG9iYWxfbGlicmFyaWVzLCBpbmRlbnQ9MikpCgogICAgIyBEZWZhdWx0IGltYWdlcyB0byB1c2UgZm9yIHJ1bm5lcnMKICAgIGRlZmF1bHRfcnVuc19vbiA9IHsKICAgICAgICAibGludXgiOiAidWJ1bnR1LWxhdGVzdCIsCiAgICAgICAgIm1hY29zIjogIm1hY29zLWxhdGVzdCIsCiAgICAgICAgIndpbmRvd3MiOiAid2luZG93cy1sYXRlc3QiLAogICAgfQogICAgY3VzdG9tX3J1bnNfb24gPSB5YW1sLmxvYWQocnVuc19vbiwgTG9hZGVyPXlhbWwuQmFzZUxvYWRlcikKICAgIGlmIGlzaW5zdGFuY2UoY3VzdG9tX3J1bnNfb24sIGRpY3QpOgogICAgICAgIGRlZmF1bHRfcnVuc19vbi51cGRhdGUoY3VzdG9tX3J1bnNfb24pCiAgICBwcmludChqc29uLmR1bXBzKGRlZmF1bHRfcnVuc19vbiwgaW5kZW50PTIpKQoKICAgICMgRGVmYXVsdCBzdHJpbmcgcGFyYW1ldGVycyB3aGljaCBjYW4gYmUgb3ZlcndyaXR0ZW4gYnkgZWFjaCBlbnYKICAgIHN0cmluZ19wYXJhbWV0ZXJzID0gewogICAgICAgICJwb3NhcmdzIjogcG9zYXJncywKICAgICAgICAidG94ZGVwcyI6IHRveGRlcHMsCiAgICAgICAgInRveGFyZ3MiOiB0b3hhcmdzLAogICAgICAgICJweXRlc3QiOiBweXRlc3QsCiAgICAgICAgInB5dGVzdC1yZXN1bHRzLXN1bW1hcnkiOiBweXRlc3RfcmVzdWx0c19zdW1tYXJ5LAogICAgICAgICJjb3ZlcmFnZSI6IGNvdmVyYWdlLAogICAgICAgICJjb25kYSI6IGNvbmRhLAogICAgICAgICJzZXRlbnYiOiBzZXRlbnYsCiAgICAgICAgImRpc3BsYXkiOiBkaXNwbGF5LAogICAgICAgICJjYWNoZS1wYXRoIjogY2FjaGVfcGF0aCwKICAgICAgICAiY2FjaGUta2V5IjogY2FjaGVfa2V5LAogICAgICAgICJjYWNoZS1yZXN0b3JlLWtleXMiOiBjYWNoZV9yZXN0b3JlX2tleXMsCiAgICAgICAgImFydGlmYWN0LXBhdGgiOiBhcnRpZmFjdF9wYXRoLAogICAgICAgICJhcnRpZmFjdC1hcmNoaXZlIjogYXJ0aWZhY3RfYXJjaGl2ZSwKICAgICAgICAiYXJ0aWZhY3QtaW5jbHVkZS1oaWRkZW4tZmlsZXMiOiBhcnRpZmFjdF9pbmNsdWRlX2hpZGRlbl9maWxlcywKICAgICAgICAiYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiOiBhcnRpZmFjdF9pZl9ub19maWxlc19mb3VuZCwKICAgICAgICAidGltZW91dC1taW51dGVzIjogdGltZW91dF9taW51dGVzLAogICAgfQoKICAgICMgQ3JlYXRlIG1hdHJpeAogICAgbWF0cml4ID0geyJpbmNsdWRlIjogW119CiAgICBmb3IgZW52IGluIGVudnM6CiAgICAgICAgbWF0cml4X2l0ZW0gPSBnZXRfbWF0cml4X2l0ZW0oCiAgICAgICAgICAgIGVudiwKICAgICAgICAgICAgZ2xvYmFsX2xpYnJhcmllcz1nbG9iYWxfbGlicmFyaWVzLAogICAgICAgICAgICBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnM9c3RyaW5nX3BhcmFtZXRlcnMsCiAgICAgICAgICAgIHJ1bnNfb249ZGVmYXVsdF9ydW5zX29uLAogICAgICAgICAgICBkZWZhdWx0X3B5dGhvbj1kZWZhdWx0X3B5dGhvbiwKICAgICAgICApCgogICAgICAgICMgY2hlY2sgaWYgd2UgbmVlZCB0byBleHBhbmQgcHl0aG9uIHZlcnNpb25zIGZyb20gYSBnbG9iIChpLmUuIHB5KiwgcHkzKiwgcHkzMSosIGV0Yy4pCiAgICAgICAgdG94ZW52ID0gbWF0cml4X2l0ZW1bInRveGVudiJdCiAgICAgICAgaWYgdG94ZW52LnN0YXJ0c3dpdGgoInB5IikgYW5kICIqIiBpbiB0b3hlbnYuc3BsaXQoIi0iKVswXToKICAgICAgICAgICAgdG94ZW52cyA9IGV4cGFuZF9weXRob25fdmVyc2lvbnModG94ZW52LCBweXRob25fdmVyc2lvbnM9cHl0aG9uX3ZlcnNpb25zKQoKICAgICAgICAgICAgZm9yIGV4cGFuZGVkX3RveGVudiwgcHl0aG9uX3ZlcnNpb24gaW4gdG94ZW52czoKICAgICAgICAgICAgICAgIGV4cGFuZGVkX21hdHJpeF9pdGVtID0gY29weShtYXRyaXhfaXRlbSkKICAgICAgICAgICAgICAgIGV4cGFuZGVkX21hdHJpeF9pdGVtWyJ0b3hlbnYiXSA9IGV4cGFuZGVkX3RveGVudgogICAgICAgICAgICAgICAgZXhwYW5kZWRfbWF0cml4X2l0ZW1bIm5hbWUiXSA9IGV4cGFuZGVkX21hdHJpeF9pdGVtWyJuYW1lIl0ucmVwbGFjZSgKICAgICAgICAgICAgICAgICAgICB0b3hlbnYsIGV4cGFuZGVkX3RveGVudgogICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgZXhwYW5kZWRfbWF0cml4X2l0ZW1bInB5dGhvbl92ZXJzaW9uIl0gPSBweXRob25fdmVyc2lvbgogICAgICAgICAgICAgICAgbWF0cml4WyJpbmNsdWRlIl0uYXBwZW5kKGV4cGFuZGVkX21hdHJpeF9pdGVtKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIG1hdHJpeFsiaW5jbHVkZSJdLmFwcGVuZChtYXRyaXhfaXRlbSkKCiAgICAjIE91dHB1dCBtYXRyaXgKICAgIHByaW50KGpzb24uZHVtcHMobWF0cml4LCBpbmRlbnQ9MikpCiAgICB3aXRoIG9wZW4ob3MuZW52aXJvblsiR0lUSFVCX09VVFBVVCJdLCAiYSIpIGFzIGY6CiAgICAgICAgZi53cml0ZShmIm1hdHJpeD17anNvbi5kdW1wcyhtYXRyaXgpfVxuIikKCgpkZWYgZ2V0X21hdHJpeF9pdGVtKGVudiwgZ2xvYmFsX2xpYnJhcmllcywgZ2xvYmFsX3N0cmluZ19wYXJhbWV0ZXJzLCBydW5zX29uLCBkZWZhdWx0X3B5dGhvbik6CgogICAgIyBkZWZpbmUgc3BlYyBmb3IgZWFjaCBtYXRyaXggaW5jbHVkZSAoKyBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnMpCiAgICBpdGVtID0gewogICAgICAgICJvcyI6IE5vbmUsCiAgICAgICAgInRveGVudiI6IE5vbmUsCiAgICAgICAgInB5dGhvbl92ZXJzaW9uIjogTm9uZSwKICAgICAgICAibmFtZSI6IE5vbmUsCiAgICAgICAgInB5dGVzdF9mbGFnIjogTm9uZSwKICAgICAgICAibGlicmFyaWVzX2JyZXciOiBOb25lLAogICAgICAgICJsaWJyYXJpZXNfYnJld19jYXNrIjogTm9uZSwKICAgICAgICAibGlicmFyaWVzX2FwdCI6IE5vbmUsCiAgICAgICAgImxpYnJhcmllc19jaG9jbyI6IE5vbmUsCiAgICAgICAgImNhY2hlLXBhdGgiOiBOb25lLAogICAgICAgICJjYWNoZS1rZXkiOiBOb25lLAogICAgICAgICJjYWNoZS1yZXN0b3JlLWtleXMiOiBOb25lLAogICAgICAgICJhcnRpZmFjdC1uYW1lIjogTm9uZSwKICAgICAgICAiYXJ0aWZhY3QtcGF0aCI6IE5vbmUsCiAgICAgICAgImFydGlmYWN0LWFyY2hpdmUiOiBOb25lLAogICAgICAgICJhcnRpZmFjdC1pbmNsdWRlLWhpZGRlbi1maWxlcyI6IE5vbmUsCiAgICAgICAgImFydGlmYWN0LWlmLW5vLWZpbGVzLWZvdW5kIjogTm9uZSwKICAgICAgICAidGltZW91dC1taW51dGVzIjogTm9uZSwKICAgIH0KICAgIGZvciBzdHJpbmdfcGFyYW0sIGRlZmF1bHQgaW4gZ2xvYmFsX3N0cmluZ19wYXJhbWV0ZXJzLml0ZW1zKCk6CiAgICAgICAgZW52X3ZhbHVlID0gZW52LmdldChzdHJpbmdfcGFyYW0pCiAgICAgICAgaXRlbVtzdHJpbmdfcGFyYW1dID0gZGVmYXVsdCBpZiBlbnZfdmFsdWUgaXMgTm9uZSBlbHNlIGVudl92YWx1ZQoKICAgICMgc2V0IG9zIGFuZCB0b3hlbnYKICAgIGZvciBrLCB2IGluIHJ1bnNfb24uaXRlbXMoKToKICAgICAgICBpZiBrIGluIGVudjoKICAgICAgICAgICAgcGxhdGZvcm0gPSBrCiAgICAgICAgICAgIGl0ZW1bIm9zIl0gPSBlbnYuZ2V0KCJydW5zLW9uIiwgdikKICAgICAgICAgICAgaXRlbVsidG94ZW52Il0gPSBlbnZba10KICAgIGFzc2VydCBpdGVtWyJvcyJdIGlzIG5vdCBOb25lIGFuZCBpdGVtWyJ0b3hlbnYiXSBpcyBub3QgTm9uZQoKICAgICMgc2V0IHB5dGhvbl92ZXJzaW9uCiAgICBweXRob25fdmVyc2lvbiA9IGVudi5nZXQoInB5dGhvbi12ZXJzaW9uIikKICAgIG0gPSByZS5zZWFyY2goIl5weSgyfDMpKFswLTldK3Q/KSIsIGl0ZW1bInRveGVudiJdKQogICAgaWYgcHl0aG9uX3ZlcnNpb24gaXMgbm90IE5vbmU6CiAgICAgICAgaXRlbVsicHl0aG9uX3ZlcnNpb24iXSA9IHB5dGhvbl92ZXJzaW9uCiAgICBlbGlmIG0gaXMgbm90IE5vbmU6CiAgICAgICAgbWFqb3IsIG1pbm9yID0gbS5ncm91cHMoKQogICAgICAgIGl0ZW1bInB5dGhvbl92ZXJzaW9uIl0gPSBmInttYWpvcn0ue21pbm9yfSIKICAgIGVsc2U6CiAgICAgICAgaXRlbVsicHl0aG9uX3ZlcnNpb24iXSA9IGVudi5nZXQoImRlZmF1bHRfcHl0aG9uIikgb3IgZGVmYXVsdF9weXRob24KCiAgICAjIHNldCBuYW1lCiAgICBpdGVtWyJuYW1lIl0gPSBlbnYuZ2V0KCJuYW1lIikgb3IgZiJ7aXRlbVsndG94ZW52J119ICh7aXRlbVsnb3MnXX0pIgoKICAgICMgc2V0IGFydGlmYWN0LW5hbWUgKHJlcGxhY2UgaW52YWxpZCBwYXRoIGNoYXJhY3RlcnMpCiAgICBpdGVtWyJhcnRpZmFjdC1uYW1lIl0gPSByZS5zdWIociJbXFwgLzo8PnwqP1wiJ10iLCAiLSIsIGl0ZW1bIm5hbWUiXSkKICAgIGl0ZW1bImFydGlmYWN0LW5hbWUiXSA9IHJlLnN1YihyIi0rIiwgIi0iLCBpdGVtWyJhcnRpZmFjdC1uYW1lIl0pCgogICAgIyBzZXQgcHl0ZXN0X2ZsYWcKICAgIGl0ZW1bInB5dGVzdF9mbGFnIl0gPSAiIgogICAgc2VwID0gciJcXCIgaWYgcGxhdGZvcm0gPT0gIndpbmRvd3MiIGVsc2UgIi8iCiAgICBpZiBpdGVtWyJweXRlc3QiXSA9PSAidHJ1ZSI6CiAgICAgICAgaWYgaXRlbVsicHl0ZXN0LXJlc3VsdHMtc3VtbWFyeSJdID09ICJ0cnVlIjoKICAgICAgICAgICAgaXRlbVsicHl0ZXN0X2ZsYWciXSArPSByZiItLWp1bml0eG1sICR7e0dJVEhVQl9XT1JLU1BBQ0V9fXtzZXB9cmVzdWx0cy54bWwgIgoKICAgICMgc2V0IGxpYnJhcmllcwogICAgZW52X2xpYnJhcmllcyA9IGVudi5nZXQoImxpYnJhcmllcyIpCiAgICBpZiBpc2luc3RhbmNlKGVudl9saWJyYXJpZXMsIHN0cikgYW5kIGxlbihlbnZfbGlicmFyaWVzLnN0cmlwKCkpID09IDA6CiAgICAgICAgZW52X2xpYnJhcmllcyA9IHt9ICAjIG5vIGxpYnJhcmllcyByZXF1ZXN0ZWQgZm9yIGVudmlyb25tZW50CiAgICBsaWJyYXJpZXMgPSBnbG9iYWxfbGlicmFyaWVzIGlmIGVudl9saWJyYXJpZXMgaXMgTm9uZSBlbHNlIGVudl9saWJyYXJpZXMKICAgIGZvciBtYW5hZ2VyIGluIFsiYnJldyIsICJicmV3X2Nhc2siLCAiYXB0IiwgImNob2NvIl06CiAgICAgICAgaXRlbVtmImxpYnJhcmllc197bWFuYWdlcn0iXSA9ICIgIi5qb2luKGxpYnJhcmllcy5nZXQobWFuYWdlciwgW10pKQoKICAgIGlmIGl0ZW1bImNvbmRhIl06CiAgICAgICAgd2FybmluZ3Mud2FybigiYGNvbmRhYCBwYXJhbWV0ZXIgaXMgZGVwcmVjYXRlZCIpCgogICAgICAgICMgc2V0ICJhdXRvIiBjb25kYSB2YWx1ZQogICAgICAgIGlmIGl0ZW1bImNvbmRhIl0gPT0gImF1dG8iOgogICAgICAgICAgICBpdGVtWyJjb25kYSJdID0gInRydWUiIGlmICJjb25kYSIgaW4gaXRlbVsidG94ZW52Il0gZWxzZSAiZmFsc2UiCgogICAgICAgICMgaW5qZWN0IHRveGRlcHMgZm9yIGNvbmRhCiAgICAgICAgaWYgaXRlbVsiY29uZGEiXSA9PSAidHJ1ZSIgYW5kICJ0b3gtY29uZGEiIG5vdCBpbiBpdGVtWyJ0b3hkZXBzIl0ubG93ZXIoKToKICAgICAgICAgICAgaXRlbVsidG94ZGVwcyJdID0gKCJ0b3gtY29uZGEgIiArIGl0ZW1bInRveGRlcHMiXSkuc3RyaXAoKQoKICAgICMgbWFrZSB0aW1lb3V0LW1pbnV0ZXMgYSBudW1iZXIKICAgIGl0ZW1bInRpbWVvdXQtbWludXRlcyJdID0gaW50KGl0ZW1bInRpbWVvdXQtbWludXRlcyJdKQoKICAgICMgdmVyaWZ5IHZhbHVlcwogICAgYXNzZXJ0IGl0ZW1bInB5dGVzdCJdIGluIHsidHJ1ZSIsICJmYWxzZSJ9CiAgICBhc3NlcnQgaXRlbVsiY29uZGEiXSBpbiB7InRydWUiLCAiZmFsc2UifQogICAgYXNzZXJ0IGl0ZW1bImRpc3BsYXkiXSBpbiB7InRydWUiLCAiZmFsc2UifQoKICAgIHJldHVybiBpdGVtCgoKZGVmIGV4cGFuZF9weXRob25fdmVyc2lvbnModG94ZW52OiBzdHIsIHB5dGhvbl92ZXJzaW9uczogbGlzdFtWZXJzaW9uIHwgc3RyXSkgLT4gbGlzdFsoc3RyLCBzdHIpXToKICAgICIiIgogICAgZXhwYW5kIGBweTMqYCBpbnRvIGBweTMxMWAsIGBweTMxMmAsIGBweTMxM2AsIGV0Yy4gYmFzZWQgb24gY3VycmVudGx5LXN1cHBvcnRlZCBQeXRob24gdmVyc2lvbnMKCiAgICA6cGFyYW0gdmVyc2lvbl9nbG9iOiBjYW4gYmUgYHB5KmAsIGBweTMqYCwgYHB5MzAqYCwgYHB5MzEqYCBldGMuCiAgICAiIiIKCiAgICBweXRob25fdmVyc2lvbnMgPSBbVmVyc2lvbih2ZXJzaW9uKSBmb3IgdmVyc2lvbiBpbiBweXRob25fdmVyc2lvbnNdCgogICAgdG94ZW52X2ZhY3RvcnMgPSB0b3hlbnYuc3BsaXQoIi0iKQogICAgcHlfdmVyc2lvbl9nbG9iID0gdG94ZW52X2ZhY3RvcnNbMF0KICAgIGlmIG5vdCBweV92ZXJzaW9uX2dsb2Iuc3RhcnRzd2l0aCgicHkiKToKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICBmJ2lucHV0ICJ7cHlfdmVyc2lvbl9nbG9ifSIgaXMgbm90IGEgUHl0aG9uIHZlcnNpb24gVG94IGZhY3RvciAobXVzdCBzdGFydCB3aXRoIGBweWApJwogICAgICAgICkKCiAgICBpZiAiKiIgbm90IGluIHB5X3ZlcnNpb25fZ2xvYjoKICAgICAgICByZXR1cm4gW3B5X3ZlcnNpb25fZ2xvYl0KCiAgICBpZiBub3QgcHlfdmVyc2lvbl9nbG9iLmVuZHN3aXRoKCIqIik6CiAgICAgICAgcmFpc2UgTm90SW1wbGVtZW50ZWRFcnJvcigKICAgICAgICAgICAgIlB5dGhvbiB2ZXJzaW9uIGdsb2IgbXVzdCBlbmQgd2l0aCBhIGAqYDsgc3VmZml4ZXMgc3VjaCBhcyBgdGAgYXJlIG5vdCB5ZXQgc3VwcG9ydGVkIgogICAgICAgICkKCiAgICBtYWpvcl92ZXJzaW9uID0gcHlfdmVyc2lvbl9nbG9iWzJdCiAgICBpZiBtYWpvcl92ZXJzaW9uICE9ICIqIjoKICAgICAgICBweXRob25fdmVyc2lvbnMgPSBbCiAgICAgICAgICAgIHB5dGhvbl92ZXJzaW9uCiAgICAgICAgICAgIGZvciBweXRob25fdmVyc2lvbiBpbiBweXRob25fdmVyc2lvbnMKICAgICAgICAgICAgaWYgcHl0aG9uX3ZlcnNpb24ubWFqb3IgPT0gaW50KG1ham9yX3ZlcnNpb24pCiAgICAgICAgXQoKICAgICAgICBtaW5vcl92ZXJzaW9uID0gcHlfdmVyc2lvbl9nbG9iWzM6XQogICAgICAgIGlmIG1pbm9yX3ZlcnNpb25fc3BlY2lmaWVyIDo9IG1pbm9yX3ZlcnNpb24uc3BsaXQoIioiKVswXSAhPSAiKiI6CiAgICAgICAgICAgIG1pbm9yX3ZlcnNpb25fYmFzZSA9IGludChtaW5vcl92ZXJzaW9uX3NwZWNpZmllcikgKiAxMAogICAgICAgICAgICBweXRob25fdmVyc2lvbnMgPSBbCiAgICAgICAgICAgICAgICBweXRob25fdmVyc2lvbgogICAgICAgICAgICAgICAgZm9yIHB5dGhvbl92ZXJzaW9uIGluIHB5dGhvbl92ZXJzaW9ucwogICAgICAgICAgICAgICAgaWYgbWlub3JfdmVyc2lvbl9iYXNlIDw9IHB5dGhvbl92ZXJzaW9uLm1pbm9yIDwgbWlub3JfdmVyc2lvbl9iYXNlICsgMTAKICAgICAgICAgICAgXQoKICAgIHJldHVybiBbCiAgICAgICAgKAogICAgICAgICAgICBmInB5e3B5dGhvbl92ZXJzaW9uLm1ham9yfXtweXRob25fdmVyc2lvbi5taW5vcn0iCiAgICAgICAgICAgICsgKGYiLXsnLScuam9pbih0b3hlbnZfZmFjdG9yc1sxOl0pfSIgaWYgbGVuKHRveGVudl9mYWN0b3JzKSA+IDEgZWxzZSAiIiksCiAgICAgICAgICAgIHN0cihweXRob25fdmVyc2lvbiksCiAgICAgICAgKQogICAgICAgIGZvciBweXRob25fdmVyc2lvbiBpbiBweXRob25fdmVyc2lvbnMKICAgIF0KCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgbG9hZF90b3hfdGFyZ2V0cygpCg== - run: cat tox_matrix.py - - if: contains(inputs.envs, '*') + - if: inputs.python-versions == '' && contains(inputs.envs, '*') id: supported-pythons uses: OpenAstronomy/supported-pythons@83fea1cbcd1df5494f6846152992a553def0325b # 2.0.4 with: package: ${{ job.workflow_repository }} package-ref: ${{ job.workflow_sha }} - id: set-outputs - run: | # zizmor: ignore[template-injection] + run: | # zizmor: ignore[template-injection] uv run tox_matrix.py \ --envs "${{ inputs.envs }}" \ --libraries "${{ inputs.libraries }}" \ @@ -174,12 +178,11 @@ jobs: --artifact-if-no-files-found "${{ inputs.artifact-if-no-files-found }}" \ --runs-on "${{ inputs.runs-on }}" --default-python "${{ inputs.default_python }}" \ --timeout-minutes "${{ inputs.timeout-minutes }}" \ - --supported-pythons '${{ contains(inputs.envs, '*') && steps.supported-pythons.outputs.versions || '["3"]' }}' + --python-versions '${{ inputs.python-versions || contains(inputs.envs, '*') && steps.supported-pythons.outputs.versions || '["3"]' }}' shell: sh outputs: matrix: ${{ steps.set-outputs.outputs.matrix }} - tox: name: ${{ matrix.name }} needs: [envs] @@ -322,7 +325,7 @@ jobs: coverage-gh: ${{ steps.upload-coverage-gh.outputs.artifact-id }} report_overall_test_coverage: - needs: [ tox ] + needs: [tox] if: needs.tox.outputs.coverage-gh name: report overall test coverage runs-on: ubuntu-latest diff --git a/docs/source/tox.rst b/docs/source/tox.rst index f29e8317..1f3d1164 100644 --- a/docs/source/tox.rst +++ b/docs/source/tox.rst @@ -89,14 +89,49 @@ a free-threaded Python interpreter will be used. Python Version Globs (``py*``) """""""""""""""""""""""""""""" -You can use glob syntax for the Python version (i.e. ``py3*``) to expand into all active (not end-of-life) minor versions of Python retrieved from https://endoflife.date +You can use glob syntax for the Python version (i.e. ``py3*``) +to multiply a single env by ``python_versions`` (see below), +preserving Tox factors, test platform, and other env parameters: Additionally, if your project has a ``pyproject.toml`` with a ``project.requires-python`` the Python versions will be constrained to respect that. -For example: -- ``py*`` for all active minor versions of Python supported by your package -- ``py3*`` for all active minor versions of Python 3 supported by your package -- ``py31*`` for all active minor versions of Python between ``3.10`` and ``3.19`` supported by your package +For example with ``python_versions: 3.9 3.10 3.11 3.12 3.13 3.14`` and ``project.requires-python = ">=3.9,<3.14"``, + +.. code-block:: yaml + + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2 + with: + envs: | + - linux: check-style + - linux: py3* + - macos: py31*-cov-xdist + +expands into the following: + +.. code:: yaml + + uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2 + with: + envs: | + - linux: check-style + - linux: py39 + - linux: py310 + - linux: py311 + - linux: py312 + - linux: py313 + - linux: py314 + - macos: py310-cov-xdist + - macos: py311-cov-xdist + - macos: py312-cov-xdist + - macos: py313-cov-xdist + - macos: py314-cov-xdist + +python_versions +^^^^^^^^^^^^^^^ + +Space-separated list of Python versions, i.e. ``3.11 3.12 3.14``, +used to expand Python version globs (see above). +Defaults to all active (not end-of-life) Python versions from https://endoflife.date libraries ^^^^^^^^^ diff --git a/tools/tox_matrix.py b/tools/tox_matrix.py index 441227e2..81fbf47e 100644 --- a/tools/tox_matrix.py +++ b/tools/tox_matrix.py @@ -39,7 +39,7 @@ @click.option("--runs-on", default="") @click.option("--default-python", default="") @click.option("--timeout-minutes", default="360") -@click.option("--supported-pythons", default='["3"]') +@click.option("--python-versions", default='["3"]') def load_tox_targets( envs, libraries, @@ -62,14 +62,17 @@ def load_tox_targets( runs_on, default_python, timeout_minutes, - supported_pythons, + python_versions, ): """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) + if not python_versions: + python_versions= ['3'] + elif isinstance(python_versions, str): + try: + python_versions= json.loads(python_versions) + except json.decoder.JSONDecodeError: + python_versions= python_versions.split() # Load envs config envs = yaml.load(envs.replace("\\n", "\n"), Loader=yaml.BaseLoader) @@ -133,7 +136,7 @@ def load_tox_targets( # 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) + toxenvs = expand_python_versions(toxenv, python_versions=python_versions) for expanded_toxenv, python_version in toxenvs: expanded_matrix_item = copy(matrix_item)