diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 29e0ad23363..343cbb11c58 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -38,24 +38,13 @@ jobs: run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update sudo DEBIAN_FRONTEND=noninteractive apt-get install $(build/bin/sage-get-system-packages debian _bootstrap _prereq) - - name: make dist (--disable-download-from-upstream-url) - id: make_dist - run: | - ./bootstrap -D && ./configure --disable-download-from-upstream-url && make dist - env: - MAKE: make -j8 - - name: make download (--disable-download-from-upstream-url) - id: make_download - run: | - make -k download DOWNLOAD_PACKAGES=":all: --no-file huge" - env: - MAKE: make -j8 - name: Reconfigure with --enable-download-from-upstream-url - if: (success() || failure()) && (steps.make_dist.outcome != 'success' || steps.make_download.outcome != 'success') run: | + make configure ./configure - name: make dist (--enable-download-from-upstream-url) - if: (success() || failure()) && steps.make_dist.outcome != 'success' + id: make_dist + if: success() || failure() run: | make dist env: diff --git a/build/make/Makefile.in b/build/make/Makefile.in index ff7ed18135e..6f15442ac5e 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -280,7 +280,7 @@ all-sage-docs: $(SAGE_DOCS_INSTALLED_PACKAGE_INSTS) $(SAGE_DOCS_UNINSTALLED_PAC # Download all packages which should be inside an sdist tarball (the -B # option to make forces all targets to be built unconditionally) download-for-sdist: - +env SAGE_INSTALL_FETCH_ONLY=yes $(MAKE_REC) -B SAGERUNTIME= \ + +env SAGE_INSTALL_FETCH_ONLY=yes SAGE_DOWNLOAD_ALL_WHEELS=yes $(MAKE_REC) -B SAGERUNTIME= \ $(SDIST_PACKAGES) # TOOLCHAIN consists of dependencies determined by configure. diff --git a/build/pkgs/jsonschema/dependencies b/build/pkgs/jsonschema/dependencies index 8c7c4532c8d..3392c6d7170 100644 --- a/build/pkgs/jsonschema/dependencies +++ b/build/pkgs/jsonschema/dependencies @@ -1,4 +1,4 @@ -jsonschema_specifications pyrsistent attrs fqdn isoduration jsonpointer uri_template webcolors | $(PYTHON_TOOLCHAIN) $(PYTHON) +jsonschema_specifications pyrsistent rpds_py attrs fqdn isoduration jsonpointer uri_template webcolors | $(PYTHON_TOOLCHAIN) $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/rpds_py/SPKG.rst b/build/pkgs/rpds_py/SPKG.rst new file mode 100644 index 00000000000..aef5d3c2ead --- /dev/null +++ b/build/pkgs/rpds_py/SPKG.rst @@ -0,0 +1,59 @@ +rpds_py: Python bindings to Rust's persistent data structures +============================================================== + +Description +----------- + +Python bindings to the Rust rpds crate for persistent data structures. + +rpds-py provides efficient, immutable data structures including: + +* ``HashTrieMap`` - Persistent hash map +* ``HashTrieSet`` - Persistent hash set +* ``List`` - Persistent list with efficient operations + +These data structures are backed by Rust implementations for high performance +while maintaining a Pythonic API. They are particularly useful for functional +programming patterns and situations requiring immutable, persistent collections. + +The library is used by projects like the referencing library (part of the +Python JSON Schema ecosystem) as a faster alternative to pyrsistent. + +License +------- + +MIT License + +Upstream Contact +---------------- + +- Author: Julian Berman +- Home page: https://github.com/crate-py/rpds +- PyPI: https://pypi.org/project/rpds-py/ +- Documentation: https://rpds.readthedocs.io/ +- Upstream Rust crate: https://github.com/orium/rpds + +Dependencies +------------ + +Python (>= 3.10) + +Build dependencies: Rust toolchain (automatically handled by pip when +installing from source) + +Special Notes +------------- + +This package provides platform-specific binary wheels for multiple Python +versions and platforms: + +* Python 3.11, 3.12, 3.13, 3.14 (including free-threaded 3.13t and 3.14t) +* Linux (x86_64, aarch64, musllinux) +* macOS (x86_64, arm64) +* Windows (win32, win_amd64, win_arm64) + +The Sage build system automatically selects and downloads the appropriate +wheel for your platform and Python version using the packaging library's compatibility tags. + +When building from source, a Rust toolchain is required as rpds-py contains +Rust extensions for performance. diff --git a/build/pkgs/rpds_py/checksums.ini b/build/pkgs/rpds_py/checksums.ini new file mode 100644 index 00000000000..6ef643a3263 --- /dev/null +++ b/build/pkgs/rpds_py/checksums.ini @@ -0,0 +1,208 @@ +tarball=rpds_py-0.28.0-cp311-cp311-macosx_10_12_x86_64.whl +sha256=03065002fd2e287725d95fbc69688e0c6daf6c6314ba38bdbaa3895418e09296 +upstream_url=https://files.pythonhosted.org/packages/a6/34/058d0db5471c6be7bef82487ad5021ff8d1d1d27794be8730aad938649cf/rpds_py-0.28.0-cp311-cp311-macosx_10_12_x86_64.whl + +tarball=rpds_py-0.28.0-cp311-cp311-macosx_11_0_arm64.whl +sha256=28ea02215f262b6d078daec0b45344c89e161eab9526b0d898221d96fdda5f27 +upstream_url=https://files.pythonhosted.org/packages/5d/67/9503f0ec8c055a0782880f300c50a2b8e5e72eb1f94dfc2053da527444dd/rpds_py-0.28.0-cp311-cp311-macosx_11_0_arm64.whl + +tarball=rpds_py-0.28.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl +sha256=25dbade8fbf30bcc551cb352376c0ad64b067e4fc56f90e22ba70c3ce205988c +upstream_url=https://files.pythonhosted.org/packages/68/2e/94223ee9b32332a41d75b6f94b37b4ce3e93878a556fc5f152cbd856a81f/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + +tarball=rpds_py-0.28.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +sha256=ac9f83e7b326a3f9ec3ef84cda98fb0a74c7159f33e692032233046e7fd15da2 +upstream_url=https://files.pythonhosted.org/packages/1f/53/14e37ce83202c632c89b0691185dca9532288ff9d390eacae3d2ff771bae/rpds_py-0.28.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + +tarball=rpds_py-0.28.0-cp311-cp311-musllinux_1_2_aarch64.whl +sha256=c9a40040aa388b037eb39416710fbcce9443498d2eaab0b9b45ae988b53f5c67 +upstream_url=https://files.pythonhosted.org/packages/dd/f5/e1cec473d4bde6df1fd3738be8e82d64dd0600868e76e92dfeaebbc2d18f/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_aarch64.whl + +tarball=rpds_py-0.28.0-cp311-cp311-musllinux_1_2_x86_64.whl +sha256=1571ae4292649100d743b26d5f9c63503bb1fedf538a8f29a98dce2d5ba6b4e6 +upstream_url=https://files.pythonhosted.org/packages/9c/9c/ffc6e9218cd1eb5c2c7dbd276c87cd10e8c2232c456b554169eb363381df/rpds_py-0.28.0-cp311-cp311-musllinux_1_2_x86_64.whl + +tarball=rpds_py-0.28.0-cp311-cp311-win32.whl +sha256=5cfa9af45e7c1140af7321fa0bef25b386ee9faa8928c80dc3a5360971a29e8c +upstream_url=https://files.pythonhosted.org/packages/5f/50/da8b6d33803a94df0149345ee33e5d91ed4d25fc6517de6a25587eae4133/rpds_py-0.28.0-cp311-cp311-win32.whl + +tarball=rpds_py-0.28.0-cp311-cp311-win_amd64.whl +sha256=dd8d86b5d29d1b74100982424ba53e56033dc47720a6de9ba0259cf81d7cecaa +upstream_url=https://files.pythonhosted.org/packages/12/fd/b0f48c4c320ee24c8c20df8b44acffb7353991ddf688af01eef5f93d7018/rpds_py-0.28.0-cp311-cp311-win_amd64.whl + +tarball=rpds_py-0.28.0-cp311-cp311-win_arm64.whl +sha256=4e27d3a5709cc2b3e013bf93679a849213c79ae0573f9b894b284b55e729e120 +upstream_url=https://files.pythonhosted.org/packages/b4/21/c8e77a2ac66e2ec4e21f18a04b4e9a0417ecf8e61b5eaeaa9360a91713b4/rpds_py-0.28.0-cp311-cp311-win_arm64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-macosx_10_12_x86_64.whl +sha256=6b4f28583a4f247ff60cd7bdda83db8c3f5b05a7a82ff20dd4b078571747708f +upstream_url=https://files.pythonhosted.org/packages/b8/5c/6c3936495003875fe7b14f90ea812841a08fca50ab26bd840e924097d9c8/rpds_py-0.28.0-cp312-cp312-macosx_10_12_x86_64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-macosx_11_0_arm64.whl +sha256=d678e91b610c29c4b3d52a2c148b641df2b4676ffe47c59f6388d58b99cdc424 +upstream_url=https://files.pythonhosted.org/packages/56/f9/a0f1ca194c50aa29895b442771f036a25b6c41a35e4f35b1a0ea713bedae/rpds_py-0.28.0-cp312-cp312-macosx_11_0_arm64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl +sha256=e819e0e37a44a78e1383bf1970076e2ccc4dc8c2bbaa2f9bd1dc987e9afff628 +upstream_url=https://files.pythonhosted.org/packages/18/ea/42d243d3a586beb72c77fa5def0487daf827210069a95f36328e869599ea/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +sha256=8455933b4bcd6e83fde3fefc987a023389c4b13f9a58c8d23e4b3f6d13f78c84 +upstream_url=https://files.pythonhosted.org/packages/3e/cd/49ce51767b879cde77e7ad9fae164ea15dce3616fe591d9ea1df51152706/rpds_py-0.28.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-musllinux_1_2_aarch64.whl +sha256=735f8495a13159ce6a0d533f01e8674cec0c57038c920495f87dcb20b3ddb48a +upstream_url=https://files.pythonhosted.org/packages/ff/6a/841337980ea253ec797eb084665436007a1aad0faac1ba097fb906c5f69c/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_aarch64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-musllinux_1_2_x86_64.whl +sha256=2374e16cc9131022e7d9a8f8d65d261d9ba55048c78f3b6e017971a4f5e6353c +upstream_url=https://files.pythonhosted.org/packages/b6/ee/44d024b4843f8386a4eeaa4c171b3d31d55f7177c415545fd1a24c249b5d/rpds_py-0.28.0-cp312-cp312-musllinux_1_2_x86_64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-win32.whl +sha256=d15431e334fba488b081d47f30f091e5d03c18527c325386091f31718952fe08 +upstream_url=https://files.pythonhosted.org/packages/7d/89/33e675dccff11a06d4d85dbb4d1865f878d5020cbb69b2c1e7b2d3f82562/rpds_py-0.28.0-cp312-cp312-win32.whl + +tarball=rpds_py-0.28.0-cp312-cp312-win_amd64.whl +sha256=a410542d61fc54710f750d3764380b53bf09e8c4edbf2f9141a82aa774a04f7c +upstream_url=https://files.pythonhosted.org/packages/af/36/45f6ebb3210887e8ee6dbf1bc710ae8400bb417ce165aaf3024b8360d999/rpds_py-0.28.0-cp312-cp312-win_amd64.whl + +tarball=rpds_py-0.28.0-cp312-cp312-win_arm64.whl +sha256=1f0cfd1c69e2d14f8c892b893997fa9a60d890a0c8a603e88dca4955f26d1edd +upstream_url=https://files.pythonhosted.org/packages/57/91/f3fb250d7e73de71080f9a221d19bd6a1c1eb0d12a1ea26513f6c1052ad6/rpds_py-0.28.0-cp312-cp312-win_arm64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl +sha256=e9e184408a0297086f880556b6168fa927d677716f83d3472ea333b42171ee3b +upstream_url=https://files.pythonhosted.org/packages/d3/03/ce566d92611dfac0085c2f4b048cd53ed7c274a5c05974b882a908d540a2/rpds_py-0.28.0-cp313-cp313-macosx_10_12_x86_64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-macosx_11_0_arm64.whl +sha256=edd267266a9b0448f33dc465a97cfc5d467594b600fe28e7fa2f36450e03053a +upstream_url=https://files.pythonhosted.org/packages/00/34/1c61da1b25592b86fd285bd7bd8422f4c9d748a7373b46126f9ae792a004/rpds_py-0.28.0-cp313-cp313-macosx_11_0_arm64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl +sha256=85beb8b3f45e4e32f6802fb6cd6b17f615ef6c6a52f265371fb916fae02814aa +upstream_url=https://files.pythonhosted.org/packages/fc/00/ed1e28616848c61c493a067779633ebf4b569eccaacf9ccbdc0e7cba2b9d/rpds_py-0.28.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +sha256=b8e1e9be4fa6305a16be628959188e4fd5cd6f1b0e724d63c6d8b2a8adf74ea6 +upstream_url=https://files.pythonhosted.org/packages/ca/ee/a324d3198da151820a326c1f988caaa4f37fc27955148a76fff7a2d787a9/rpds_py-0.28.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-musllinux_1_2_aarch64.whl +sha256=5b43c6a3726efd50f18d8120ec0551241c38785b68952d240c45ea553912ac41 +upstream_url=https://files.pythonhosted.org/packages/68/61/7c195b30d57f1b8d5970f600efee72a4fad79ec829057972e13a0370fd24/rpds_py-0.28.0-cp313-cp313-musllinux_1_2_aarch64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-musllinux_1_2_x86_64.whl +sha256=7a52a5169c664dfb495882adc75c304ae1d50df552fbd68e100fdc719dee4ff9 +upstream_url=https://files.pythonhosted.org/packages/66/df/62fc783781a121e77fee9a21ead0a926f1b652280a33f5956a5e7833ed30/rpds_py-0.28.0-cp313-cp313-musllinux_1_2_x86_64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-win32.whl +sha256=2e42456917b6687215b3e606ab46aa6bca040c77af7df9a08a6dcfe8a4d10ca5 +upstream_url=https://files.pythonhosted.org/packages/84/85/d34366e335140a4837902d3dea89b51f087bd6a63c993ebdff59e93ee61d/rpds_py-0.28.0-cp313-cp313-win32.whl + +tarball=rpds_py-0.28.0-cp313-cp313-win_amd64.whl +sha256=e0a0311caedc8069d68fc2bf4c9019b58a2d5ce3cd7cb656c845f1615b577e1e +upstream_url=https://files.pythonhosted.org/packages/3c/1c/f25a3f3752ad7601476e3eff395fe075e0f7813fbb9862bd67c82440e880/rpds_py-0.28.0-cp313-cp313-win_amd64.whl + +tarball=rpds_py-0.28.0-cp313-cp313-win_arm64.whl +sha256=04c1b207ab8b581108801528d59ad80aa83bb170b35b0ddffb29c20e411acdc1 +upstream_url=https://files.pythonhosted.org/packages/e0/d6/5f39b42b99615b5bc2f36ab90423ea404830bdfee1c706820943e9a645eb/rpds_py-0.28.0-cp313-cp313-win_arm64.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-macosx_10_12_x86_64.whl +sha256=f296ea3054e11fc58ad42e850e8b75c62d9a93a9f981ad04b2e5ae7d2186ff9c +upstream_url=https://files.pythonhosted.org/packages/5c/8b/0c69b72d1cee20a63db534be0df271effe715ef6c744fdf1ff23bb2b0b1c/rpds_py-0.28.0-cp313-cp313t-macosx_10_12_x86_64.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-macosx_11_0_arm64.whl +sha256=5a7306c19b19005ad98468fcefeb7100b19c79fc23a5f24a12e06d91181193fa +upstream_url=https://files.pythonhosted.org/packages/f7/6d/0c2ee773cfb55c31a8514d2cece856dd299170a49babd50dcffb15ddc749/rpds_py-0.28.0-cp313-cp313t-macosx_11_0_arm64.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl +sha256=e5d9b86aa501fed9862a443c5c3116f6ead8bc9296185f369277c42542bd646b +upstream_url=https://files.pythonhosted.org/packages/e2/1c/22513ab25a27ea205144414724743e305e8153e6abe81833b5e678650f5a/rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +sha256=8d252db6b1a78d0a3928b6190156042d54c93660ce4d98290d7b16b5296fb7cc +upstream_url=https://files.pythonhosted.org/packages/b1/2c/f30892f9e54bd02e5faca3f6a26d6933c51055e67d54818af90abed9748e/rpds_py-0.28.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_aarch64.whl +sha256=8aa23b6f0fc59b85b4c7d89ba2965af274346f738e8d9fc2455763602e62fd5f +upstream_url=https://files.pythonhosted.org/packages/42/b5/71e8777ac55e6af1f4f1c05b47542a1eaa6c33c1cf0d300dca6a1c6e159a/rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_aarch64.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_x86_64.whl +sha256=bcf1d210dfee61a6c86551d67ee1031899c0fdbae88b2d44a569995d43797712 +upstream_url=https://files.pythonhosted.org/packages/4a/d4/407ad9960ca7856d7b25c96dcbe019270b5ffdd83a561787bc682c797086/rpds_py-0.28.0-cp313-cp313t-musllinux_1_2_x86_64.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-win32.whl +sha256=3aa4dc0fdab4a7029ac63959a3ccf4ed605fee048ba67ce89ca3168da34a1342 +upstream_url=https://files.pythonhosted.org/packages/51/31/2f46fe0efcac23fbf5797c6b6b7e1c76f7d60773e525cb65fcbc582ee0f2/rpds_py-0.28.0-cp313-cp313t-win32.whl + +tarball=rpds_py-0.28.0-cp313-cp313t-win_amd64.whl +sha256=7b7d9d83c942855e4fdcfa75d4f96f6b9e272d42fffcb72cd4bb2577db2e2907 +upstream_url=https://files.pythonhosted.org/packages/92/e4/15947bda33cbedfc134490a41841ab8870a72a867a03d4969d886f6594a2/rpds_py-0.28.0-cp313-cp313t-win_amd64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl +sha256=dcdcb890b3ada98a03f9f2bb108489cdc7580176cb73b4f2d789e9a1dac1d472 +upstream_url=https://files.pythonhosted.org/packages/08/47/ffe8cd7a6a02833b10623bf765fbb57ce977e9a4318ca0e8cf97e9c3d2b3/rpds_py-0.28.0-cp314-cp314-macosx_10_12_x86_64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-macosx_11_0_arm64.whl +sha256=f274f56a926ba2dc02976ca5b11c32855cbd5925534e57cfe1fda64e04d1add2 +upstream_url=https://files.pythonhosted.org/packages/f9/9f/890f36cbd83a58491d0d91ae0db1702639edb33fb48eeb356f80ecc6b000/rpds_py-0.28.0-cp314-cp314-macosx_11_0_arm64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl +sha256=4fe0438ac4a29a520ea94c8c7f1754cdd8feb1bc490dfda1bfd990072363d527 +upstream_url=https://files.pythonhosted.org/packages/09/e3/921eb109f682aa24fb76207698fbbcf9418738f35a40c21652c29053f23d/rpds_py-0.28.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +sha256=5ae8ee156d6b586e4292491e885d41483136ab994e719a13458055bec14cf370 +upstream_url=https://files.pythonhosted.org/packages/da/37/e84283b9e897e3adc46b4c88bb3f6ec92a43bd4d2f7ef5b13459963b2e9c/rpds_py-0.28.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-musllinux_1_2_aarch64.whl +sha256=6796079e5d24fdaba6d49bda28e2c47347e89834678f2bc2c1b4fc1489c0fb01 +upstream_url=https://files.pythonhosted.org/packages/74/ae/cab05ff08dfcc052afc73dcb38cbc765ffc86f94e966f3924cd17492293c/rpds_py-0.28.0-cp314-cp314-musllinux_1_2_aarch64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-musllinux_1_2_x86_64.whl +sha256=bbdc5640900a7dbf9dd707fe6388972f5bbd883633eb68b76591044cfe346f7e +upstream_url=https://files.pythonhosted.org/packages/ab/12/85a57d7a5855a3b188d024b099fd09c90db55d32a03626d0ed16352413ff/rpds_py-0.28.0-cp314-cp314-musllinux_1_2_x86_64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-win32.whl +sha256=adc8aa88486857d2b35d75f0640b949759f79dc105f50aa2c27816b2e0dd749f +upstream_url=https://files.pythonhosted.org/packages/6c/65/10643fb50179509150eb94d558e8837c57ca8b9adc04bd07b98e57b48f8c/rpds_py-0.28.0-cp314-cp314-win32.whl + +tarball=rpds_py-0.28.0-cp314-cp314-win_amd64.whl +sha256=66e6fa8e075b58946e76a78e69e1a124a21d9a48a5b4766d15ba5b06869d1fa1 +upstream_url=https://files.pythonhosted.org/packages/b4/84/0c11fe4d9aaea784ff4652499e365963222481ac647bcd0251c88af646eb/rpds_py-0.28.0-cp314-cp314-win_amd64.whl + +tarball=rpds_py-0.28.0-cp314-cp314-win_arm64.whl +sha256=a6fe887c2c5c59413353b7c0caff25d0e566623501ccfff88957fa438a69377d +upstream_url=https://files.pythonhosted.org/packages/0f/e0/3ab3b86ded7bb18478392dc3e835f7b754cd446f62f3fc96f4fe2aca78f6/rpds_py-0.28.0-cp314-cp314-win_arm64.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-macosx_10_12_x86_64.whl +sha256=7a69df082db13c7070f7b8b1f155fa9e687f1d6aefb7b0e3f7231653b79a067b +upstream_url=https://files.pythonhosted.org/packages/51/ec/d5681bb425226c3501eab50fc30e9d275de20c131869322c8a1729c7b61c/rpds_py-0.28.0-cp314-cp314t-macosx_10_12_x86_64.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-macosx_11_0_arm64.whl +sha256=b1cde22f2c30ebb049a9e74c5374994157b9b70a16147d332f89c99c5960737a +upstream_url=https://files.pythonhosted.org/packages/be/ec/568c5e689e1cfb1ea8b875cffea3649260955f677fdd7ddc6176902d04cd/rpds_py-0.28.0-cp314-cp314t-macosx_11_0_arm64.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl +sha256=5338742f6ba7a51012ea470bd4dc600a8c713c0c72adaa0977a1b1f4327d6592 +upstream_url=https://files.pythonhosted.org/packages/32/fe/51ada84d1d2a1d9d8f2c902cfddd0133b4a5eb543196ab5161d1c07ed2ad/rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +sha256=3114f4db69ac5a1f32e7e4d1cbbe7c8f9cf8217f78e6e002cedf2d54c2a548ed +upstream_url=https://files.pythonhosted.org/packages/ab/81/5d98cc0329bbb911ccecd0b9e19fbf7f3a5de8094b4cda5e71013b2dd77e/rpds_py-0.28.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_aarch64.whl +sha256=1e8ee6413cfc677ce8898d9cde18cc3a60fc2ba756b0dec5b71eb6eb21c49fa1 +upstream_url=https://files.pythonhosted.org/packages/39/4c/f08283a82ac141331a83a40652830edd3a4a92c34e07e2bbe00baaea2f5f/rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_aarch64.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_x86_64.whl +sha256=b670c30fd87a6aec281c3c9896d3bae4b205fd75d79d06dc87c2503717e46092 +upstream_url=https://files.pythonhosted.org/packages/d3/0c/5bafdd8ccf6aa9d3bfc630cfece457ff5b581af24f46a9f3590f790e3df2/rpds_py-0.28.0-cp314-cp314t-musllinux_1_2_x86_64.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-win32.whl +sha256=8014045a15b4d2b3476f0a287fcc93d4f823472d7d1308d47884ecac9e612be3 +upstream_url=https://files.pythonhosted.org/packages/2c/37/dcc5d8397caa924988693519069d0beea077a866128719351a4ad95e82fc/rpds_py-0.28.0-cp314-cp314t-win32.whl + +tarball=rpds_py-0.28.0-cp314-cp314t-win_amd64.whl +sha256=7a4e59c90d9c27c561eb3160323634a9ff50b04e4f7820600a2beb0ac90db578 +upstream_url=https://files.pythonhosted.org/packages/d7/69/64d43b21a10d72b45939a28961216baeb721cc2a430f5f7c3bfa21659a53/rpds_py-0.28.0-cp314-cp314t-win_amd64.whl + diff --git a/build/pkgs/rpds_py/dependencies b/build/pkgs/rpds_py/dependencies new file mode 100644 index 00000000000..a05443f5c4f --- /dev/null +++ b/build/pkgs/rpds_py/dependencies @@ -0,0 +1 @@ +pip packaging diff --git a/build/pkgs/rpds_py/package-version.txt b/build/pkgs/rpds_py/package-version.txt new file mode 100644 index 00000000000..022a0337400 --- /dev/null +++ b/build/pkgs/rpds_py/package-version.txt @@ -0,0 +1 @@ +0.28.0 \ No newline at end of file diff --git a/build/pkgs/rpds_py/type b/build/pkgs/rpds_py/type new file mode 100644 index 00000000000..aa0bc074b62 --- /dev/null +++ b/build/pkgs/rpds_py/type @@ -0,0 +1 @@ +standard \ No newline at end of file diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py index 2d40d4915df..5c16eba623e 100644 --- a/build/sage_bootstrap/package.py +++ b/build/sage_bootstrap/package.py @@ -241,6 +241,159 @@ def tarball_upstream_url(self): else: return None + @property + def tarballs_info(self): + """ + Return information about all tarballs for this package. + + This supports packages with multiple platform-specific wheels. + + OUTPUT: + + List of dictionaries, each containing: + - 'tarball': tarball filename pattern + - 'sha256': SHA256 checksum + - 'sha1': SHA1 checksum (optional) + - 'upstream_url': upstream URL pattern + """ + return self.__tarballs_info + + def find_tarball_for_platform(self): + """ + Find the appropriate tarball for the current platform. + + For packages with multiple platform-specific wheels, this selects + the one matching the current platform and Python version using + the packaging.tags module to ensure compatibility. + + Properly handles wheel ABI tags: + - cp313-cp313 (CPython 3.13 specific) + - cp313-cp313t (CPython 3.13 free-threaded/nogil) + - cp313-abi3 (stable ABI, forward compatible) + - py3-none-any (universal pure Python wheel) + - pp39-pypy39_pp73 (PyPy wheels) + + INPUT: + + - ``python_version`` -- Python version string (e.g., '3.11'), or None to auto-detect + + OUTPUT: + + Dictionary with tarball info, or None if no suitable tarball found. + The dictionary contains the same fields as tarballs_info entries. + """ + import json + import subprocess + + from sage_bootstrap.env import SAGE_ROOT + + if not self.__tarballs_info: + return None + + # If only one tarball, return it + if len(self.__tarballs_info) == 1: + return self.__tarballs_info[0] + + # Get compatible tags from Sage's Python using packaging.tags + sage_script = os.path.join(SAGE_ROOT, 'sage') + if not os.path.exists(sage_script): + raise RuntimeError('Sage script not found at: {0}'.format(sage_script)) + + try: + # Get all compatible tags from Sage's Python + result = subprocess.run( + [sage_script, '-python', '-c', + 'import packaging.tags; import json; tags = [str(t) for t in packaging.tags.sys_tags()]; print(json.dumps(tags))'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + timeout=10, + cwd=SAGE_ROOT + ) + if result.returncode != 0: + raise RuntimeError('Failed to get compatible tags from sage -python: {0}'.format(result.stderr)) + + compatible_tags = json.loads(result.stdout.strip()) + + except subprocess.TimeoutExpired: + raise RuntimeError('Timeout while querying compatible tags via ./sage -python') + except Exception as e: + raise RuntimeError('Error querying compatible tags via ./sage -python: {0}'.format(str(e))) + + # Convert tags list to a set for fast lookup with priority + # Lower index = higher priority + tag_priority = {tag: idx for idx, tag in enumerate(compatible_tags)} + + # Separate wheels from non-wheel tarballs + wheel_tarballs = [] + source_tarballs = [] + for tarball_info in self.__tarballs_info: + if tarball_info['tarball'].endswith('.whl'): + wheel_tarballs.append(tarball_info) + else: + source_tarballs.append(tarball_info) + + # Batch parse all wheel filenames in a single subprocess call + # This avoids subprocess overhead for packages with many wheels + wheel_tags_map = {} + if wheel_tarballs: + try: + wheel_filenames = [info['tarball'] for info in wheel_tarballs] + python_code = 'import packaging.utils as pu,json,sys;d={};[exec(f"try: d[f]=[str(t)for t in pu.parse_wheel_filename(f)[3]]\\nexcept: d[f]=None",{"f":f,"d":d,"pu":pu})for f in sys.argv[1:]];print(json.dumps(d))' + result = subprocess.run( + [sage_script, '-python', '-c', python_code] + wheel_filenames, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + timeout=30, + cwd=SAGE_ROOT + ) + if result.returncode != 0: + log.warning(f'Failed to parse wheel filenames: {result.stderr}') + else: + wheel_tags_map = json.loads(result.stdout.strip()) + + except subprocess.TimeoutExpired: + log.warning('Timeout while parsing wheel filenames') + except Exception as e: + log.warning(f'Error parsing wheel filenames: {e}') + + # Find the best matching tarball + best_match = None + best_priority = float('inf') + + # Check wheel tarballs first (they have higher priority than source) + for tarball_info in wheel_tarballs: + tarball = tarball_info['tarball'] + wheel_tags_str = wheel_tags_map.get(tarball) + + if wheel_tags_str is None: + log.debug(f'Could not parse wheel filename {tarball}') + continue + + # Check each tag in the wheel (multi-platform wheels have multiple tags) + for wheel_tag_str in wheel_tags_str: + if wheel_tag_str in tag_priority: + priority = tag_priority[wheel_tag_str] + if priority < best_priority: + best_match = tarball_info + best_priority = priority + log.debug(f'Found compatible wheel: {tarball} with tag {wheel_tag_str} (priority: {priority})') + break # Found a match, no need to check other tags for this wheel + + # If no wheel matched, consider source distributions + if best_match is None and source_tarballs: + best_match = source_tarballs[0] + best_priority = len(compatible_tags) + 1000 + + if best_match: + log.debug(f'Selected {best_match["tarball"]} with priority {best_priority}') + return best_match + + # If no match found, return the first one (backward compatibility) + log.warning(f'No exact platform match found for {self.name}, using first tarball') + return self.__tarballs_info[0] + @property def tarball_package(self): """ @@ -507,10 +660,24 @@ def line_count_file(self, filename): def _init_checksum(self): """ Load the checksums from the appropriate ``checksums.ini`` file + + Supports multiple tarballs with format: + tarball=package-VERSION-cp311-cp311-manylinux_2_17_x86_64.whl + sha256=abc123... + upstream_url=https://... + tarball=package-VERSION-cp311-cp311-macosx_11_0_arm64.whl + sha256=def456... + upstream_url=https://... """ checksums_ini = os.path.join(self.path, 'checksums.ini') assignment = re.compile('(?P[a-zA-Z0-9_]*)=(?P.*)') - result = dict() + + # Store all entries, supporting multiple values for tarball, sha256, upstream_url + tarballs = [] + sha256s = [] + sha1s = [] + upstream_urls = [] + try: with open(checksums_ini, 'rt') as f: for line in f.readlines(): @@ -518,13 +685,36 @@ def _init_checksum(self): if match is None: continue var, value = match.groups() - result[var] = value + + # Collect multiple entries + if var == 'tarball': + tarballs.append(value) + elif var == 'sha256': + sha256s.append(value) + elif var == 'sha1': + sha1s.append(value) + elif var == 'upstream_url': + upstream_urls.append(value) except IOError: pass - self.__sha1 = result.get('sha1', None) - self.__sha256 = result.get('sha256', None) - self.__tarball_pattern = result.get('tarball', None) - self.__tarball_upstream_url_pattern = result.get('upstream_url', None) + + # Store all tarballs info + self.__tarballs_info = [] + for i, tarball in enumerate(tarballs): + info = { + 'tarball': tarball, + 'sha256': sha256s[i] if i < len(sha256s) else None, + 'sha1': sha1s[i] if i < len(sha1s) else None, + 'upstream_url': upstream_urls[i] if i < len(upstream_urls) else None, + } + self.__tarballs_info.append(info) + + # For backward compatibility, set the first tarball as primary + self.__sha1 = sha1s[0] if sha1s else None + self.__sha256 = sha256s[0] if sha256s else None + self.__tarball_pattern = tarballs[0] if tarballs else None + self.__tarball_upstream_url_pattern = upstream_urls[0] if upstream_urls else None + # Name of the directory containing the checksums.ini file self.__tarball_package_name = os.path.realpath(checksums_ini).split(os.sep)[-2] diff --git a/build/sage_bootstrap/tarball.py b/build/sage_bootstrap/tarball.py index 54af50dd8f8..0cfd2a0e9d2 100644 --- a/build/sage_bootstrap/tarball.py +++ b/build/sage_bootstrap/tarball.py @@ -16,6 +16,7 @@ # **************************************************************************** import os + import logging log = logging.getLogger() @@ -40,7 +41,7 @@ class FileNotMirroredError(Exception): class Tarball(object): - def __init__(self, tarball_name, package=None): + def __init__(self, tarball_name, package=None, tarball_info=None): """ A (third-party downloadable) tarball @@ -52,8 +53,13 @@ def __init__(self, tarball_name, package=None): - ``tarball_name`` - string. The full filename (``foo-1.3.tar.bz2``) of a tarball on the Sage mirror network. + - ``package`` - Package object, or None to auto-detect + - ``tarball_info`` - dict with tarball info (for multi-tarball packages) + containing sha256, sha1, upstream_url """ self.__filename = tarball_name + self.__tarball_info = tarball_info + if package is None: self.__package = None for pkg in Package.all(): @@ -65,7 +71,9 @@ def __init__(self, tarball_name, package=None): raise ValueError(error) else: self.__package = package - if package.tarball_filename != tarball_name: + # For multi-tarball packages (with tarball_info), skip the filename check + # since we're selecting a platform-specific wheel + if tarball_info is None and package.tarball_filename != tarball_name: error = 'tarball {0} is not referenced by the {1} package'.format(tarball_name, package.name) log.error(error) raise ValueError(error) @@ -126,18 +134,43 @@ def _compute_sha256(self): def checksum_verifies(self, force_sha256=False): """ Test whether the checksum of the downloaded file is correct. + + Uses tarball_info if available (for multi-tarball packages), + otherwise falls back to package-level checksums. """ - if self.package.sha256: + # Use tarball_info if available (for multi-tarball packages) + if self.__tarball_info: + sha256_expected = self.__tarball_info.get('sha256') + sha1_expected = self.__tarball_info.get('sha1') + else: + sha256_expected = self.package.sha256 + sha1_expected = self.package.sha1 + + if sha256_expected: sha256 = self._compute_sha256() - if sha256 != self.package.sha256: + if sha256 != sha256_expected: + log.error(f'SHA256 mismatch for {self.filename}') + log.error(f'Expected: {sha256_expected}') + log.error(f'Got: {sha256}') return False elif force_sha256: log.warning('sha256 not available for {0}'.format(self.package.name)) return False else: log.warning('sha256 not available for {0}, using sha1'.format(self.package.name)) - sha1 = self._compute_sha1() - return sha1 == self.package.sha1 + if sha1_expected: + sha1 = self._compute_sha1() + if sha1 != sha1_expected: + log.error(f'SHA1 mismatch for {self.filename}') + log.error(f'Expected: {sha1_expected}') + log.error(f'Got: {sha1}') + return False + return True + else: + log.warning('No checksum available for {0}'.format(self.package.name)) + return False + + return True def is_distributable(self): return 'do-not-distribute' not in self.filename @@ -149,10 +182,26 @@ def download(self, allow_upstream=False): If allow_upstream is False and the package cannot be found on the sage mirrors, fall back to downloading it from the upstream URL if the package has one. + + For platform-specific wheels, this method: + 1. Checks for a cached wheel matching the current platform + 2. If cached and checksum valid, uses it + 3. Otherwise, download from upstream """ if not self.filename: raise ValueError('non-normal package does define a tarball, so cannot download') + destination = self.upstream_fqn + + # Check if package has multiple tarballs (multi-platform wheels) + has_multiple_tarballs = len(self.package.tarballs_info) > 1 + + if has_multiple_tarballs: + log.info(f'Package {self.package.name} has {len(self.package.tarballs_info)} platform-specific tarballs') + return self._download_multiple_wheels(allow_upstream) + + # Single tarball case - existing logic + # Check if file already exists and is valid if os.path.isfile(destination): if self.checksum_verifies(): log.info('Using cached file {destination}'.format(destination=destination)) @@ -163,6 +212,8 @@ def download(self, allow_upstream=False): # update the checksum (Issue #23972). log.warning('Invalid checksum; ignoring cached file {destination}' .format(destination=destination)) + + # Traditional download logic for tarballs and platform-independent wheels successful_download = False log.info('Attempting to download package {0} from mirrors'.format(self.filename)) for mirror in MirrorList(): @@ -190,6 +241,109 @@ def download(self, allow_upstream=False): if not self.checksum_verifies(): raise ChecksumError('checksum does not match') + def _download_multiple_wheels(self, allow_upstream=False): + """ + Handle download for packages with multiple platform-specific wheels. + + Strategy: + 1. If SAGE_DOWNLOAD_ALL_WHEELS is set, download all wheels (for make dist) + 2. Otherwise, use find_tarball_for_platform() to get the exact filename for this platform + 3. Check if file is already cached with valid checksum + 4. If not cached, download from mirrors/upstream + """ + download_all = os.environ.get('SAGE_DOWNLOAD_ALL_WHEELS', '').lower() in ['yes', '1', 'true'] + + if download_all: + # Download all wheels for distribution + log.info(f'Downloading all {len(self.package.tarballs_info)} wheels for {self.package.name}') + for tarball_info in self.package.tarballs_info: + self._download_single_wheel(tarball_info, allow_upstream) + # Keep self.__filename pointing to the first one for compatibility + tarball_pattern = self.package.tarballs_info[0]['tarball'] + self.__filename = self.package._substitute_variables(tarball_pattern) + return + + # Find the appropriate tarball for this platform from checksums.ini + tarball_info = self.package.find_tarball_for_platform() + + if not tarball_info: + raise ValueError(f'No suitable tarball found for {self.package.name} on current platform') + + # Download the single platform-specific wheel + self._download_single_wheel(tarball_info, allow_upstream) + + def _download_single_wheel(self, tarball_info, allow_upstream=False): + """ + Download a single wheel given its tarball info. + + INPUT: + - ``tarball_info`` - dict with tarball, sha256, sha1, upstream_url + - ``allow_upstream`` - whether to allow downloading from upstream + """ + # Get the actual filename with version substituted + tarball_pattern = tarball_info['tarball'] + tarball_filename = self.package._substitute_variables(tarball_pattern) + + log.info(f'Selected tarball for platform: {tarball_filename}') + + # Check if file already exists in cache + destination = os.path.join(SAGE_DISTFILES, tarball_filename) + + # Create a temporary Tarball object for this specific wheel + wheel_tarball = Tarball(tarball_filename, + package=self.package, + tarball_info=tarball_info) + + # Check if file already exists and is valid + if os.path.isfile(destination): + if wheel_tarball.checksum_verifies(): + log.info('Using cached file {destination}'.format(destination=destination)) + # Update self to point to the cached wheel + self.__filename = tarball_filename + self.__tarball_info = tarball_info + return + else: + # Garbage in the upstream directory? Ignore it. + # Don't delete it because maybe somebody just forgot to + # update the checksum (Issue #23972). + log.warning('Invalid checksum; ignoring cached file {destination}' + .format(destination=destination)) + + # Download logic for platform-specific wheels + successful_download = False + log.info('Attempting to download package {0} from mirrors'.format(tarball_filename)) + for mirror in MirrorList(): + url = mirror.replace('${SPKG}', self.package.name) + if not url.endswith('/'): + url += '/' + url += tarball_filename + log.info(url) + try: + Download(url, destination).run() + successful_download = True + break + except IOError: + log.debug('File not on mirror') + + if not successful_download: + upstream_url_pattern = tarball_info.get('upstream_url') + url = self.package._substitute_variables(upstream_url_pattern) if upstream_url_pattern else None + if allow_upstream and url: + log.info('Attempting to download from {}'.format(url)) + try: + Download(url, destination).run() + except IOError: + raise FileNotMirroredError('tarball does not exist on mirror network and neither at the upstream URL') + else: + raise FileNotMirroredError('tarball does not exist on mirror network') + + if not wheel_tarball.checksum_verifies(): + raise ChecksumError('checksum does not match') + + # Update self to point to the successfully downloaded wheel + self.__filename = tarball_filename + self.__tarball_info = tarball_info + def save_as(self, destination): """ Save the tarball as a new file diff --git a/configure.ac b/configure.ac index 12c3e99e6d2..e91615f2dc6 100644 --- a/configure.ac +++ b/configure.ac @@ -506,7 +506,7 @@ AC_ARG_ENABLE([cvxopt], AC_ARG_ENABLE([notebook], AS_HELP_STRING([--disable-notebook], [disable build of the Jupyter notebook and related packages]), [ - for pkg in notebook nbconvert beautifulsoup4 sagenb_export nbformat nbclient terminado send2trash prometheus_client mistune pandocfilters bleach defusedxml jsonschema jupyter_jsmol argon2_cffi argon2_cffi_bindings webencodings tinycss2 ipympl soupsieve fastjsonschema anyio arrow async_lru fqdn isoduration json5 jsonpointer jsonschema_specifications jupyter_events jupyter_lsp jupyter_server jupyter_server_terminals jupyterlab jupyterlab_server jupyterlab_pygments jupyterlab_mathjax2 jupyter_sphinx notebook_shim overrides python_json_logger pyyaml referencing rfc3339_validator rfc3986_validator sniffio types_python_dateutil uri_template webcolors websocket_client httpx httpcore h11; do + for pkg in notebook nbconvert beautifulsoup4 sagenb_export nbformat nbclient terminado send2trash prometheus_client mistune pandocfilters bleach defusedxml jsonschema jupyter_jsmol argon2_cffi argon2_cffi_bindings webencodings tinycss2 ipympl soupsieve fastjsonschema anyio arrow async_lru fqdn isoduration json5 jsonpointer jsonschema_specifications jupyter_events jupyter_lsp jupyter_server jupyter_server_terminals jupyterlab jupyterlab_server jupyterlab_pygments jupyterlab_mathjax2 jupyter_sphinx notebook_shim overrides python_json_logger pyyaml referencing rfc3339_validator rfc3986_validator sniffio types_python_dateutil uri_template webcolors websocket_client httpx httpcore h11 rpds_py; do AS_VAR_SET([SAGE_ENABLE_$pkg], [$enableval]) done ])