diff --git a/.github/workflows/test_tox.yml b/.github/workflows/test_tox.yml index 5f13564..acdd90e 100644 --- a/.github/workflows/test_tox.yml +++ b/.github/workflows/test_tox.yml @@ -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: diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 3b5a2ae..6dd1d91 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -7,21 +7,6 @@ on: description: Array of tox environments to test required: true type: string - fill: - description: Add an extra toxenv to the matrix for each current version of Python supported by the package - required: false - default: false - type: boolean - fill_platforms: - description: Platforms to iterate with fill - required: false - default: '' - type: string - fill_factors: - description: Tox factors to add to toxenvs added with `fill` - required: false - default: '' - type: string libraries: description: Additional packages to install required: false @@ -158,27 +143,24 @@ jobs: version: "0.10.6" enable-cache: false ignore-empty-workdir: "true" - - if: inputs.fill - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: ${{ inputs.checkout_ref }} persist-credentials: false - - if: inputs.fill - run: echo $SUPPORTED_PYTHONS_SCRIPT | base64 --decode > supported_pythons.py - env: - SUPPORTED_PYTHONS_SCRIPT: IyAvLy8gc2NyaXB0CiMgcmVxdWlyZXMtcHl0aG9uID0gIj49My4xMiIKIyBkZXBlbmRlbmNpZXMgPSBbCiMgICAgICJjbGljaz09OC4yLjEiLAojICAgICAicGFja2FnaW5nPT0yNS4wIiwKIyAgICAgInJlcXVlc3RzPT0yLjMyLjUiLAojICAgICAidG9tbGk9PTIuNC4wIiwKIyBdCiMgLy8vCmltcG9ydCBvcwppbXBvcnQgd2FybmluZ3MKZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCgppbXBvcnQgY2xpY2sKaW1wb3J0IHJlcXVlc3RzCmltcG9ydCB0b21saQpmcm9tIHBhY2thZ2luZy5zcGVjaWZpZXJzIGltcG9ydCBTcGVjaWZpZXJTZXQKZnJvbSBwYWNrYWdpbmcudmVyc2lvbiBpbXBvcnQgVmVyc2lvbgoKCkBjbGljay5jb21tYW5kKCkKQGNsaWNrLm9wdGlvbigiLS1wYWNrYWdlLXNvdXJjZSIsIGRlZmF1bHQ9Tm9uZSkKQGNsaWNrLm9wdGlvbigiLS1mYWN0b3JzIiwgZGVmYXVsdD1Ob25lKQpAY2xpY2sub3B0aW9uKCItLW5vLWVvYXMiLCBpc19mbGFnPVRydWUsIGRlZmF1bHQ9RmFsc2UpCkBjbGljay5vcHRpb24oIi0tcGxhdGZvcm1zIiwgZGVmYXVsdD1Ob25lKQpkZWYgc3VwcG9ydGVkX3B5dGhvbl9lbnZzX2Jsb2NrKAogICAgcGFja2FnZV9zb3VyY2U6IFBhdGggPSBOb25lLAogICAgZmFjdG9yczogbGlzdFtzdHJdID0gTm9uZSwKICAgIG5vX2VvYXM6IGJvb2wgPSBGYWxzZSwKICAgIHBsYXRmb3JtczogbGlzdFtzdHJdID0gTm9uZSwKKToKICAgICIiImVudW1lcmF0ZSB0b3hlbnZzIGZvciBlYWNoIFB5dGhvbiB2ZXJzaW9uIHN1cHBvcnRlZCBieSBwYWNrYWdlIiIiCgogICAgaWYgcGxhdGZvcm1zIGlzIE5vbmU6CiAgICAgICAgcGxhdGZvcm1zID0gWyJsaW51eCJdCiAgICBlbGlmIGlzaW5zdGFuY2UocGxhdGZvcm1zLCBzdHIpOgogICAgICAgIHBsYXRmb3JtcyA9IHBsYXRmb3Jtcy5zcGxpdCgiLCIpCgogICAgdG94ZW52cyA9IHN1cHBvcnRlZF9weXRob25fdG94ZW52cyhwYWNrYWdlX3NvdXJjZSwgZmFjdG9ycywgbm9fZW9hcykKICAgIGVudnNfYmxvY2sgPSAiXFxuIi5qb2luKAogICAgICAgIGYiLSB7cGxhdGZvcm19OiB7dG94ZW52fSIgZm9yIHBsYXRmb3JtIGluIHBsYXRmb3JtcyBmb3IgdG94ZW52IGluIHRveGVudnMKICAgICkKCiAgICBwcmludChlbnZzX2Jsb2NrKQogICAgd2l0aCBvcGVuKG9zLmVudmlyb25bIkdJVEhVQl9PVVRQVVQiXSwgImEiKSBhcyBmOgogICAgICAgIGYud3JpdGUoZiJlbnZzPXtlbnZzX2Jsb2NrfVxuIikKCgpkZWYgc3VwcG9ydGVkX3B5dGhvbl90b3hlbnZzKAogICAgcGFja2FnZV9zb3VyY2U6IFBhdGggPSBOb25lLAogICAgZmFjdG9yczogbGlzdFtzdHJdID0gTm9uZSwKICAgIG5vX2VvYXM6IGJvb2wgPSBGYWxzZSwKKSAtPiBsaXN0W3N0cl06CiAgICBpZiBpc2luc3RhbmNlKGZhY3RvcnMsIHN0cik6CiAgICAgICAgZmFjdG9ycyA9IGZhY3RvcnMuc3BsaXQoIiwiKQoKICAgIHJldHVybiBbCiAgICAgICAgZiJweXtzdHIocHl0aG9uX3ZlcnNpb24pLnJlcGxhY2UoJy4nLCAnJyl9eyctJyArICctJy5qb2luKGZhY3RvcnMpIGlmIGZhY3RvcnMgaXMgbm90IE5vbmUgYW5kIGxlbihmYWN0b3JzKSA+IDAgZWxzZSAnJ30iCiAgICAgICAgZm9yIHB5dGhvbl92ZXJzaW9uIGluIHN1cHBvcnRlZF9weXRob25zKHBhY2thZ2Vfc291cmNlLCBub19lb2FzPW5vX2VvYXMpCiAgICBdCgoKZGVmIHN1cHBvcnRlZF9weXRob25zKAogICAgcGFja2FnZV9zb3VyY2U6IFBhdGggPSBOb25lLAogICAgbm9fZW9hczogYm9vbCA9IEZhbHNlLAopIC0+IGxpc3RbVmVyc2lvbl06CiAgICBjdXJyZW50X3B5dGhvbl92ZXJzaW9ucyA9IGN1cnJlbnRfcHl0aG9ucyhub19lb2FzPW5vX2VvYXMpCgogICAgaWYgbm90IHBhY2thZ2Vfc291cmNlOgogICAgICAgIHN1cHBvcnRlZF92ZXJzaW9ucyA9IGN1cnJlbnRfcHl0aG9uX3ZlcnNpb25zCiAgICBlbHNlOgogICAgICAgIHRyeToKICAgICAgICAgICAgcHlwcm9qZWN0X3RvbWxfZmlsZW5hbWUgPSBQYXRoKHBhY2thZ2Vfc291cmNlKSAvICJweXByb2plY3QudG9tbCIKICAgICAgICAgICAgaWYgcHlwcm9qZWN0X3RvbWxfZmlsZW5hbWUuZXhpc3RzKCk6CiAgICAgICAgICAgICAgICB3aXRoIG9wZW4ocHlwcm9qZWN0X3RvbWxfZmlsZW5hbWUsICJyYiIpIGFzIHB5cHJvamVjdF90b21sX2ZpbGU6CiAgICAgICAgICAgICAgICAgICAgcHlwcm9qZWN0X3RvbWwgPSB0b21saS5sb2FkKHB5cHJvamVjdF90b21sX2ZpbGUpCiAgICAgICAgICAgICAgICBpZiAicHJvamVjdCIgaW4gcHlwcm9qZWN0X3RvbWw6CiAgICAgICAgICAgICAgICAgICAgcHJvamVjdF9tZXRhZGF0YSA9IHB5cHJvamVjdF90b21sWyJwcm9qZWN0Il0KICAgICAgICAgICAgICAgICAgICBpZiAicmVxdWlyZXMtcHl0aG9uIiBpbiBwcm9qZWN0X21ldGFkYXRhOgogICAgICAgICAgICAgICAgICAgICAgICBweXRob25fdmVyc2lvbl9yZXF1aXJlbWVudHMgPSBTcGVjaWZpZXJTZXQoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0X21ldGFkYXRhWyJyZXF1aXJlcy1weXRob24iXQogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICAgICAgcmFpc2UgS2V5RXJyb3IoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYHByb2plY3QucmVxdWlyZXMtcHl0aG9uYCBub3QgZm91bmQgaW4gYHB5cHJvamVjdC50b21sYDsgZW5zdXJlIHlvdXIgcGFja2FnZSBjb25mb3JtcyB0byBQRVA2MjEiCiAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICAgICAgcmFpc2UgS2V5RXJyb3IoCiAgICAgICAgICAgICAgICAgICAgICAgICJgcHJvamVjdGAgbm90IGZvdW5kIGluIGBweXByb2plY3QudG9tbGA7IGVuc3VyZSB5b3VyIHBhY2thZ2UgY29uZm9ybXMgdG8gUEVQNjIxIgogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIHJhaXNlIEZpbGVOb3RGb3VuZEVycm9yKAogICAgICAgICAgICAgICAgICAgICJjb3VsZCBub3QgZmluZCBgcHlwcm9qZWN0LnRvbWxgIGluIHRoZSBwcm92aWRlZCBwYWNrYWdlIHNvdXJjZTsgZW5zdXJlIHlvdXIgcGFja2FnZSBjb25mb3JtcyB0byBQRVA2MjEiCiAgICAgICAgICAgICAgICApCgogICAgICAgICAgICBzdXBwb3J0ZWRfdmVyc2lvbnMgPSBbCiAgICAgICAgICAgICAgICBweXRob25fdmVyc2lvbgogICAgICAgICAgICAgICAgZm9yIHB5dGhvbl92ZXJzaW9uIGluIGN1cnJlbnRfcHl0aG9uX3ZlcnNpb25zCiAgICAgICAgICAgICAgICBpZiBweXRob25fdmVyc2lvbiBpbiBweXRob25fdmVyc2lvbl9yZXF1aXJlbWVudHMKICAgICAgICAgICAgXQogICAgICAgIGV4Y2VwdCAoS2V5RXJyb3IsIFR5cGVFcnJvciwgRmlsZU5vdEZvdW5kRXJyb3IpIGFzIGVycm9yOgogICAgICAgICAgICB3YXJuaW5ncy53YXJuKHN0cihlcnJvcikpCiAgICAgICAgICAgIHdhcm5pbmdzLndhcm4oImZhbGxpbmcgYmFjayB0byBjdXJyZW50IFB5dGhvbiB2ZXJzaW9ucy4uLiIpCiAgICAgICAgICAgIHN1cHBvcnRlZF92ZXJzaW9ucyA9IGN1cnJlbnRfcHl0aG9uX3ZlcnNpb25zCgogICAgcmV0dXJuIHN1cHBvcnRlZF92ZXJzaW9ucwoKCmRlZiBjdXJyZW50X3B5dGhvbnMobm9fZW9hczogYm9vbCA9IEZhbHNlKSAtPiBsaXN0W1ZlcnNpb25dOgogICAgdXJsID0gImh0dHBzOi8vZW5kb2ZsaWZlLmRhdGUvYXBpL3YxL3Byb2R1Y3RzL3B5dGhvbiIKICAgIHJlc3BvbnNlID0gcmVxdWVzdHMuZ2V0KCJodHRwczovL2VuZG9mbGlmZS5kYXRlL2FwaS92MS9wcm9kdWN0cy9weXRob24iKQogICAgaWYgcmVzcG9uc2Uuc3RhdHVzX2NvZGUgPT0gMjAwOgogICAgICAgIHJldHVybiBbCiAgICAgICAgICAgIFZlcnNpb24ocHl0aG9uX3ZlcnNpb25bIm5hbWUiXSkKICAgICAgICAgICAgZm9yIHB5dGhvbl92ZXJzaW9uIGluIHJlc3BvbnNlLmpzb24oKVsicmVzdWx0Il1bInJlbGVhc2VzIl0KICAgICAgICAgICAgaWYgbm90IHB5dGhvbl92ZXJzaW9uWyJpc0VvYXMiIGlmIG5vX2VvYXMgZWxzZSAiaXNFb2wiXQogICAgICAgIF0KICAgIGVsc2U6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcihmInJlcXVlc3QgdG8ge3VybH0gcmV0dXJuZWQgc3RhdHVzIGNvZGUge3Jlc3BvbnNlLnN0YXR1c19jb2RlfSIpCgoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIHN1cHBvcnRlZF9weXRob25fZW52c19ibG9jaygpCg== - - if: inputs.fill # zizmor: ignore[template-injection] - id: supported-pythons - run: uv run supported_pythons.py --package-source . ${{ inputs.fill_platforms != '' && format('--platforms {0}', inputs.fill_platforms) || '' }} ${{ inputs.fill_factors != '' && format('--factors {0}', inputs.fill_factors) || '' }} - shell: sh - run: echo $TOX_MATRIX_SCRIPT | base64 --decode > tox_matrix.py env: - TOX_MATRIX_SCRIPT: IyAvLy8gc2NyaXB0CiMgcmVxdWlyZXMtcHl0aG9uID0gIj09My4xMiIKIyBkZXBlbmRlbmNpZXMgPSBbCiMgICAgICJjbGljaz09OC4yLjEiLAojICAgICAicHl5YW1sPT02LjAuMiIsCiMgXQojIC8vLwppbXBvcnQganNvbgppbXBvcnQgb3MKaW1wb3J0IHJlCmltcG9ydCB3YXJuaW5ncwoKaW1wb3J0IGNsaWNrCmltcG9ydCB5YW1sCgoKQGNsaWNrLmNvbW1hbmQoKQpAY2xpY2sub3B0aW9uKCItLWVudnMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWxpYnJhcmllcyIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tcG9zYXJncyIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tdG94ZGVwcyIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tdG94YXJncyIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tcHl0ZXN0IiwgZGVmYXVsdD0idHJ1ZSIpCkBjbGljay5vcHRpb24oIi0tcHl0ZXN0LXJlc3VsdHMtc3VtbWFyeSIsIGRlZmF1bHQ9ImZhbHNlIikKQGNsaWNrLm9wdGlvbigiLS1jb3ZlcmFnZSIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tY29uZGEiLCBkZWZhdWx0PSJhdXRvIikKQGNsaWNrLm9wdGlvbigiLS1zZXRlbnYiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWRpc3BsYXkiLCBkZWZhdWx0PSJmYWxzZSIpCkBjbGljay5vcHRpb24oIi0tY2FjaGUtcGF0aCIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tY2FjaGUta2V5IiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1jYWNoZS1yZXN0b3JlLWtleXMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWFydGlmYWN0LXBhdGgiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWFydGlmYWN0LWFyY2hpdmUiLCBkZWZhdWx0PSJ0cnVlIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1pbmNsdWRlLWhpZGRlbi1maWxlcyIsIGRlZmF1bHQ9ImZhbHNlIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1pZi1uby1maWxlcy1mb3VuZCIsIGRlZmF1bHQ9Indhcm4iKQpAY2xpY2sub3B0aW9uKCItLXJ1bnMtb24iLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWRlZmF1bHQtcHl0aG9uIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS10aW1lb3V0LW1pbnV0ZXMiLCBkZWZhdWx0PSIzNjAiKQpkZWYgbG9hZF90b3hfdGFyZ2V0cygKICAgIGVudnMsCiAgICBsaWJyYXJpZXMsCiAgICBwb3NhcmdzLAogICAgdG94ZGVwcywKICAgIHRveGFyZ3MsCiAgICBweXRlc3QsCiAgICBweXRlc3RfcmVzdWx0c19zdW1tYXJ5LAogICAgY292ZXJhZ2UsCiAgICBjb25kYSwKICAgIHNldGVudiwKICAgIGRpc3BsYXksCiAgICBjYWNoZV9wYXRoLAogICAgY2FjaGVfa2V5LAogICAgY2FjaGVfcmVzdG9yZV9rZXlzLAogICAgYXJ0aWZhY3RfcGF0aCwKICAgIGFydGlmYWN0X2FyY2hpdmUsCiAgICBhcnRpZmFjdF9pbmNsdWRlX2hpZGRlbl9maWxlcywKICAgIGFydGlmYWN0X2lmX25vX2ZpbGVzX2ZvdW5kLAogICAgcnVuc19vbiwKICAgIGRlZmF1bHRfcHl0aG9uLAogICAgdGltZW91dF9taW51dGVzLAopOgogICAgIiIiU2NyaXB0IHRvIGxvYWQgdG94IHRhcmdldHMgZm9yIEdpdEh1YiBBY3Rpb25zIHdvcmtmbG93LiIiIgogICAgIyBMb2FkIGVudnMgY29uZmlnCiAgICBlbnZzID0geWFtbC5sb2FkKGVudnMucmVwbGFjZSgiXFxuIiwgIlxuIiksIExvYWRlcj15YW1sLkJhc2VMb2FkZXIpCiAgICBwcmludChqc29uLmR1bXBzKGVudnMsIGluZGVudD0yKSkKCiAgICAjIExvYWQgZ2xvYmFsIGxpYnJhcmllcyBjb25maWcKICAgIGdsb2JhbF9saWJyYXJpZXMgPSB7CiAgICAgICAgImJyZXciOiBbXSwKICAgICAgICAiYnJldy1jYXNrIjogW10sCiAgICAgICAgImFwdCI6IFtdLAogICAgICAgICJjaG9jbyI6IFtdLAogICAgfQogICAgbGlicmFyaWVzID0geWFtbC5sb2FkKGxpYnJhcmllcywgTG9hZGVyPXlhbWwuQmFzZUxvYWRlcikKICAgIGlmIGxpYnJhcmllcyBpcyBub3QgTm9uZToKICAgICAgICBnbG9iYWxfbGlicmFyaWVzLnVwZGF0ZShsaWJyYXJpZXMpCiAgICBwcmludChqc29uLmR1bXBzKGdsb2JhbF9saWJyYXJpZXMsIGluZGVudD0yKSkKCiAgICAjIERlZmF1bHQgaW1hZ2VzIHRvIHVzZSBmb3IgcnVubmVycwogICAgZGVmYXVsdF9ydW5zX29uID0gewogICAgICAgICJsaW51eCI6ICJ1YnVudHUtbGF0ZXN0IiwKICAgICAgICAibWFjb3MiOiAibWFjb3MtbGF0ZXN0IiwKICAgICAgICAid2luZG93cyI6ICJ3aW5kb3dzLWxhdGVzdCIsCiAgICB9CiAgICBjdXN0b21fcnVuc19vbiA9IHlhbWwubG9hZChydW5zX29uLCBMb2FkZXI9eWFtbC5CYXNlTG9hZGVyKQogICAgaWYgaXNpbnN0YW5jZShjdXN0b21fcnVuc19vbiwgZGljdCk6CiAgICAgICAgZGVmYXVsdF9ydW5zX29uLnVwZGF0ZShjdXN0b21fcnVuc19vbikKICAgIHByaW50KGpzb24uZHVtcHMoZGVmYXVsdF9ydW5zX29uLCBpbmRlbnQ9MikpCgogICAgIyBEZWZhdWx0IHN0cmluZyBwYXJhbWV0ZXJzIHdoaWNoIGNhbiBiZSBvdmVyd3JpdHRlbiBieSBlYWNoIGVudgogICAgc3RyaW5nX3BhcmFtZXRlcnMgPSB7CiAgICAgICAgInBvc2FyZ3MiOiBwb3NhcmdzLAogICAgICAgICJ0b3hkZXBzIjogdG94ZGVwcywKICAgICAgICAidG94YXJncyI6IHRveGFyZ3MsCiAgICAgICAgInB5dGVzdCI6IHB5dGVzdCwKICAgICAgICAicHl0ZXN0LXJlc3VsdHMtc3VtbWFyeSI6IHB5dGVzdF9yZXN1bHRzX3N1bW1hcnksCiAgICAgICAgImNvdmVyYWdlIjogY292ZXJhZ2UsCiAgICAgICAgImNvbmRhIjogY29uZGEsCiAgICAgICAgInNldGVudiI6IHNldGVudiwKICAgICAgICAiZGlzcGxheSI6IGRpc3BsYXksCiAgICAgICAgImNhY2hlLXBhdGgiOiBjYWNoZV9wYXRoLAogICAgICAgICJjYWNoZS1rZXkiOiBjYWNoZV9rZXksCiAgICAgICAgImNhY2hlLXJlc3RvcmUta2V5cyI6IGNhY2hlX3Jlc3RvcmVfa2V5cywKICAgICAgICAiYXJ0aWZhY3QtcGF0aCI6IGFydGlmYWN0X3BhdGgsCiAgICAgICAgImFydGlmYWN0LWFyY2hpdmUiOiBhcnRpZmFjdF9hcmNoaXZlLAogICAgICAgICJhcnRpZmFjdC1pbmNsdWRlLWhpZGRlbi1maWxlcyI6IGFydGlmYWN0X2luY2x1ZGVfaGlkZGVuX2ZpbGVzLAogICAgICAgICJhcnRpZmFjdC1pZi1uby1maWxlcy1mb3VuZCI6IGFydGlmYWN0X2lmX25vX2ZpbGVzX2ZvdW5kLAogICAgICAgICJ0aW1lb3V0LW1pbnV0ZXMiOiB0aW1lb3V0X21pbnV0ZXMsCiAgICB9CgogICAgIyBDcmVhdGUgbWF0cml4CiAgICBtYXRyaXggPSB7ImluY2x1ZGUiOiBbXX0KICAgIGZvciBlbnYgaW4gZW52czoKICAgICAgICBtYXRyaXhbImluY2x1ZGUiXS5hcHBlbmQoCiAgICAgICAgICAgIGdldF9tYXRyaXhfaXRlbSgKICAgICAgICAgICAgICAgIGVudiwKICAgICAgICAgICAgICAgIGdsb2JhbF9saWJyYXJpZXM9Z2xvYmFsX2xpYnJhcmllcywKICAgICAgICAgICAgICAgIGdsb2JhbF9zdHJpbmdfcGFyYW1ldGVycz1zdHJpbmdfcGFyYW1ldGVycywKICAgICAgICAgICAgICAgIHJ1bnNfb249ZGVmYXVsdF9ydW5zX29uLAogICAgICAgICAgICAgICAgZGVmYXVsdF9weXRob249ZGVmYXVsdF9weXRob24sCiAgICAgICAgICAgICkKICAgICAgICApCgogICAgIyBPdXRwdXQgbWF0cml4CiAgICBwcmludChqc29uLmR1bXBzKG1hdHJpeCwgaW5kZW50PTIpKQogICAgd2l0aCBvcGVuKG9zLmVudmlyb25bIkdJVEhVQl9PVVRQVVQiXSwgImEiKSBhcyBmOgogICAgICAgIGYud3JpdGUoZiJtYXRyaXg9e2pzb24uZHVtcHMobWF0cml4KX1cbiIpCgoKZGVmIGdldF9tYXRyaXhfaXRlbShlbnYsIGdsb2JhbF9saWJyYXJpZXMsIGdsb2JhbF9zdHJpbmdfcGFyYW1ldGVycywgcnVuc19vbiwgZGVmYXVsdF9weXRob24pOgoKICAgICMgZGVmaW5lIHNwZWMgZm9yIGVhY2ggbWF0cml4IGluY2x1ZGUgKCsgZ2xvYmFsX3N0cmluZ19wYXJhbWV0ZXJzKQogICAgaXRlbSA9IHsKICAgICAgICAib3MiOiBOb25lLAogICAgICAgICJ0b3hlbnYiOiBOb25lLAogICAgICAgICJweXRob25fdmVyc2lvbiI6IE5vbmUsCiAgICAgICAgIm5hbWUiOiBOb25lLAogICAgICAgICJweXRlc3RfZmxhZyI6IE5vbmUsCiAgICAgICAgImxpYnJhcmllc19icmV3IjogTm9uZSwKICAgICAgICAibGlicmFyaWVzX2JyZXdfY2FzayI6IE5vbmUsCiAgICAgICAgImxpYnJhcmllc19hcHQiOiBOb25lLAogICAgICAgICJsaWJyYXJpZXNfY2hvY28iOiBOb25lLAogICAgICAgICJjYWNoZS1wYXRoIjogTm9uZSwKICAgICAgICAiY2FjaGUta2V5IjogTm9uZSwKICAgICAgICAiY2FjaGUtcmVzdG9yZS1rZXlzIjogTm9uZSwKICAgICAgICAiYXJ0aWZhY3QtbmFtZSI6IE5vbmUsCiAgICAgICAgImFydGlmYWN0LXBhdGgiOiBOb25lLAogICAgICAgICJhcnRpZmFjdC1hcmNoaXZlIjogTm9uZSwKICAgICAgICAiYXJ0aWZhY3QtaW5jbHVkZS1oaWRkZW4tZmlsZXMiOiBOb25lLAogICAgICAgICJhcnRpZmFjdC1pZi1uby1maWxlcy1mb3VuZCI6IE5vbmUsCiAgICAgICAgInRpbWVvdXQtbWludXRlcyI6IE5vbmUsCiAgICB9CiAgICBmb3Igc3RyaW5nX3BhcmFtLCBkZWZhdWx0IGluIGdsb2JhbF9zdHJpbmdfcGFyYW1ldGVycy5pdGVtcygpOgogICAgICAgIGVudl92YWx1ZSA9IGVudi5nZXQoc3RyaW5nX3BhcmFtKQogICAgICAgIGl0ZW1bc3RyaW5nX3BhcmFtXSA9IGRlZmF1bHQgaWYgZW52X3ZhbHVlIGlzIE5vbmUgZWxzZSBlbnZfdmFsdWUKCiAgICAjIHNldCBvcyBhbmQgdG94ZW52CiAgICBmb3IgaywgdiBpbiBydW5zX29uLml0ZW1zKCk6CiAgICAgICAgaWYgayBpbiBlbnY6CiAgICAgICAgICAgIHBsYXRmb3JtID0gawogICAgICAgICAgICBpdGVtWyJvcyJdID0gZW52LmdldCgicnVucy1vbiIsIHYpCiAgICAgICAgICAgIGl0ZW1bInRveGVudiJdID0gZW52W2tdCiAgICBhc3NlcnQgaXRlbVsib3MiXSBpcyBub3QgTm9uZSBhbmQgaXRlbVsidG94ZW52Il0gaXMgbm90IE5vbmUKCiAgICAjIHNldCBweXRob25fdmVyc2lvbgogICAgcHl0aG9uX3ZlcnNpb24gPSBlbnYuZ2V0KCJweXRob24tdmVyc2lvbiIpCiAgICBtID0gcmUuc2VhcmNoKCJecHkoMnwzKShbMC05XSt0PykiLCBpdGVtWyJ0b3hlbnYiXSkKICAgIGlmIHB5dGhvbl92ZXJzaW9uIGlzIG5vdCBOb25lOgogICAgICAgIGl0ZW1bInB5dGhvbl92ZXJzaW9uIl0gPSBweXRob25fdmVyc2lvbgogICAgZWxpZiBtIGlzIG5vdCBOb25lOgogICAgICAgIG1ham9yLCBtaW5vciA9IG0uZ3JvdXBzKCkKICAgICAgICBpdGVtWyJweXRob25fdmVyc2lvbiJdID0gZiJ7bWFqb3J9LnttaW5vcn0iCiAgICBlbHNlOgogICAgICAgIGl0ZW1bInB5dGhvbl92ZXJzaW9uIl0gPSBlbnYuZ2V0KCJkZWZhdWx0X3B5dGhvbiIpIG9yIGRlZmF1bHRfcHl0aG9uCgogICAgIyBzZXQgbmFtZQogICAgaXRlbVsibmFtZSJdID0gZW52LmdldCgibmFtZSIpIG9yIGYie2l0ZW1bJ3RveGVudiddfSAoe2l0ZW1bJ29zJ119KSIKCiAgICAjIHNldCBhcnRpZmFjdC1uYW1lIChyZXBsYWNlIGludmFsaWQgcGF0aCBjaGFyYWN0ZXJzKQogICAgaXRlbVsiYXJ0aWZhY3QtbmFtZSJdID0gcmUuc3ViKHIiW1xcIC86PD58Kj9cIiddIiwgIi0iLCBpdGVtWyJuYW1lIl0pCiAgICBpdGVtWyJhcnRpZmFjdC1uYW1lIl0gPSByZS5zdWIociItKyIsICItIiwgaXRlbVsiYXJ0aWZhY3QtbmFtZSJdKQoKICAgICMgc2V0IHB5dGVzdF9mbGFnCiAgICBpdGVtWyJweXRlc3RfZmxhZyJdID0gIiIKICAgIHNlcCA9IHIiXFwiIGlmIHBsYXRmb3JtID09ICJ3aW5kb3dzIiBlbHNlICIvIgogICAgaWYgaXRlbVsicHl0ZXN0Il0gPT0gInRydWUiOgogICAgICAgIGlmICJjb2RlY292IiBpbiBpdGVtLmdldCgiY292ZXJhZ2UiLCAiIik6CiAgICAgICAgICAgICMgTm90ZSB0aGF0IHdlIGRvbid0IGluY2x1ZGUgLS1jb3YgaGVyZSBhcyBpZiBpdCdzIHByb3ZpZGVkIHRvIHB5dGVzdCB0d2ljZSBpdCBicmVha3MgY292IHJlcG9ydGluZy4KICAgICAgICAgICAgIyBMb3RzIG9mIHVzZXJzIG9mIHRoaXMgc3BlY2lmeSAtLWNvdiBpbiB0aGVpciB0b3guaW5pIHNvIGl0J3MgYmVlbiByZW1vdmVkIGZvciBiYWNrd2FyZHMgY29tcGF0aWJpbGl0eS4KICAgICAgICAgICAgIyBodHRwczovL2dpdGh1Yi5jb20vT3BlbkFzdHJvbm9teS9naXRodWItYWN0aW9ucy13b3JrZmxvd3MvaXNzdWVzLzM4MwogICAgICAgICAgICBpdGVtWyJweXRlc3RfZmxhZyJdICs9ICgKICAgICAgICAgICAgICAgIHJmIi0tY292LXJlcG9ydD14bWw6JHt7R0lUSFVCX1dPUktTUEFDRX19e3NlcH1jb3ZlcmFnZS54bWwgIgogICAgICAgICAgICApCgogICAgICAgIGlmIGl0ZW1bInB5dGVzdC1yZXN1bHRzLXN1bW1hcnkiXSA9PSAidHJ1ZSI6CiAgICAgICAgICAgIGl0ZW1bInB5dGVzdF9mbGFnIl0gKz0gcmYiLS1qdW5pdHhtbCAke3tHSVRIVUJfV09SS1NQQUNFfX17c2VwfXJlc3VsdHMueG1sICIKCiAgICAjIHNldCBsaWJyYXJpZXMKICAgIGVudl9saWJyYXJpZXMgPSBlbnYuZ2V0KCJsaWJyYXJpZXMiKQogICAgaWYgaXNpbnN0YW5jZShlbnZfbGlicmFyaWVzLCBzdHIpIGFuZCBsZW4oZW52X2xpYnJhcmllcy5zdHJpcCgpKSA9PSAwOgogICAgICAgIGVudl9saWJyYXJpZXMgPSB7fSAgIyBubyBsaWJyYXJpZXMgcmVxdWVzdGVkIGZvciBlbnZpcm9ubWVudAogICAgbGlicmFyaWVzID0gZ2xvYmFsX2xpYnJhcmllcyBpZiBlbnZfbGlicmFyaWVzIGlzIE5vbmUgZWxzZSBlbnZfbGlicmFyaWVzCiAgICBmb3IgbWFuYWdlciBpbiBbImJyZXciLCAiYnJld19jYXNrIiwgImFwdCIsICJjaG9jbyJdOgogICAgICAgIGl0ZW1bZiJsaWJyYXJpZXNfe21hbmFnZXJ9Il0gPSAiICIuam9pbihsaWJyYXJpZXMuZ2V0KG1hbmFnZXIsIFtdKSkKCiAgICBpZiBpdGVtWyJjb25kYSJdOgogICAgICAgIHdhcm5pbmdzLndhcm4oImBjb25kYWAgcGFyYW1ldGVyIGlzIGRlcHJlY2F0ZWQiKQoKICAgICAgICAjIHNldCAiYXV0byIgY29uZGEgdmFsdWUKICAgICAgICBpZiBpdGVtWyJjb25kYSJdID09ICJhdXRvIjoKICAgICAgICAgICAgaXRlbVsiY29uZGEiXSA9ICJ0cnVlIiBpZiAiY29uZGEiIGluIGl0ZW1bInRveGVudiJdIGVsc2UgImZhbHNlIgoKICAgICAgICAjIGluamVjdCB0b3hkZXBzIGZvciBjb25kYQogICAgICAgIGlmIGl0ZW1bImNvbmRhIl0gPT0gInRydWUiIGFuZCAidG94LWNvbmRhIiBub3QgaW4gaXRlbVsidG94ZGVwcyJdLmxvd2VyKCk6CiAgICAgICAgICAgIGl0ZW1bInRveGRlcHMiXSA9ICgidG94LWNvbmRhICIgKyBpdGVtWyJ0b3hkZXBzIl0pLnN0cmlwKCkKCiAgICAjIG1ha2UgdGltZW91dC1taW51dGVzIGEgbnVtYmVyCiAgICBpdGVtWyJ0aW1lb3V0LW1pbnV0ZXMiXSA9IGludChpdGVtWyJ0aW1lb3V0LW1pbnV0ZXMiXSkKCiAgICAjIHZlcmlmeSB2YWx1ZXMKICAgIGFzc2VydCBpdGVtWyJweXRlc3QiXSBpbiB7InRydWUiLCAiZmFsc2UifQogICAgYXNzZXJ0IGl0ZW1bImNvbmRhIl0gaW4geyJ0cnVlIiwgImZhbHNlIn0KICAgIGFzc2VydCBpdGVtWyJkaXNwbGF5Il0gaW4geyJ0cnVlIiwgImZhbHNlIn0KCiAgICByZXR1cm4gaXRlbQoKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBsb2FkX3RveF90YXJnZXRzKCkK + TOX_MATRIX_SCRIPT: IyAvLy8gc2NyaXB0CiMgcmVxdWlyZXMtcHl0aG9uID0gIj09My4xMiIKIyBkZXBlbmRlbmNpZXMgPSBbCiMgICAgICJjbGljaz09OC4yLjEiLAojICAgICAicGFja2FnaW5nPT0yNS4wIiwKIyAgICAgInB5eWFtbD09Ni4wLjIiLAojICAgICAicmVxdWVzdHM9PTIuMzIuNSIsCiMgICAgICJ0b21saT09Mi40LjAiLAojIF0KIyAvLy8KaW1wb3J0IGpzb24KaW1wb3J0IG9zCmltcG9ydCByZQppbXBvcnQgd2FybmluZ3MKZnJvbSBjb3B5IGltcG9ydCBjb3B5CgppbXBvcnQgY2xpY2sKaW1wb3J0IHlhbWwKZnJvbSBwYWNrYWdpbmcudmVyc2lvbiBpbXBvcnQgVmVyc2lvbgoKCkBjbGljay5jb21tYW5kKCkKQGNsaWNrLm9wdGlvbigiLS1lbnZzIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1saWJyYXJpZXMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXBvc2FyZ3MiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXRveGRlcHMiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXRveGFyZ3MiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLXB5dGVzdCIsIGRlZmF1bHQ9InRydWUiKQpAY2xpY2sub3B0aW9uKCItLXB5dGVzdC1yZXN1bHRzLXN1bW1hcnkiLCBkZWZhdWx0PSJmYWxzZSIpCkBjbGljay5vcHRpb24oIi0tY292ZXJhZ2UiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWNvbmRhIiwgZGVmYXVsdD0iYXV0byIpCkBjbGljay5vcHRpb24oIi0tc2V0ZW52IiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1kaXNwbGF5IiwgZGVmYXVsdD0iZmFsc2UiKQpAY2xpY2sub3B0aW9uKCItLWNhY2hlLXBhdGgiLCBkZWZhdWx0PSIiKQpAY2xpY2sub3B0aW9uKCItLWNhY2hlLWtleSIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tY2FjaGUtcmVzdG9yZS1rZXlzIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1wYXRoIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1hcnRpZmFjdC1hcmNoaXZlIiwgZGVmYXVsdD0idHJ1ZSIpCkBjbGljay5vcHRpb24oIi0tYXJ0aWZhY3QtaW5jbHVkZS1oaWRkZW4tZmlsZXMiLCBkZWZhdWx0PSJmYWxzZSIpCkBjbGljay5vcHRpb24oIi0tYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiLCBkZWZhdWx0PSJ3YXJuIikKQGNsaWNrLm9wdGlvbigiLS1ydW5zLW9uIiwgZGVmYXVsdD0iIikKQGNsaWNrLm9wdGlvbigiLS1kZWZhdWx0LXB5dGhvbiIsIGRlZmF1bHQ9IiIpCkBjbGljay5vcHRpb24oIi0tdGltZW91dC1taW51dGVzIiwgZGVmYXVsdD0iMzYwIikKQGNsaWNrLm9wdGlvbigiLS1zdXBwb3J0ZWQtcHl0aG9ucyIsIGRlZmF1bHQ9J1siMyJdJykKZGVmIGxvYWRfdG94X3RhcmdldHMoCiAgICBlbnZzLAogICAgbGlicmFyaWVzLAogICAgcG9zYXJncywKICAgIHRveGRlcHMsCiAgICB0b3hhcmdzLAogICAgcHl0ZXN0LAogICAgcHl0ZXN0X3Jlc3VsdHNfc3VtbWFyeSwKICAgIGNvdmVyYWdlLAogICAgY29uZGEsCiAgICBzZXRlbnYsCiAgICBkaXNwbGF5LAogICAgY2FjaGVfcGF0aCwKICAgIGNhY2hlX2tleSwKICAgIGNhY2hlX3Jlc3RvcmVfa2V5cywKICAgIGFydGlmYWN0X3BhdGgsCiAgICBhcnRpZmFjdF9hcmNoaXZlLAogICAgYXJ0aWZhY3RfaW5jbHVkZV9oaWRkZW5fZmlsZXMsCiAgICBhcnRpZmFjdF9pZl9ub19maWxlc19mb3VuZCwKICAgIHJ1bnNfb24sCiAgICBkZWZhdWx0X3B5dGhvbiwKICAgIHRpbWVvdXRfbWludXRlcywKICAgIHN1cHBvcnRlZF9weXRob25zLAopOgogICAgIiIiU2NyaXB0IHRvIGxvYWQgdG94IHRhcmdldHMgZm9yIEdpdEh1YiBBY3Rpb25zIHdvcmtmbG93LiIiIgoKICAgIGlmIG5vdCBzdXBwb3J0ZWRfcHl0aG9uczoKICAgICAgICBzdXBwb3J0ZWRfcHl0aG9ucyA9IFsnMyddCiAgICBlbGlmIGlzaW5zdGFuY2Uoc3VwcG9ydGVkX3B5dGhvbnMsIHN0cik6CiAgICAgICAgc3VwcG9ydGVkX3B5dGhvbnMgPSBqc29uLmxvYWRzKHN1cHBvcnRlZF9weXRob25zKQoKICAgICMgTG9hZCBlbnZzIGNvbmZpZwogICAgZW52cyA9IHlhbWwubG9hZChlbnZzLnJlcGxhY2UoIlxcbiIsICJcbiIpLCBMb2FkZXI9eWFtbC5CYXNlTG9hZGVyKQogICAgcHJpbnQoanNvbi5kdW1wcyhlbnZzLCBpbmRlbnQ9MikpCgogICAgIyBMb2FkIGdsb2JhbCBsaWJyYXJpZXMgY29uZmlnCiAgICBnbG9iYWxfbGlicmFyaWVzID0gewogICAgICAgICJicmV3IjogW10sCiAgICAgICAgImJyZXctY2FzayI6IFtdLAogICAgICAgICJhcHQiOiBbXSwKICAgICAgICAiY2hvY28iOiBbXSwKICAgIH0KICAgIGxpYnJhcmllcyA9IHlhbWwubG9hZChsaWJyYXJpZXMsIExvYWRlcj15YW1sLkJhc2VMb2FkZXIpCiAgICBpZiBsaWJyYXJpZXMgaXMgbm90IE5vbmU6CiAgICAgICAgZ2xvYmFsX2xpYnJhcmllcy51cGRhdGUobGlicmFyaWVzKQogICAgcHJpbnQoanNvbi5kdW1wcyhnbG9iYWxfbGlicmFyaWVzLCBpbmRlbnQ9MikpCgogICAgIyBEZWZhdWx0IGltYWdlcyB0byB1c2UgZm9yIHJ1bm5lcnMKICAgIGRlZmF1bHRfcnVuc19vbiA9IHsKICAgICAgICAibGludXgiOiAidWJ1bnR1LWxhdGVzdCIsCiAgICAgICAgIm1hY29zIjogIm1hY29zLWxhdGVzdCIsCiAgICAgICAgIndpbmRvd3MiOiAid2luZG93cy1sYXRlc3QiLAogICAgfQogICAgY3VzdG9tX3J1bnNfb24gPSB5YW1sLmxvYWQocnVuc19vbiwgTG9hZGVyPXlhbWwuQmFzZUxvYWRlcikKICAgIGlmIGlzaW5zdGFuY2UoY3VzdG9tX3J1bnNfb24sIGRpY3QpOgogICAgICAgIGRlZmF1bHRfcnVuc19vbi51cGRhdGUoY3VzdG9tX3J1bnNfb24pCiAgICBwcmludChqc29uLmR1bXBzKGRlZmF1bHRfcnVuc19vbiwgaW5kZW50PTIpKQoKICAgICMgRGVmYXVsdCBzdHJpbmcgcGFyYW1ldGVycyB3aGljaCBjYW4gYmUgb3ZlcndyaXR0ZW4gYnkgZWFjaCBlbnYKICAgIHN0cmluZ19wYXJhbWV0ZXJzID0gewogICAgICAgICJwb3NhcmdzIjogcG9zYXJncywKICAgICAgICAidG94ZGVwcyI6IHRveGRlcHMsCiAgICAgICAgInRveGFyZ3MiOiB0b3hhcmdzLAogICAgICAgICJweXRlc3QiOiBweXRlc3QsCiAgICAgICAgInB5dGVzdC1yZXN1bHRzLXN1bW1hcnkiOiBweXRlc3RfcmVzdWx0c19zdW1tYXJ5LAogICAgICAgICJjb3ZlcmFnZSI6IGNvdmVyYWdlLAogICAgICAgICJjb25kYSI6IGNvbmRhLAogICAgICAgICJzZXRlbnYiOiBzZXRlbnYsCiAgICAgICAgImRpc3BsYXkiOiBkaXNwbGF5LAogICAgICAgICJjYWNoZS1wYXRoIjogY2FjaGVfcGF0aCwKICAgICAgICAiY2FjaGUta2V5IjogY2FjaGVfa2V5LAogICAgICAgICJjYWNoZS1yZXN0b3JlLWtleXMiOiBjYWNoZV9yZXN0b3JlX2tleXMsCiAgICAgICAgImFydGlmYWN0LXBhdGgiOiBhcnRpZmFjdF9wYXRoLAogICAgICAgICJhcnRpZmFjdC1hcmNoaXZlIjogYXJ0aWZhY3RfYXJjaGl2ZSwKICAgICAgICAiYXJ0aWZhY3QtaW5jbHVkZS1oaWRkZW4tZmlsZXMiOiBhcnRpZmFjdF9pbmNsdWRlX2hpZGRlbl9maWxlcywKICAgICAgICAiYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiOiBhcnRpZmFjdF9pZl9ub19maWxlc19mb3VuZCwKICAgICAgICAidGltZW91dC1taW51dGVzIjogdGltZW91dF9taW51dGVzLAogICAgfQoKICAgICMgQ3JlYXRlIG1hdHJpeAogICAgbWF0cml4ID0geyJpbmNsdWRlIjogW119CiAgICBmb3IgZW52IGluIGVudnM6CiAgICAgICAgbWF0cml4X2l0ZW0gPSBnZXRfbWF0cml4X2l0ZW0oCiAgICAgICAgICAgIGVudiwKICAgICAgICAgICAgZ2xvYmFsX2xpYnJhcmllcz1nbG9iYWxfbGlicmFyaWVzLAogICAgICAgICAgICBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnM9c3RyaW5nX3BhcmFtZXRlcnMsCiAgICAgICAgICAgIHJ1bnNfb249ZGVmYXVsdF9ydW5zX29uLAogICAgICAgICAgICBkZWZhdWx0X3B5dGhvbj1kZWZhdWx0X3B5dGhvbiwKICAgICAgICApCgogICAgICAgICMgY2hlY2sgaWYgd2UgbmVlZCB0byBleHBhbmQgcHl0aG9uIHZlcnNpb25zIGZyb20gYSBnbG9iIChpLmUuIHB5KiwgcHkzKiwgcHkzMSosIGV0Yy4pCiAgICAgICAgdG94ZW52ID0gbWF0cml4X2l0ZW1bInRveGVudiJdCiAgICAgICAgaWYgdG94ZW52LnN0YXJ0c3dpdGgoInB5IikgYW5kICIqIiBpbiB0b3hlbnYuc3BsaXQoIi0iKVswXToKICAgICAgICAgICAgdG94ZW52cyA9IGV4cGFuZF9weXRob25fdmVyc2lvbnModG94ZW52LCBweXRob25fdmVyc2lvbnM9c3VwcG9ydGVkX3B5dGhvbnMpCgogICAgICAgICAgICBmb3IgZXhwYW5kZWRfdG94ZW52LCBweXRob25fdmVyc2lvbiBpbiB0b3hlbnZzOgogICAgICAgICAgICAgICAgZXhwYW5kZWRfbWF0cml4X2l0ZW0gPSBjb3B5KG1hdHJpeF9pdGVtKQogICAgICAgICAgICAgICAgZXhwYW5kZWRfbWF0cml4X2l0ZW1bInRveGVudiJdID0gZXhwYW5kZWRfdG94ZW52CiAgICAgICAgICAgICAgICBleHBhbmRlZF9tYXRyaXhfaXRlbVsibmFtZSJdID0gZXhwYW5kZWRfbWF0cml4X2l0ZW1bIm5hbWUiXS5yZXBsYWNlKAogICAgICAgICAgICAgICAgICAgIHRveGVudiwgZXhwYW5kZWRfdG94ZW52CiAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICBleHBhbmRlZF9tYXRyaXhfaXRlbVsicHl0aG9uX3ZlcnNpb24iXSA9IHB5dGhvbl92ZXJzaW9uCiAgICAgICAgICAgICAgICBtYXRyaXhbImluY2x1ZGUiXS5hcHBlbmQoZXhwYW5kZWRfbWF0cml4X2l0ZW0pCiAgICAgICAgZWxzZToKICAgICAgICAgICAgbWF0cml4WyJpbmNsdWRlIl0uYXBwZW5kKG1hdHJpeF9pdGVtKQoKICAgICMgT3V0cHV0IG1hdHJpeAogICAgcHJpbnQoanNvbi5kdW1wcyhtYXRyaXgsIGluZGVudD0yKSkKICAgIHdpdGggb3Blbihvcy5lbnZpcm9uWyJHSVRIVUJfT1VUUFVUIl0sICJhIikgYXMgZjoKICAgICAgICBmLndyaXRlKGYibWF0cml4PXtqc29uLmR1bXBzKG1hdHJpeCl9XG4iKQoKCmRlZiBnZXRfbWF0cml4X2l0ZW0oZW52LCBnbG9iYWxfbGlicmFyaWVzLCBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnMsIHJ1bnNfb24sIGRlZmF1bHRfcHl0aG9uKToKCiAgICAjIGRlZmluZSBzcGVjIGZvciBlYWNoIG1hdHJpeCBpbmNsdWRlICgrIGdsb2JhbF9zdHJpbmdfcGFyYW1ldGVycykKICAgIGl0ZW0gPSB7CiAgICAgICAgIm9zIjogTm9uZSwKICAgICAgICAidG94ZW52IjogTm9uZSwKICAgICAgICAicHl0aG9uX3ZlcnNpb24iOiBOb25lLAogICAgICAgICJuYW1lIjogTm9uZSwKICAgICAgICAicHl0ZXN0X2ZsYWciOiBOb25lLAogICAgICAgICJsaWJyYXJpZXNfYnJldyI6IE5vbmUsCiAgICAgICAgImxpYnJhcmllc19icmV3X2Nhc2siOiBOb25lLAogICAgICAgICJsaWJyYXJpZXNfYXB0IjogTm9uZSwKICAgICAgICAibGlicmFyaWVzX2Nob2NvIjogTm9uZSwKICAgICAgICAiY2FjaGUtcGF0aCI6IE5vbmUsCiAgICAgICAgImNhY2hlLWtleSI6IE5vbmUsCiAgICAgICAgImNhY2hlLXJlc3RvcmUta2V5cyI6IE5vbmUsCiAgICAgICAgImFydGlmYWN0LW5hbWUiOiBOb25lLAogICAgICAgICJhcnRpZmFjdC1wYXRoIjogTm9uZSwKICAgICAgICAiYXJ0aWZhY3QtYXJjaGl2ZSI6IE5vbmUsCiAgICAgICAgImFydGlmYWN0LWluY2x1ZGUtaGlkZGVuLWZpbGVzIjogTm9uZSwKICAgICAgICAiYXJ0aWZhY3QtaWYtbm8tZmlsZXMtZm91bmQiOiBOb25lLAogICAgICAgICJ0aW1lb3V0LW1pbnV0ZXMiOiBOb25lLAogICAgfQogICAgZm9yIHN0cmluZ19wYXJhbSwgZGVmYXVsdCBpbiBnbG9iYWxfc3RyaW5nX3BhcmFtZXRlcnMuaXRlbXMoKToKICAgICAgICBlbnZfdmFsdWUgPSBlbnYuZ2V0KHN0cmluZ19wYXJhbSkKICAgICAgICBpdGVtW3N0cmluZ19wYXJhbV0gPSBkZWZhdWx0IGlmIGVudl92YWx1ZSBpcyBOb25lIGVsc2UgZW52X3ZhbHVlCgogICAgIyBzZXQgb3MgYW5kIHRveGVudgogICAgZm9yIGssIHYgaW4gcnVuc19vbi5pdGVtcygpOgogICAgICAgIGlmIGsgaW4gZW52OgogICAgICAgICAgICBwbGF0Zm9ybSA9IGsKICAgICAgICAgICAgaXRlbVsib3MiXSA9IGVudi5nZXQoInJ1bnMtb24iLCB2KQogICAgICAgICAgICBpdGVtWyJ0b3hlbnYiXSA9IGVudltrXQogICAgYXNzZXJ0IGl0ZW1bIm9zIl0gaXMgbm90IE5vbmUgYW5kIGl0ZW1bInRveGVudiJdIGlzIG5vdCBOb25lCgogICAgIyBzZXQgcHl0aG9uX3ZlcnNpb24KICAgIHB5dGhvbl92ZXJzaW9uID0gZW52LmdldCgicHl0aG9uLXZlcnNpb24iKQogICAgbSA9IHJlLnNlYXJjaCgiXnB5KDJ8MykoWzAtOV0rdD8pIiwgaXRlbVsidG94ZW52Il0pCiAgICBpZiBweXRob25fdmVyc2lvbiBpcyBub3QgTm9uZToKICAgICAgICBpdGVtWyJweXRob25fdmVyc2lvbiJdID0gcHl0aG9uX3ZlcnNpb24KICAgIGVsaWYgbSBpcyBub3QgTm9uZToKICAgICAgICBtYWpvciwgbWlub3IgPSBtLmdyb3VwcygpCiAgICAgICAgaXRlbVsicHl0aG9uX3ZlcnNpb24iXSA9IGYie21ham9yfS57bWlub3J9IgogICAgZWxzZToKICAgICAgICBpdGVtWyJweXRob25fdmVyc2lvbiJdID0gZW52LmdldCgiZGVmYXVsdF9weXRob24iKSBvciBkZWZhdWx0X3B5dGhvbgoKICAgICMgc2V0IG5hbWUKICAgIGl0ZW1bIm5hbWUiXSA9IGVudi5nZXQoIm5hbWUiKSBvciBmIntpdGVtWyd0b3hlbnYnXX0gKHtpdGVtWydvcyddfSkiCgogICAgIyBzZXQgYXJ0aWZhY3QtbmFtZSAocmVwbGFjZSBpbnZhbGlkIHBhdGggY2hhcmFjdGVycykKICAgIGl0ZW1bImFydGlmYWN0LW5hbWUiXSA9IHJlLnN1YihyIltcXCAvOjw+fCo/XCInXSIsICItIiwgaXRlbVsibmFtZSJdKQogICAgaXRlbVsiYXJ0aWZhY3QtbmFtZSJdID0gcmUuc3ViKHIiLSsiLCAiLSIsIGl0ZW1bImFydGlmYWN0LW5hbWUiXSkKCiAgICAjIHNldCBweXRlc3RfZmxhZwogICAgaXRlbVsicHl0ZXN0X2ZsYWciXSA9ICIiCiAgICBzZXAgPSByIlxcIiBpZiBwbGF0Zm9ybSA9PSAid2luZG93cyIgZWxzZSAiLyIKICAgIGlmIGl0ZW1bInB5dGVzdCJdID09ICJ0cnVlIjoKICAgICAgICBpZiAiY29kZWNvdiIgaW4gaXRlbS5nZXQoImNvdmVyYWdlIiwgIiIpOgogICAgICAgICAgICAjIE5vdGUgdGhhdCB3ZSBkb24ndCBpbmNsdWRlIC0tY292IGhlcmUgYXMgaWYgaXQncyBwcm92aWRlZCB0byBweXRlc3QgdHdpY2UgaXQgYnJlYWtzIGNvdiByZXBvcnRpbmcuCiAgICAgICAgICAgICMgTG90cyBvZiB1c2VycyBvZiB0aGlzIHNwZWNpZnkgLS1jb3YgaW4gdGhlaXIgdG94LmluaSBzbyBpdCdzIGJlZW4gcmVtb3ZlZCBmb3IgYmFja3dhcmRzIGNvbXBhdGliaWxpdHkuCiAgICAgICAgICAgICMgaHR0cHM6Ly9naXRodWIuY29tL09wZW5Bc3Ryb25vbXkvZ2l0aHViLWFjdGlvbnMtd29ya2Zsb3dzL2lzc3Vlcy8zODMKICAgICAgICAgICAgaXRlbVsicHl0ZXN0X2ZsYWciXSArPSByZiItLWNvdi1yZXBvcnQ9eG1sOiR7e0dJVEhVQl9XT1JLU1BBQ0V9fXtzZXB9Y292ZXJhZ2UueG1sICIKCiAgICAgICAgaWYgaXRlbVsicHl0ZXN0LXJlc3VsdHMtc3VtbWFyeSJdID09ICJ0cnVlIjoKICAgICAgICAgICAgaXRlbVsicHl0ZXN0X2ZsYWciXSArPSByZiItLWp1bml0eG1sICR7e0dJVEhVQl9XT1JLU1BBQ0V9fXtzZXB9cmVzdWx0cy54bWwgIgoKICAgICMgc2V0IGxpYnJhcmllcwogICAgZW52X2xpYnJhcmllcyA9IGVudi5nZXQoImxpYnJhcmllcyIpCiAgICBpZiBpc2luc3RhbmNlKGVudl9saWJyYXJpZXMsIHN0cikgYW5kIGxlbihlbnZfbGlicmFyaWVzLnN0cmlwKCkpID09IDA6CiAgICAgICAgZW52X2xpYnJhcmllcyA9IHt9ICAjIG5vIGxpYnJhcmllcyByZXF1ZXN0ZWQgZm9yIGVudmlyb25tZW50CiAgICBsaWJyYXJpZXMgPSBnbG9iYWxfbGlicmFyaWVzIGlmIGVudl9saWJyYXJpZXMgaXMgTm9uZSBlbHNlIGVudl9saWJyYXJpZXMKICAgIGZvciBtYW5hZ2VyIGluIFsiYnJldyIsICJicmV3X2Nhc2siLCAiYXB0IiwgImNob2NvIl06CiAgICAgICAgaXRlbVtmImxpYnJhcmllc197bWFuYWdlcn0iXSA9ICIgIi5qb2luKGxpYnJhcmllcy5nZXQobWFuYWdlciwgW10pKQoKICAgIGlmIGl0ZW1bImNvbmRhIl06CiAgICAgICAgd2FybmluZ3Mud2FybigiYGNvbmRhYCBwYXJhbWV0ZXIgaXMgZGVwcmVjYXRlZCIpCgogICAgICAgICMgc2V0ICJhdXRvIiBjb25kYSB2YWx1ZQogICAgICAgIGlmIGl0ZW1bImNvbmRhIl0gPT0gImF1dG8iOgogICAgICAgICAgICBpdGVtWyJjb25kYSJdID0gInRydWUiIGlmICJjb25kYSIgaW4gaXRlbVsidG94ZW52Il0gZWxzZSAiZmFsc2UiCgogICAgICAgICMgaW5qZWN0IHRveGRlcHMgZm9yIGNvbmRhCiAgICAgICAgaWYgaXRlbVsiY29uZGEiXSA9PSAidHJ1ZSIgYW5kICJ0b3gtY29uZGEiIG5vdCBpbiBpdGVtWyJ0b3hkZXBzIl0ubG93ZXIoKToKICAgICAgICAgICAgaXRlbVsidG94ZGVwcyJdID0gKCJ0b3gtY29uZGEgIiArIGl0ZW1bInRveGRlcHMiXSkuc3RyaXAoKQoKICAgICMgbWFrZSB0aW1lb3V0LW1pbnV0ZXMgYSBudW1iZXIKICAgIGl0ZW1bInRpbWVvdXQtbWludXRlcyJdID0gaW50KGl0ZW1bInRpbWVvdXQtbWludXRlcyJdKQoKICAgICMgdmVyaWZ5IHZhbHVlcwogICAgYXNzZXJ0IGl0ZW1bInB5dGVzdCJdIGluIHsidHJ1ZSIsICJmYWxzZSJ9CiAgICBhc3NlcnQgaXRlbVsiY29uZGEiXSBpbiB7InRydWUiLCAiZmFsc2UifQogICAgYXNzZXJ0IGl0ZW1bImRpc3BsYXkiXSBpbiB7InRydWUiLCAiZmFsc2UifQoKICAgIHJldHVybiBpdGVtCgoKZGVmIGV4cGFuZF9weXRob25fdmVyc2lvbnModG94ZW52OiBzdHIsIHB5dGhvbl92ZXJzaW9uczogbGlzdFtWZXJzaW9uIHwgc3RyXSkgLT4gbGlzdFsoc3RyLCBzdHIpXToKICAgICIiIgogICAgZXhwYW5kIGBweTMqYCBpbnRvIGBweTMxMWAsIGBweTMxMmAsIGBweTMxM2AsIGV0Yy4gYmFzZWQgb24gY3VycmVudGx5LXN1cHBvcnRlZCBQeXRob24gdmVyc2lvbnMKCiAgICA6cGFyYW0gdmVyc2lvbl9nbG9iOiBjYW4gYmUgYHB5KmAsIGBweTMqYCwgYHB5MzAqYCwgYHB5MzEqYCBldGMuCiAgICAiIiIKCiAgICBweXRob25fdmVyc2lvbnMgPSBbVmVyc2lvbih2ZXJzaW9uKSBmb3IgdmVyc2lvbiBpbiBweXRob25fdmVyc2lvbnNdCgogICAgdG94ZW52X2ZhY3RvcnMgPSB0b3hlbnYuc3BsaXQoIi0iKQogICAgcHlfdmVyc2lvbl9nbG9iID0gdG94ZW52X2ZhY3RvcnNbMF0KICAgIGlmIG5vdCBweV92ZXJzaW9uX2dsb2Iuc3RhcnRzd2l0aCgicHkiKToKICAgICAgICByYWlzZSBWYWx1ZUVycm9yKAogICAgICAgICAgICBmJ2lucHV0ICJ7cHlfdmVyc2lvbl9nbG9ifSIgaXMgbm90IGEgUHl0aG9uIHZlcnNpb24gVG94IGZhY3RvciAobXVzdCBzdGFydCB3aXRoIGBweWApJwogICAgICAgICkKCiAgICBpZiAiKiIgbm90IGluIHB5X3ZlcnNpb25fZ2xvYjoKICAgICAgICByZXR1cm4gW3B5X3ZlcnNpb25fZ2xvYl0KCiAgICBpZiBub3QgcHlfdmVyc2lvbl9nbG9iLmVuZHN3aXRoKCIqIik6CiAgICAgICAgcmFpc2UgTm90SW1wbGVtZW50ZWRFcnJvcigKICAgICAgICAgICAgIlB5dGhvbiB2ZXJzaW9uIGdsb2IgbXVzdCBlbmQgd2l0aCBhIGAqYDsgc3VmZml4ZXMgc3VjaCBhcyBgdGAgYXJlIG5vdCB5ZXQgc3VwcG9ydGVkIgogICAgICAgICkKCiAgICBtYWpvcl92ZXJzaW9uID0gcHlfdmVyc2lvbl9nbG9iWzJdCiAgICBpZiBtYWpvcl92ZXJzaW9uICE9ICIqIjoKICAgICAgICBweXRob25fdmVyc2lvbnMgPSBbCiAgICAgICAgICAgIHB5dGhvbl92ZXJzaW9uCiAgICAgICAgICAgIGZvciBweXRob25fdmVyc2lvbiBpbiBweXRob25fdmVyc2lvbnMKICAgICAgICAgICAgaWYgcHl0aG9uX3ZlcnNpb24ubWFqb3IgPT0gaW50KG1ham9yX3ZlcnNpb24pCiAgICAgICAgXQoKICAgICAgICBtaW5vcl92ZXJzaW9uID0gcHlfdmVyc2lvbl9nbG9iWzM6XQogICAgICAgIGlmIG1pbm9yX3ZlcnNpb25fc3BlY2lmaWVyIDo9IG1pbm9yX3ZlcnNpb24uc3BsaXQoIioiKVswXSAhPSAiKiI6CiAgICAgICAgICAgIG1pbm9yX3ZlcnNpb25fYmFzZSA9IGludChtaW5vcl92ZXJzaW9uX3NwZWNpZmllcikgKiAxMAogICAgICAgICAgICBweXRob25fdmVyc2lvbnMgPSBbCiAgICAgICAgICAgICAgICBweXRob25fdmVyc2lvbgogICAgICAgICAgICAgICAgZm9yIHB5dGhvbl92ZXJzaW9uIGluIHB5dGhvbl92ZXJzaW9ucwogICAgICAgICAgICAgICAgaWYgbWlub3JfdmVyc2lvbl9iYXNlIDw9IHB5dGhvbl92ZXJzaW9uLm1pbm9yIDwgbWlub3JfdmVyc2lvbl9iYXNlICsgMTAKICAgICAgICAgICAgXQoKICAgIHJldHVybiBbCiAgICAgICAgKAogICAgICAgICAgICBmInB5e3B5dGhvbl92ZXJzaW9uLm1ham9yfXtweXRob25fdmVyc2lvbi5taW5vcn0iCiAgICAgICAgICAgICsgKGYiLXsnLScuam9pbih0b3hlbnZfZmFjdG9yc1sxOl0pfSIgaWYgbGVuKHRveGVudl9mYWN0b3JzKSA+IDEgZWxzZSAiIiksCiAgICAgICAgICAgIHN0cihweXRob25fdmVyc2lvbiksCiAgICAgICAgKQogICAgICAgIGZvciBweXRob25fdmVyc2lvbiBpbiBweXRob25fdmVyc2lvbnMKICAgIF0KCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgbG9hZF90b3hfdGFyZ2V0cygpCg== - run: cat tox_matrix.py + - if: contains(inputs.envs, '*') + id: supported-pythons + uses: zacharyburnett/supported-pythons@1f2cfc00e73631d5ed7f01a6c7b0d2744ddc310c # 2.0.2 + with: + package: ${{ job.workflow_repository }} + package-ref: ${{ job.workflow_sha }} - id: set-outputs run: | # zizmor: ignore[template-injection] uv run tox_matrix.py \ - --envs "${{ !inputs.fill && inputs.envs || format('{0}\n{1}', inputs.envs, steps.supported-pythons.outputs.envs) }}" \ + --envs "${{ inputs.envs }}" \ --libraries "${{ inputs.libraries }}" \ --posargs "${{ inputs.posargs }}" --toxdeps "${{ inputs.toxdeps }}" \ --toxargs "${{ inputs.toxargs }}" --pytest "${{ inputs.pytest }}" \ @@ -192,7 +174,8 @@ jobs: --artifact-include-hidden-files "${{ inputs.artifact-include-hidden-files }}" \ --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 }}" + --timeout-minutes "${{ inputs.timeout-minutes }}" \ + --supported-pythons '${{ contains(inputs.envs, '*') && steps.supported-pythons.outputs.versions || '["3"]' }}' shell: sh outputs: matrix: ${{ steps.set-outputs.outputs.matrix }} diff --git a/docs/source/tox.rst b/docs/source/tox.rst index 73e1340..25a3270 100644 --- a/docs/source/tox.rst +++ b/docs/source/tox.rst @@ -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 ^^^^^^^^^ @@ -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 ~~~~~~~ diff --git a/tools/supported_pythons.py b/tools/supported_pythons.py deleted file mode 100644 index b01b5a2..0000000 --- a/tools/supported_pythons.py +++ /dev/null @@ -1,123 +0,0 @@ -# /// script -# requires-python = ">=3.12" -# dependencies = [ -# "click==8.2.1", -# "packaging==25.0", -# "requests==2.32.5", -# "tomli==2.4.0", -# ] -# /// -import os -import warnings -from pathlib import Path - -import click -import requests -import tomli -from packaging.specifiers import SpecifierSet -from packaging.version import Version - - -@click.command() -@click.option("--package-source", default=None) -@click.option("--factors", default=None) -@click.option("--no-eoas", is_flag=True, default=False) -@click.option("--platforms", default=None) -def supported_python_envs_block( - package_source: Path = None, - factors: list[str] = None, - no_eoas: bool = False, - platforms: list[str] = None, -): - """enumerate toxenvs for each Python version supported by package""" - - if platforms is None: - platforms = ["linux"] - elif isinstance(platforms, str): - platforms = platforms.split(",") - - toxenvs = supported_python_toxenvs(package_source, factors, no_eoas) - envs_block = "\\n".join( - f"- {platform}: {toxenv}" for platform in platforms for toxenv in toxenvs - ) - - print(envs_block) - with open(os.environ["GITHUB_OUTPUT"], "a") as f: - f.write(f"envs={envs_block}\n") - - -def supported_python_toxenvs( - package_source: Path = None, - factors: list[str] = None, - no_eoas: bool = False, -) -> list[str]: - if isinstance(factors, str): - factors = factors.split(",") - - return [ - f"py{str(python_version).replace('.', '')}{'-' + '-'.join(factors) if factors is not None and len(factors) > 0 else ''}" - for python_version in supported_pythons(package_source, no_eoas=no_eoas) - ] - - -def supported_pythons( - package_source: Path = None, - no_eoas: bool = False, -) -> list[Version]: - current_python_versions = current_pythons(no_eoas=no_eoas) - - if not package_source: - supported_versions = current_python_versions - else: - try: - pyproject_toml_filename = Path(package_source) / "pyproject.toml" - if pyproject_toml_filename.exists(): - with open(pyproject_toml_filename, "rb") as pyproject_toml_file: - pyproject_toml = tomli.load(pyproject_toml_file) - if "project" in pyproject_toml: - project_metadata = pyproject_toml["project"] - if "requires-python" in project_metadata: - python_version_requirements = SpecifierSet( - project_metadata["requires-python"] - ) - else: - raise KeyError( - "`project.requires-python` not found in `pyproject.toml`; ensure your package conforms to PEP621" - ) - else: - raise KeyError( - "`project` not found in `pyproject.toml`; ensure your package conforms to PEP621" - ) - else: - raise FileNotFoundError( - "could not find `pyproject.toml` in the provided package source; ensure your package conforms to PEP621" - ) - - supported_versions = [ - python_version - for python_version in current_python_versions - if python_version in python_version_requirements - ] - except (KeyError, TypeError, FileNotFoundError) as error: - warnings.warn(str(error)) - warnings.warn("falling back to current Python versions...") - supported_versions = current_python_versions - - return supported_versions - - -def current_pythons(no_eoas: bool = False) -> list[Version]: - url = "https://endoflife.date/api/v1/products/python" - response = requests.get("https://endoflife.date/api/v1/products/python") - if response.status_code == 200: - return [ - Version(python_version["name"]) - for python_version in response.json()["result"]["releases"] - if not python_version["isEoas" if no_eoas else "isEol"] - ] - else: - raise ValueError(f"request to {url} returned status code {response.status_code}") - - -if __name__ == "__main__": - supported_python_envs_block() diff --git a/tools/tox_matrix.py b/tools/tox_matrix.py index 9ceed1a..0aace64 100644 --- a/tools/tox_matrix.py +++ b/tools/tox_matrix.py @@ -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() @@ -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, @@ -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)) @@ -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: @@ -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 " @@ -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() diff --git a/update_scripts_in_yml.py b/update_scripts_in_yml.py index fcc8c39..ec45756 100755 --- a/update_scripts_in_yml.py +++ b/update_scripts_in_yml.py @@ -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')