From 3b70f3e23c83e7f1f9fa220ad78397f4f9be07fe Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Mon, 28 Oct 2024 17:10:22 +1000 Subject: [PATCH] Remove implicit build support (#48) The implicit build support adds a lot of complexity, while also being an inherently bad idea (the local wheel directory support is a much better approach). Closes #6 --- ci-bootstrap-requirements.txt | 12 +- ci-constraints.txt | 86 ++++---- docs/api/venvstacks.stacks.ApplicationEnv.rst | 3 - docs/api/venvstacks.stacks.FrameworkEnv.rst | 3 - .../venvstacks.stacks.PackageIndexConfig.rst | 1 - docs/api/venvstacks.stacks.RuntimeEnv.rst | 3 - docs/requirements.txt | 16 +- pdm.lock | 136 ++++-------- pyproject.toml | 5 +- src/venvstacks/_util.py | 5 +- src/venvstacks/cli.py | 14 -- src/venvstacks/stacks.py | 194 +++--------------- tests/test_cli_invocation.py | 19 -- tests/test_index_config.py | 44 +--- tests/test_minimal_project.py | 176 +--------------- 15 files changed, 135 insertions(+), 582 deletions(-) diff --git a/ci-bootstrap-requirements.txt b/ci-bootstrap-requirements.txt index e39e3a3..3efc673 100644 --- a/ci-bootstrap-requirements.txt +++ b/ci-bootstrap-requirements.txt @@ -87,9 +87,9 @@ python-dotenv==1.0.1 \ resolvelib==1.0.1 \ --hash=sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309 \ --hash=sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf -rich==13.9.2 \ - --hash=sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c \ - --hash=sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1 +rich==13.9.3 \ + --hash=sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283 \ + --hash=sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e shellingham==1.5.4 \ --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de @@ -102,9 +102,9 @@ socksio==1.0.0 \ tomlkit==0.13.2 \ --hash=sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde \ --hash=sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79 -truststore==0.9.2 \ - --hash=sha256:04559916f8810cc1a5ecc41f215eddc988746067b754fc0995da7a2ceaf54735 \ - --hash=sha256:a1dee0d0575ff22d2875476343783a5d64575419974e228f3248772613c3d993 +truststore==0.10.0 \ + --hash=sha256:5da347c665714fdfbd46f738c823fe9f0d8775e41ac5fb94f325749091187896 \ + --hash=sha256:b3798548e421ffe2ca2a6217cca49e7a17baf40b72d86a5505dc7d701e77d15b typing-extensions==4.12.2 \ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 diff --git a/ci-constraints.txt b/ci-constraints.txt index 6d3d61a..c0bd57a 100644 --- a/ci-constraints.txt +++ b/ci-constraints.txt @@ -19,9 +19,6 @@ beautifulsoup4==4.12.3 \ blinker==1.8.2 \ --hash=sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01 \ --hash=sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83 -build==1.2.2.post1 \ - --hash=sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5 \ - --hash=sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7 cachetools==5.5.0 \ --hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \ --hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a @@ -141,24 +138,24 @@ msgpack==1.1.0 \ --hash=sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d \ --hash=sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e \ --hash=sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788 -mypy==1.12.1 \ - --hash=sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66 \ - --hash=sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735 \ - --hash=sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931 \ - --hash=sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a \ - --hash=sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02 \ - --hash=sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0 \ - --hash=sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635 \ - --hash=sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179 \ - --hash=sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81 \ - --hash=sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811 \ - --hash=sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042 \ - --hash=sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f \ - --hash=sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6 \ - --hash=sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e \ - --hash=sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc \ - --hash=sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4 \ - --hash=sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d +mypy==1.13.0 \ + --hash=sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e \ + --hash=sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2 \ + --hash=sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b \ + --hash=sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73 \ + --hash=sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e \ + --hash=sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d \ + --hash=sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca \ + --hash=sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d \ + --hash=sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5 \ + --hash=sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62 \ + --hash=sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7 \ + --hash=sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a \ + --hash=sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc \ + --hash=sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2 \ + --hash=sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0 \ + --hash=sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7 \ + --hash=sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8 mypy-extensions==1.0.0 \ --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 @@ -171,12 +168,9 @@ pbs-installer==2024.10.10 \ pdm==2.19.3 \ --hash=sha256:80594e5d6167fb17ea724df09b68cdfe9c601ad7f218f1beea2c032b61bf30e9 \ --hash=sha256:a9cc7f2078cd3b25ac645ffb5eca9d6b3d5dfcd788eaddfb6083432da71c97c2 -pip==24.2 \ - --hash=sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2 \ - --hash=sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8 -pip-tools==7.4.1 \ - --hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \ - --hash=sha256:864826f5073864450e24dbeeb85ce3920cdfb09848a3d69ebf537b521f14bcc9 +pip==24.3.1 \ + --hash=sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed \ + --hash=sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99 platformdirs==4.3.6 \ --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb @@ -207,19 +201,16 @@ requests==2.32.3 \ resolvelib==1.0.1 \ --hash=sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309 \ --hash=sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf -rich==13.9.2 \ - --hash=sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c \ - --hash=sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1 -ruff==0.7.0 \ - --hash=sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e \ - --hash=sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06 \ - --hash=sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b \ - --hash=sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737 \ - --hash=sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9 \ - --hash=sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2 -setuptools==75.2.0 \ - --hash=sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec \ - --hash=sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8 +rich==13.9.3 \ + --hash=sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283 \ + --hash=sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e +ruff==0.7.1 \ + --hash=sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37 \ + --hash=sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35 \ + --hash=sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99 \ + --hash=sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad \ + --hash=sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4 \ + --hash=sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307 shellingham==1.5.4 \ --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de @@ -262,18 +253,18 @@ sphinxcontrib-serializinghtml==2.0.0 \ tomlkit==0.13.2 \ --hash=sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde \ --hash=sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79 -tox==4.23.0 \ - --hash=sha256:46da40afb660e46238c251280eb910bdaf00b390c7557c8e4bb611f422e9db12 \ - --hash=sha256:a6bd7d54231d755348d3c3a7b450b5bf6563833716d1299a1619587a1b77a3bf +tox==4.23.2 \ + --hash=sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38 \ + --hash=sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c tox-gh==1.4.4 \ --hash=sha256:4ea585f66585b90f5826b1677cfc9453747792a0f9ff83d468603bc17556e07b \ --hash=sha256:b962e0f8c4619e98d11c2a135939876691e148b843b7dac4cff7de1dc4f7c215 tox-pdm==0.7.2 \ --hash=sha256:12f6215416b7acd00a80a9e7128f3dc3e3c89308d60707f5d0a24abdf83ac104 \ --hash=sha256:a841a7e1e942a71805624703b9a6d286663bd6af79bba6130ba756975c315308 -truststore==0.9.2 \ - --hash=sha256:04559916f8810cc1a5ecc41f215eddc988746067b754fc0995da7a2ceaf54735 \ - --hash=sha256:a1dee0d0575ff22d2875476343783a5d64575419974e228f3248772613c3d993 +truststore==0.10.0 \ + --hash=sha256:5da347c665714fdfbd46f738c823fe9f0d8775e41ac5fb94f325749091187896 \ + --hash=sha256:b3798548e421ffe2ca2a6217cca49e7a17baf40b72d86a5505dc7d701e77d15b typer-slim==0.12.5 \ --hash=sha256:9a994f721b828783dbf144e17461b1c720bb4598e0d5eff7c1b3f08ee58cb062 \ --hash=sha256:c8e3fcf93cc7dd584036df8755d2e2363f85f8a4dd028c7911eed3f00cf0ebb1 @@ -296,6 +287,3 @@ uv==0.4.21 \ virtualenv==20.27.0 \ --hash=sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2 \ --hash=sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655 -wheel==0.44.0 \ - --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ - --hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49 diff --git a/docs/api/venvstacks.stacks.ApplicationEnv.rst b/docs/api/venvstacks.stacks.ApplicationEnv.rst index 4d69509..c796d6a 100644 --- a/docs/api/venvstacks.stacks.ApplicationEnv.rst +++ b/docs/api/venvstacks.stacks.ApplicationEnv.rst @@ -15,15 +15,12 @@ venvstacks.stacks.ApplicationEnv ~ApplicationEnv.create_archive ~ApplicationEnv.create_environment ~ApplicationEnv.define_archive_build - ~ApplicationEnv.ensure_runtime_dependencies ~ApplicationEnv.export_environment ~ApplicationEnv.get_constraint_paths - ~ApplicationEnv.install_build_requirements ~ApplicationEnv.install_requirements ~ApplicationEnv.link_base_runtime_paths ~ApplicationEnv.link_layered_environments ~ApplicationEnv.lock_requirements - ~ApplicationEnv.remove_build_only_packages ~ApplicationEnv.report_python_site_details ~ApplicationEnv.request_export ~ApplicationEnv.select_operations diff --git a/docs/api/venvstacks.stacks.FrameworkEnv.rst b/docs/api/venvstacks.stacks.FrameworkEnv.rst index 09368fe..dfde998 100644 --- a/docs/api/venvstacks.stacks.FrameworkEnv.rst +++ b/docs/api/venvstacks.stacks.FrameworkEnv.rst @@ -16,14 +16,11 @@ venvstacks.stacks.FrameworkEnv ~FrameworkEnv.create_archive ~FrameworkEnv.create_environment ~FrameworkEnv.define_archive_build - ~FrameworkEnv.ensure_runtime_dependencies ~FrameworkEnv.export_environment ~FrameworkEnv.get_constraint_paths - ~FrameworkEnv.install_build_requirements ~FrameworkEnv.install_requirements ~FrameworkEnv.link_base_runtime_paths ~FrameworkEnv.lock_requirements - ~FrameworkEnv.remove_build_only_packages ~FrameworkEnv.report_python_site_details ~FrameworkEnv.request_export ~FrameworkEnv.select_operations diff --git a/docs/api/venvstacks.stacks.PackageIndexConfig.rst b/docs/api/venvstacks.stacks.PackageIndexConfig.rst index e5a92f5..7288eca 100644 --- a/docs/api/venvstacks.stacks.PackageIndexConfig.rst +++ b/docs/api/venvstacks.stacks.PackageIndexConfig.rst @@ -21,7 +21,6 @@ venvstacks.stacks.PackageIndexConfig .. autosummary:: - ~PackageIndexConfig.allow_source_builds ~PackageIndexConfig.local_wheel_dirs ~PackageIndexConfig.query_default_index ~PackageIndexConfig.local_wheel_paths diff --git a/docs/api/venvstacks.stacks.RuntimeEnv.rst b/docs/api/venvstacks.stacks.RuntimeEnv.rst index a8bf5ad..ddecd61 100644 --- a/docs/api/venvstacks.stacks.RuntimeEnv.rst +++ b/docs/api/venvstacks.stacks.RuntimeEnv.rst @@ -17,13 +17,10 @@ venvstacks.stacks.RuntimeEnv ~RuntimeEnv.create_build_environment ~RuntimeEnv.create_environment ~RuntimeEnv.define_archive_build - ~RuntimeEnv.ensure_runtime_dependencies ~RuntimeEnv.export_environment ~RuntimeEnv.get_constraint_paths - ~RuntimeEnv.install_build_requirements ~RuntimeEnv.install_requirements ~RuntimeEnv.lock_requirements - ~RuntimeEnv.remove_build_only_packages ~RuntimeEnv.report_python_site_details ~RuntimeEnv.request_export ~RuntimeEnv.select_operations diff --git a/docs/requirements.txt b/docs/requirements.txt index eab3e89..cb309a7 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -7,7 +7,6 @@ attrs==24.2.0 babel==2.16.0 beautifulsoup4==4.12.3 blinker==1.8.2 -build==1.2.2.post1 cachetools==5.5.0 certifi==2024.8.30 chardet==5.2.0 @@ -33,13 +32,12 @@ markdown-it-py==3.0.0 markupsafe==3.0.2 mdurl==0.1.2 msgpack==1.1.0 -mypy==1.12.1 +mypy==1.13.0 mypy-extensions==1.0.0 packaging==24.1 pbs-installer==2024.10.10 pdm==2.19.3 -pip==24.2 -pip-tools==7.4.1 +pip==24.3.1 platformdirs==4.3.6 pluggy==1.5.0 pygments==2.18.0 @@ -50,9 +48,8 @@ pytest-subtests==0.13.1 python-dotenv==1.0.1 requests==2.32.3 resolvelib==1.0.1 -rich==13.9.2 -ruff==0.7.0 -setuptools==75.2.0 +rich==13.9.3 +ruff==0.7.1 shellingham==1.5.4 sniffio==1.3.1 snowballstemmer==2.2.0 @@ -67,15 +64,14 @@ sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 tomlkit==0.13.2 -tox==4.23.0 +tox==4.23.2 tox-gh==1.4.4 tox-pdm==0.7.2 -truststore==0.9.2 +truststore==0.10.0 typer-slim==0.12.5 typing-extensions==4.12.2 unearth==0.17.2 urllib3==2.2.3 uv==0.4.21 virtualenv==20.27.0 -wheel==0.44.0 . # this package diff --git a/pdm.lock b/pdm.lock index 2e58dcb..e6b94d0 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "bootstrap", "dev", "docs"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:4282beefc4ca59510cc96299ef8d8d087145393f52982332332f43ad12843a3b" +content_hash = "sha256:6b3ff1f79f6ee9817518655d1bdb5179f333894b6bfd08a3088f48c683631aa0" [[metadata.targets]] requires_python = ">=3.11" @@ -112,24 +112,6 @@ files = [ {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, ] -[[package]] -name = "build" -version = "1.2.2.post1" -requires_python = ">=3.8" -summary = "A simple, correct Python build frontend" -groups = ["default"] -dependencies = [ - "colorama; os_name == \"nt\"", - "importlib-metadata>=4.6; python_full_version < \"3.10.2\"", - "packaging>=19.1", - "pyproject-hooks", - "tomli>=1.1.0; python_version < \"3.11\"", -] -files = [ - {file = "build-1.2.2.post1-py3-none-any.whl", hash = "sha256:1d61c0887fa860c01971625baae8bdd338e517b836a2f70dd1f7aa3a6b2fc5b5"}, - {file = "build-1.2.2.post1.tar.gz", hash = "sha256:b36993e92ca9375a219c99e606a122ff365a760a2d4bba0caa09bd5278b608b7"}, -] - [[package]] name = "cachetools" version = "5.5.0" @@ -506,7 +488,7 @@ files = [ [[package]] name = "mypy" -version = "1.12.1" +version = "1.13.0" requires_python = ">=3.8" summary = "Optional static typing for Python" groups = ["dev"] @@ -516,23 +498,23 @@ dependencies = [ "typing-extensions>=4.6.0", ] files = [ - {file = "mypy-1.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1230048fec1380faf240be6385e709c8570604d2d27ec6ca7e573e3bc09c3735"}, - {file = "mypy-1.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02dcfe270c6ea13338210908f8cadc8d31af0f04cee8ca996438fe6a97b4ec66"}, - {file = "mypy-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5a437c9102a6a252d9e3a63edc191a3aed5f2fcb786d614722ee3f4472e33f6"}, - {file = "mypy-1.12.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:186e0c8346efc027ee1f9acf5ca734425fc4f7dc2b60144f0fbe27cc19dc7931"}, - {file = "mypy-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:673ba1140a478b50e6d265c03391702fa11a5c5aff3f54d69a62a48da32cb811"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9fb83a7be97c498176fb7486cafbb81decccaef1ac339d837c377b0ce3743a7f"}, - {file = "mypy-1.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:389e307e333879c571029d5b93932cf838b811d3f5395ed1ad05086b52148fb0"}, - {file = "mypy-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b2048a95a21f7a9ebc9fbd075a4fcd310410d078aa0228dbbad7f71335e042"}, - {file = "mypy-1.12.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5932370ccf7ebf83f79d1c157a5929d7ea36313027b0d70a488493dc1b179"}, - {file = "mypy-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:19bf51f87a295e7ab2894f1d8167622b063492d754e69c3c2fed6563268cb42a"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d34167d43613ffb1d6c6cdc0cc043bb106cac0aa5d6a4171f77ab92a3c758bcc"}, - {file = "mypy-1.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:427878aa54f2e2c5d8db31fa9010c599ed9f994b3b49e64ae9cd9990c40bd635"}, - {file = "mypy-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5fcde63ea2c9f69d6be859a1e6dd35955e87fa81de95bc240143cf00de1f7f81"}, - {file = "mypy-1.12.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d54d840f6c052929f4a3d2aab2066af0f45a020b085fe0e40d4583db52aab4e4"}, - {file = "mypy-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:20db6eb1ca3d1de8ece00033b12f793f1ea9da767334b7e8c626a4872090cf02"}, - {file = "mypy-1.12.1-py3-none-any.whl", hash = "sha256:ce561a09e3bb9863ab77edf29ae3a50e65685ad74bba1431278185b7e5d5486e"}, - {file = "mypy-1.12.1.tar.gz", hash = "sha256:f5b3936f7a6d0e8280c9bdef94c7ce4847f5cdfc258fbb2c29a8c1711e8bb96d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, ] [[package]] @@ -606,33 +588,13 @@ files = [ [[package]] name = "pip" -version = "24.2" +version = "24.3.1" requires_python = ">=3.8" summary = "The PyPA recommended tool for installing Python packages." groups = ["default"] files = [ - {file = "pip-24.2-py3-none-any.whl", hash = "sha256:2cd581cf58ab7fcfca4ce8efa6dcacd0de5bf8d0a3eb9ec927e07405f4d9e2a2"}, - {file = "pip-24.2.tar.gz", hash = "sha256:5b5e490b5e9cb275c879595064adce9ebd31b854e3e803740b72f9ccf34a45b8"}, -] - -[[package]] -name = "pip-tools" -version = "7.4.1" -requires_python = ">=3.8" -summary = "pip-tools keeps your pinned dependencies fresh." -groups = ["default"] -dependencies = [ - "build>=1.0.0", - "click>=8", - "pip>=22.2", - "pyproject-hooks", - "setuptools", - "tomli; python_version < \"3.11\"", - "wheel", -] -files = [ - {file = "pip-tools-7.4.1.tar.gz", hash = "sha256:864826f5073864450e24dbeeb85ce3920cdfb09848a3d69ebf537b521f14bcc9"}, - {file = "pip_tools-7.4.1-py3-none-any.whl", hash = "sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9"}, + {file = "pip-24.3.1-py3-none-any.whl", hash = "sha256:3790624780082365f47549d032f3770eeb2b1e8bd1f7b2e02dace1afa361b4ed"}, + {file = "pip-24.3.1.tar.gz", hash = "sha256:ebcb60557f2aefabc2e0f918751cd24ea0d56d8ec5445fe1807f1d2109660b99"}, ] [[package]] @@ -769,7 +731,7 @@ files = [ [[package]] name = "rich" -version = "13.9.2" +version = "13.9.3" requires_python = ">=3.8.0" summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" groups = ["default", "bootstrap"] @@ -779,34 +741,23 @@ dependencies = [ "typing-extensions<5.0,>=4.0.0; python_version < \"3.11\"", ] files = [ - {file = "rich-13.9.2-py3-none-any.whl", hash = "sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1"}, - {file = "rich-13.9.2.tar.gz", hash = "sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c"}, + {file = "rich-13.9.3-py3-none-any.whl", hash = "sha256:9836f5096eb2172c9e77df411c1b009bace4193d6a481d534fea75ebba758283"}, + {file = "rich-13.9.3.tar.gz", hash = "sha256:bc1e01b899537598cf02579d2b9f4a415104d3fc439313a7a2c165d76557a08e"}, ] [[package]] name = "ruff" -version = "0.7.0" +version = "0.7.1" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] files = [ - {file = "ruff-0.7.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:496494d350c7fdeb36ca4ef1c9f21d80d182423718782222c29b3e72b3512737"}, - {file = "ruff-0.7.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:214b88498684e20b6b2b8852c01d50f0651f3cc6118dfa113b4def9f14faaf06"}, - {file = "ruff-0.7.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d71672336e46b34e0c90a790afeac8a31954fd42872c1f6adaea1dff76fd44f9"}, - {file = "ruff-0.7.0-py3-none-win_amd64.whl", hash = "sha256:ff4aabfbaaba880e85d394603b9e75d32b0693152e16fa659a3064a85df7fce2"}, - {file = "ruff-0.7.0-py3-none-win_arm64.whl", hash = "sha256:10842f69c245e78d6adec7e1db0a7d9ddc2fff0621d730e61657b64fa36f207e"}, - {file = "ruff-0.7.0.tar.gz", hash = "sha256:47a86360cf62d9cd53ebfb0b5eb0e882193fc191c6d717e8bef4462bc3b9ea2b"}, -] - -[[package]] -name = "setuptools" -version = "75.2.0" -requires_python = ">=3.8" -summary = "Easily download, build, install, upgrade, and uninstall Python packages" -groups = ["default"] -files = [ - {file = "setuptools-75.2.0-py3-none-any.whl", hash = "sha256:a7fcb66f68b4d9e8e66b42f9876150a3371558f98fa32222ffaa5bced76406f8"}, - {file = "setuptools-75.2.0.tar.gz", hash = "sha256:753bb6ebf1f465a1912e19ed1d41f403a79173a9acf66a42e7e6aec45c3c16ec"}, + {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"}, + {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"}, + {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"}, + {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"}, + {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"}, ] [[package]] @@ -986,7 +937,7 @@ files = [ [[package]] name = "tox" -version = "4.23.0" +version = "4.23.2" requires_python = ">=3.8" summary = "tox is a generic virtualenv management and test command line tool" groups = ["dev"] @@ -1004,8 +955,8 @@ dependencies = [ "virtualenv>=20.26.6", ] files = [ - {file = "tox-4.23.0-py3-none-any.whl", hash = "sha256:46da40afb660e46238c251280eb910bdaf00b390c7557c8e4bb611f422e9db12"}, - {file = "tox-4.23.0.tar.gz", hash = "sha256:a6bd7d54231d755348d3c3a7b450b5bf6563833716d1299a1619587a1b77a3bf"}, + {file = "tox-4.23.2-py3-none-any.whl", hash = "sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38"}, + {file = "tox-4.23.2.tar.gz", hash = "sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c"}, ] [[package]] @@ -1039,13 +990,13 @@ files = [ [[package]] name = "truststore" -version = "0.9.2" +version = "0.10.0" requires_python = ">=3.10" summary = "Verify certificates using native system trust stores" groups = ["default", "bootstrap"] files = [ - {file = "truststore-0.9.2-py3-none-any.whl", hash = "sha256:04559916f8810cc1a5ecc41f215eddc988746067b754fc0995da7a2ceaf54735"}, - {file = "truststore-0.9.2.tar.gz", hash = "sha256:a1dee0d0575ff22d2875476343783a5d64575419974e228f3248772613c3d993"}, + {file = "truststore-0.10.0-py3-none-any.whl", hash = "sha256:b3798548e421ffe2ca2a6217cca49e7a17baf40b72d86a5505dc7d701e77d15b"}, + {file = "truststore-0.10.0.tar.gz", hash = "sha256:5da347c665714fdfbd46f738c823fe9f0d8775e41ac5fb94f325749091187896"}, ] [[package]] @@ -1131,14 +1082,3 @@ files = [ {file = "virtualenv-20.27.0-py3-none-any.whl", hash = "sha256:44a72c29cceb0ee08f300b314848c86e57bf8d1f13107a5e671fb9274138d655"}, {file = "virtualenv-20.27.0.tar.gz", hash = "sha256:2ca56a68ed615b8fe4326d11a0dca5dfbe8fd68510fb6c6349163bed3c15f2b2"}, ] - -[[package]] -name = "wheel" -version = "0.44.0" -requires_python = ">=3.8" -summary = "A built-package format for Python" -groups = ["default"] -files = [ - {file = "wheel-0.44.0-py3-none-any.whl", hash = "sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f"}, - {file = "wheel-0.44.0.tar.gz", hash = "sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49"}, -] diff --git a/pyproject.toml b/pyproject.toml index e8ab091..36e2f35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,13 +14,10 @@ dependencies = [ "pip>=24.1.1", # PDM is used to install the base runtime environments for deployment "pdm>=2.17.3", - # pip-sync needs to be run externally otherwise it runs into problems - # when the potentially non-portable executables get cleaned up - "pip-tools>=7.4.1", # `uv pip compile` is used rather than `pip-compile` as it is faster and # doesn't have to be installed into the target Python runtime environment. # Due to https://github.com/astral-sh/uv/issues/2500 `uv` can't be used to - # replace `pip install` or `pip-sync` yet. + # replace `pip install` # Due to https://github.com/astral-sh/uv/issues/2831 we're also not trying to # replace `python -Im venv` with `uv venv` at this point since we want explicit # control over whether files are symlinked or copied between the environments diff --git a/src/venvstacks/_util.py b/src/venvstacks/_util.py index c61ebe4..ea06074 100644 --- a/src/venvstacks/_util.py +++ b/src/venvstacks/_util.py @@ -72,8 +72,9 @@ def get_env_python(env_path: Path) -> Path: "PYTHONLEGACYWINDOWSSTDIO": "", # There are other dev settings that may cause problems, but are also unlikely to be set # See https://docs.python.org/3/using/cmdline.html#environment-variables - # These settings are here specifically to avoid the `pip-sync` issues noted - # in https://github.com/jazzband/pip-tools/issues/2117 + # These settings were originally added to avoid the `pip-sync` issues noted + # in https://github.com/jazzband/pip-tools/issues/2117, and then retained + # even after the `pip-sync` dependency was removed } diff --git a/src/venvstacks/cli.py b/src/venvstacks/cli.py index 26fa3ad..cffc177 100644 --- a/src/venvstacks/cli.py +++ b/src/venvstacks/cli.py @@ -99,12 +99,6 @@ def handle_app_options() -> None: help="Query the default package index (PyPI) for installation artifacts" ), ] # fmt: skip -_CLI_OPT_FLAG_allow_source = Annotated[ - bool, - typer.Option( - help="Allow implicit source builds (may affect archive reproducibility)" - ), -] # fmt: skip _CLI_OPT_STRLIST_local_wheels = Annotated[ list[str] | None, typer.Option( @@ -178,14 +172,12 @@ def _define_build_environment( build_path: str, *, index: bool, - allow_source: bool, local_wheels: list[str] | None, ) -> BuildEnvironment: """Load given stack specification and define a build environment""" stack_spec = StackSpec.load(spec_path) index_config = PackageIndexConfig( query_default_index=index, - allow_source_builds=allow_source, local_wheel_dirs=local_wheels, ) return stack_spec.define_build_environment(build_path, index_config) @@ -328,7 +320,6 @@ def build( publish: _CLI_OPT_FLAG_publish = False, # Package index access configuration index: _CLI_OPT_FLAG_index = True, - allow_source: _CLI_OPT_FLAG_allow_source = False, local_wheels: _CLI_OPT_STRLIST_local_wheels = None, # Adjust naming of published archives and metadata files tag_outputs: _CLI_OPT_FLAG_tag_outputs = False, @@ -355,7 +346,6 @@ def build( spec_path, build_dir, index=index, - allow_source=allow_source, local_wheels=local_wheels, ) # Update the various `want_*` flags on each environment @@ -396,7 +386,6 @@ def lock( clean: _CLI_OPT_FLAG_clean = False, # Package index access configuration index: _CLI_OPT_FLAG_index = True, - allow_source: _CLI_OPT_FLAG_allow_source = False, local_wheels: _CLI_OPT_STRLIST_local_wheels = None, # Selective processing of defined layers include: _CLI_OPT_STRLIST_include = None, @@ -410,7 +399,6 @@ def lock( spec_path, build_dir, index=index, - allow_source=allow_source, local_wheels=local_wheels, ) # Update the various `want_*` flags on each environment @@ -474,7 +462,6 @@ def publish( build_dir, # No locking or build steps will be invoked on the environment index=False, - allow_source=False, local_wheels=None, ) # Update the various `want_*` flags on each environment @@ -528,7 +515,6 @@ def local_export( build_dir, # No locking or build steps will be invoked on the environment index=False, - allow_source=False, local_wheels=None, ) # Update the various `want_*` flags on each environment diff --git a/src/venvstacks/stacks.py b/src/venvstacks/stacks.py index a295582..b2b1bb8 100755 --- a/src/venvstacks/stacks.py +++ b/src/venvstacks/stacks.py @@ -13,7 +13,6 @@ import sysconfig import tempfile import tomllib -import warnings from abc import ABC, abstractmethod from dataclasses import dataclass, field, InitVar @@ -118,7 +117,6 @@ class PackageIndexConfig: """Python package index access configuration""" query_default_index: bool = field(default=True) - allow_source_builds: bool = field(default=False) local_wheel_dirs: InitVar[Sequence[StrPath] | None] = None local_wheel_paths: list[Path] = field(init=False) @@ -133,9 +131,9 @@ def __post_init__(self, local_wheel_dirs: Sequence[StrPath] | None) -> None: @classmethod def disabled(cls) -> Self: + """Package index configuration that disallows package installation""" return cls( query_default_index=False, - allow_source_builds=False, local_wheel_dirs=None, ) @@ -146,37 +144,19 @@ def resolve_lexical_paths(self, base_path: StrPath) -> None: _resolve_lexical_path(path, base_path) for path in self.local_wheel_paths ] - @staticmethod - def _get_require_binary_args() -> list[str]: - return ["--only-binary", ":all:"] - - def _get_common_pip_args(self, require_binary: bool) -> list[str]: + def _get_common_pip_args(self) -> list[str]: result = [] if not self.query_default_index: result.append("--no-index") - if require_binary: - result.extend(self._get_require_binary_args()) for local_wheel_path in self.local_wheel_paths: result.extend(("--find-links", os.fspath(local_wheel_path))) return result def _get_uv_pip_compile_args(self) -> list[str]: - require_binary = not self.allow_source_builds - return self._get_common_pip_args(require_binary) - - def _get_pip_install_args(self, require_binary: bool | None) -> list[str]: - if require_binary is None: - require_binary = not self.allow_source_builds - return self._get_common_pip_args(require_binary) + return self._get_common_pip_args() - def _get_pip_sync_args(self) -> list[str]: - # Local cache should always have been populated by the time sync runs - # pip-sync wraps pip, so only some args are accepted at top level - sync_args = self._get_common_pip_args(require_binary=False) - pip_args = " ".join(self._get_require_binary_args()) - if pip_args: - sync_args.extend(("--pip-args", pip_args)) - return sync_args + def _get_pip_install_args(self) -> list[str]: + return self._get_common_pip_args() ###################################################### @@ -1056,7 +1036,7 @@ def select_operations( self.was_built = False def _create_environment( - self, *, clean: bool = False, build_only: bool = False + self, *, clean: bool = False, lock_only: bool = False ) -> None: env_path = self.env_path env_updated = False @@ -1070,13 +1050,13 @@ def _create_environment( if self.want_build or self.was_created: # Run the update if requested, or if env was created earlier in the build print(f"{str(env_path)!r} exists, updating...") - self._update_existing_environment(build_only=build_only) + self._update_existing_environment(lock_only=lock_only) env_updated = True else: print(f"{str(env_path)!r} exists, reusing without updating...") create_env = False if create_env: - self._create_new_environment(build_only=build_only) + self._create_new_environment(lock_only=lock_only) self.was_created = create_env self.was_built = create_env or env_updated @@ -1170,50 +1150,26 @@ def _run_pip( return run_python_command(command, **kwds) def _run_pip_install( - self, *pip_install_args: str, require_binary: bool | None = None + self, *pip_install_args: str ) -> subprocess.CompletedProcess[str]: # TODO: Switch to `uv pip install` once https://github.com/astral-sh/uv/issues/2500 # is resolved (so environment layering is still handled correctly) + # Requirements are fully transitively locked, so no implicit deps are allowed + # Implicit source builds are not supported (use local wheel dirs instead) pip_args = [ "install", "--no-warn-script-location", - *self.index_config._get_pip_install_args(require_binary), + *self.index_config._get_pip_install_args(), + "--only-binary", + ":all:", + "--no-deps", + "--upgrade", *pip_install_args, ] result = self._run_pip(pip_args) print(f"Dependencies installed and updated in {str(self.env_path)!r}") return result - def _run_pip_sync( - self, - env_python_path: StrPath, - requirements_paths: Sequence[StrPath], - ) -> subprocess.CompletedProcess[str]: - # TODO: Switch to `uv pip sync` once https://github.com/astral-sh/uv/issues/2500 - # is resolved (so environment layering is still handled correctly) - # Work around https://github.com/jazzband/pip-tools/issues/2103 by clearing - # `pip-sync`'s list of packages to leave behind before running the sync command - command = [ - str(self.tools_python_path), - "-X", - "utf8", - "-Ic", - ( - "import piptools.sync, piptools.scripts.sync; " - "piptools.sync.PACKAGES_TO_IGNORE.clear(); " - "piptools.scripts.sync.cli()" - ), - "--quiet", - "--no-config", - *self.index_config._get_pip_sync_args(), - "--python-executable", - os.fspath(env_python_path), - *( - os.fspath(p) for p in requirements_paths - ), # `map` upsets typecheckers here - ] - return run_python_command(command) - def get_constraint_paths(self) -> list[Path]: # No constraints files by default, subclasses override as necessary return [] @@ -1245,88 +1201,23 @@ def lock_requirements(self) -> EnvironmentLock: print(f" Environment lock time set: {self.env_lock.locked_at!r}") return self.env_lock - def install_build_requirements(self) -> subprocess.CompletedProcess[str] | None: - # Install build-only dependencies inside the target environment - # All build dependencies must be available as pre-built binary packages - # TODO: drop support for implicit source builds (superseded by local wheel dirs) - if not self.index_config.allow_source_builds: - return None # No implicit source builds -> never need build dependencies - build_requirements = self.env_spec.build_requirements - if not build_requirements: - return None # Nothing to remove - deprecation_msg = ( - "Support for implicit source builds is being removed (use local wheels)" - ) - warnings.warn(deprecation_msg, DeprecationWarning) - return self._run_pip_install( - "--upgrade", - *build_requirements, - require_binary=True, - ) - def install_requirements(self) -> subprocess.CompletedProcess[str]: # Run a pip dependency upgrade inside the target environment - # Build isolation is intentionally turned off so source builds that need big - # dependencies like `torch` can access the runtime environment - # Requirements are fully transitively locked, so no implicit deps are allowed if not self.env_lock.is_locked: raise BuildEnvError( "Environment must be locked before installing dependencies" ) - if self.index_config.allow_source_builds: - self.install_build_requirements() # Ensure source dependencies can be built - return self._run_pip_install( - "--no-build-isolation", - "--no-deps", - "--upgrade", - "-r", - str(self.requirements_path), - ) - - def remove_build_only_packages(self) -> subprocess.CompletedProcess[str] | None: - # Remove build-only dependencies before publishing the environment layer - if not self.index_config.allow_source_builds: - return None # No implicit source builds -> never install build dependencies - build_requirements = self.env_spec.build_requirements - if not build_requirements or not self.was_built: - return ( - None # Nothing to remove (no build requirements, or env wasn't built) - ) - requirements_paths = [ - self.requirements_path, - *self.get_constraint_paths(), - ] - result = self._run_pip_sync(self.python_path, requirements_paths) - print(f"Removed build-only packages from {str(self.env_path)!r}") - return result - - def ensure_runtime_dependencies(self) -> subprocess.CompletedProcess[str] | None: - # Second pass reinstalling purely from local cache - # This adds packages previously skipped due to build dependencies in lower layers - # Ideally, hashes wouldn't be rechecked to allow for cached wheels built from source - # artifacts, but `pip` doesn't allow the hash check to be turned off when hashes are - # present in the requirements file - if not self.was_built or not self.index_config.allow_source_builds: - # Nothing to ensure (env wasn't built or build deps were never installed) - return None return self._run_pip_install( - # Downloads allowed to work around https://github.com/pypa/pip/issues/12807 - # "--no-index", - "--quiet", - "--no-deps", "-r", str(self.requirements_path), - require_binary=True, ) - def _update_existing_environment(self, *, build_only: bool = False) -> None: - if build_only: - self.install_build_requirements() - else: + def _update_existing_environment(self, *, lock_only: bool = False) -> None: + if not lock_only: self.install_requirements() @abstractmethod - def _create_new_environment(self, *, build_only: bool = False) -> None: + def _create_new_environment(self, *, lock_only: bool = False) -> None: raise NotImplementedError def _ensure_portability(self) -> None: @@ -1457,9 +1348,6 @@ def env_spec(self) -> RuntimeSpec: assert isinstance(self._env_spec, RuntimeSpec) return self._env_spec - def _update_existing_environment(self, *, build_only: bool = False) -> None: - super()._update_existing_environment(build_only=build_only) - def _remove_pip(self) -> subprocess.CompletedProcess[str] | None: to_be_checked = ["pip", "wheel", "setuptools"] to_be_removed = [] @@ -1471,24 +1359,18 @@ def _remove_pip(self) -> subprocess.CompletedProcess[str] | None: pip_args = ["uninstall", "-y", *to_be_removed] return self._run_pip(pip_args) - def _create_new_environment(self, *, build_only: bool = False) -> None: + def _create_new_environment(self, *, lock_only: bool = False) -> None: python_runtime = self.env_spec.fully_versioned_name install_path = _pdm_python_install(self.build_path, python_runtime) if install_path is None: raise BuildEnvError(f"Failed to install {python_runtime}") shutil.move(install_path, self.env_path) - if not self.index_config.allow_source_builds: - # Only `pip-sync` needs `pip` to be installed in the target environment, - # and that step is skipped when implicit source builds are disabled - self._remove_pip() + # No build step needs `pip` to be installed in the target environment, + # and we don't want to ship it unless explicitly requested to do so + # as a declared dependency of an included component + self._remove_pip() fs_sync() - if build_only: - if self.index_config.allow_source_builds: - print( - f"Preparing {str(self.python_path)!r} build dependencies in {self}" - ) - self.install_build_requirements() - else: + if not lock_only: print( f"Using {str(self.python_path)!r} as runtime environment layer in {self}" ) @@ -1501,7 +1383,7 @@ def _update_output_metadata(self, metadata: LayerSpecMetadata) -> None: def create_build_environment(self, *, clean: bool = False) -> None: """Create or update runtime build environment. Returns True if env is new or updated.""" - super()._create_environment(clean=clean, build_only=True) + super()._create_environment(clean=clean, lock_only=True) class _VirtualEnvironment(_PythonEnvironment): @@ -1562,16 +1444,16 @@ def _ensure_virtual_environment(self) -> subprocess.CompletedProcess[str]: def _link_layered_environment(self) -> None: pass # Nothing to do by default, subclasses override if necessary - def _update_existing_environment(self, *, build_only: bool = False) -> None: - if build_only: + def _update_existing_environment(self, *, lock_only: bool = False) -> None: + if lock_only: raise RuntimeError( - "Only runtime environments support build-only installation" + "Only runtime environments support lock-only installation" ) self._ensure_virtual_environment() super()._update_existing_environment() - def _create_new_environment(self, *, build_only: bool = False) -> None: - self._update_existing_environment(build_only=build_only) + def _create_new_environment(self, *, lock_only: bool = False) -> None: + self._update_existing_environment(lock_only=lock_only) def _update_output_metadata(self, metadata: LayerSpecMetadata) -> None: super()._update_output_metadata(metadata) @@ -1705,8 +1587,8 @@ def _link_layered_environment(self) -> None: with open(sc_path, "w", encoding="utf-8") as f: f.write("\n".join(sc_contents)) - def _update_existing_environment(self, *, build_only: bool = False) -> None: - super()._update_existing_environment(build_only=build_only) + def _update_existing_environment(self, *, lock_only: bool = False) -> None: + super()._update_existing_environment(lock_only=lock_only) # Also publish the specified launch module as an importable top level module launch_module_source_path = self.env_spec.launch_module_path launch_module_env_path = self.pylib_path / launch_module_source_path.name @@ -2176,16 +2058,6 @@ def create_environments(self, *, clean: bool = False, lock: bool = False) -> Non for layered_env in self.venvstacks_to_build(): layered_env.create_environment(clean=clean) layered_env.report_python_site_details() - # Remove build packages that shouldn't be shipped - for env in reversed(list(self.built_environments())): - env.remove_build_only_packages() - # Fix up layered environments that were inadvertently relying on build dependencies - # Need to check all upper layers, not just those that declared the build-only dependencies - for env in self.built_environments(): - if env.kind == LayerVariants.RUNTIME: - # Runtimes have no dependencies, so nothing to check - continue - env.ensure_runtime_dependencies() @staticmethod def _env_metadata_path( diff --git a/tests/test_cli_invocation.py b/tests/test_cli_invocation.py index dcb790e..144c598 100644 --- a/tests/test_cli_invocation.py +++ b/tests/test_cli_invocation.py @@ -342,15 +342,6 @@ def _cli_args_case_id(cli_args_test_case: tuple[Any, ...]) -> str: (), PackageIndexConfig( query_default_index=True, - allow_source_builds=False, - local_wheel_dirs=None, - ), - ), - ( - ("--index", "--no-allow-source"), - PackageIndexConfig( - query_default_index=True, - allow_source_builds=False, local_wheel_dirs=None, ), ), @@ -358,15 +349,6 @@ def _cli_args_case_id(cli_args_test_case: tuple[Any, ...]) -> str: ("--no-index",), PackageIndexConfig( query_default_index=False, - allow_source_builds=False, - local_wheel_dirs=None, - ), - ), - ( - ("--allow-source",), - PackageIndexConfig( - query_default_index=True, - allow_source_builds=True, local_wheel_dirs=None, ), ), @@ -374,7 +356,6 @@ def _cli_args_case_id(cli_args_test_case: tuple[Any, ...]) -> str: ("--local-wheels", "/some_dir", "--local-wheels", "some/other/dir"), PackageIndexConfig( query_default_index=True, - allow_source_builds=False, local_wheel_dirs=["/some_dir", "some/other/dir"], ), ), diff --git a/tests/test_index_config.py b/tests/test_index_config.py index 11a8ac4..9b53c26 100644 --- a/tests/test_index_config.py +++ b/tests/test_index_config.py @@ -13,35 +13,21 @@ class TestDefaultOptions: TEST_CONFIG = PackageIndexConfig() def test_uv_pip_compile(self) -> None: - # Nominal config is always used when locking - assert self.TEST_CONFIG._get_uv_pip_compile_args() == ["--only-binary", ":all:"] + assert self.TEST_CONFIG._get_uv_pip_compile_args() == [] def test_pip_install(self) -> None: - # Nominal config can be overridden for package installation commands - allow_source_config: list[str] = [] - binary_only_config = ["--only-binary", ":all:"] - assert self.TEST_CONFIG._get_pip_install_args(None) == binary_only_config - assert self.TEST_CONFIG._get_pip_install_args(False) == allow_source_config - assert self.TEST_CONFIG._get_pip_install_args(True) == binary_only_config - - def test_pip_sync(self) -> None: - # Final sync to remove source build dependencies is always binary-only - assert self.TEST_CONFIG._get_pip_sync_args() == [ - "--pip-args", - "--only-binary :all:", - ] + assert self.TEST_CONFIG._get_pip_install_args() == [] class TestConfiguredOptions: TEST_CONFIG = PackageIndexConfig( query_default_index=False, - allow_source_builds=True, local_wheel_dirs=["/some_dir"], ) WHEEL_DIR = f"{os.sep}some_dir" def test_uv_pip_compile(self) -> None: - # Nominal config is always used when locking + # There are currently no locking specific args assert self.TEST_CONFIG._get_uv_pip_compile_args() == [ "--no-index", "--find-links", @@ -49,31 +35,11 @@ def test_uv_pip_compile(self) -> None: ] def test_pip_install(self) -> None: - # Nominal config can be overridden for package installation commands - allow_source_config: list[str] = [ - "--no-index", - "--find-links", - self.WHEEL_DIR, - ] - binary_only_config = [ - "--no-index", - "--only-binary", - ":all:", - "--find-links", - self.WHEEL_DIR, - ] - assert self.TEST_CONFIG._get_pip_install_args(None) == allow_source_config - assert self.TEST_CONFIG._get_pip_install_args(False) == allow_source_config - assert self.TEST_CONFIG._get_pip_install_args(True) == binary_only_config - - def test_pip_sync(self) -> None: - # Final sync to remove source build dependencies is always binary-only - assert self.TEST_CONFIG._get_pip_sync_args() == [ + # There are currently no installation specific args + assert self.TEST_CONFIG._get_pip_install_args() == [ "--no-index", "--find-links", self.WHEEL_DIR, - "--pip-args", - "--only-binary :all:", ] diff --git a/tests/test_minimal_project.py b/tests/test_minimal_project.py index 1da32f4..3aefa28 100644 --- a/tests/test_minimal_project.py +++ b/tests/test_minimal_project.py @@ -10,7 +10,7 @@ # Use unittest for consistency with test_sample_project (which needs the better diff support) import unittest -from unittest.mock import Mock, call as expect_call +from unittest.mock import Mock import pytest # To mark slow test cases @@ -31,7 +31,6 @@ BuildEnvironment, EnvNameDeploy, StackSpec, - LayerVariants, ExportedEnvironmentPaths, ExportMetadata, PackageIndexConfig, @@ -590,16 +589,13 @@ def test_locking_and_publishing(self) -> None: self.assertRecentlyLocked(dry_run_last_locked_times, minimum_lock_time) # Check for expected subprocess argument lookups for env in self.build_env.all_environments(): - # First binary only build: lock with uv, install with pip - # sync is never called for binary only builds + # First environment build: lock with uv, install with pip mock_compile = cast(Mock, env.index_config._get_uv_pip_compile_args) - mock_compile.assert_called_once() + mock_compile.assert_called_once_with() mock_compile.reset_mock() mock_install = cast(Mock, env.index_config._get_pip_install_args) - mock_install.assert_called_once_with(None) + mock_install.assert_called_once_with() mock_install.reset_mock() - mock_sync = cast(Mock, env.index_config._get_pip_sync_args) - mock_sync.assert_not_called() subtests_passed += 1 subtests_started += 1 with self.subTest("Check tagged dry run"): @@ -623,12 +619,10 @@ def test_locking_and_publishing(self) -> None: # The lock file is recreated, the timestamp metadata just doesn't # get updated if the hash of the contents doesn't change mock_compile = cast(Mock, env.index_config._get_uv_pip_compile_args) - mock_compile.assert_called_once() + mock_compile.assert_called_once_with() mock_compile.reset_mock() mock_install = cast(Mock, env.index_config._get_pip_install_args) mock_install.assert_not_called() - mock_sync = cast(Mock, env.index_config._get_pip_sync_args) - mock_sync.assert_not_called() subtests_passed += 1 # Test stage: ensure lock timestamps *do* change when the requirements "change" for env in build_env.all_environments(): @@ -650,12 +644,10 @@ def test_locking_and_publishing(self) -> None: for env in self.build_env.all_environments(): # Locked, but not rebuilt, so only uv should be called mock_compile = cast(Mock, env.index_config._get_uv_pip_compile_args) - mock_compile.assert_called_once() + mock_compile.assert_called_once_with() mock_compile.reset_mock() mock_install = cast(Mock, env.index_config._get_pip_install_args) mock_install.assert_not_called() - mock_sync = cast(Mock, env.index_config._get_pip_sync_args) - mock_sync.assert_not_called() subtests_passed += 1 # Test stage: ensure exported environments allow launch module execution subtests_started += 1 @@ -689,159 +681,3 @@ def test_locking_and_publishing(self) -> None: self.assertEqual( subtests_passed, subtests_started, "Fail due to failed subtest(s)" ) - - @pytest.mark.slow - def test_implicit_source_builds(self) -> None: - # TODO: Completely drop support for implicit source builds (use local wheel dirs instead) - # This is organised as subtests in a monolothic test sequence to reduce CI overhead - # Separating the tests wouldn't really make them independent, unless the outputs of - # the earlier steps were checked in for use when testing the later steps. - # Actually configuring and building the environments is executed outside the subtest - # declarations, since actual build failures need to fail the entire test method. - subtests_started = subtests_passed = 0 # Track subtest failures - build_env = self.build_env - source_build_index_config = PackageIndexConfig(allow_source_builds=True) - self.mock_index_config_options(source_build_index_config) - platform_tag = build_env.build_platform - expected_tag = f"-{platform_tag}" - versioned_tag = ( - f"{expected_tag}-1" # No previous metadata when running the test - ) - expected_dry_run_result = EXPECTED_MANIFEST - expected_tagged_dry_run_result = _tag_manifest(EXPECTED_MANIFEST, versioned_tag) - # Ensure the locking and publication steps always run for all environments - build_env.select_operations(lock=True, build=True, publish=True) - # Handle running this test case repeatedly in a local checkout - # Also inject a cheap-to-install build dependency in all environments - for env in build_env.all_environments(): - env.env_lock._purge_lock() - env.env_spec.build_requirements = ["uv"] - # Test stage: check dry run metadata results are as expected - minimum_lock_time = datetime.now(timezone.utc) - with pytest.deprecated_call(): - build_env.create_environments() - subtests_started += 1 - with self.subTest("Check untagged dry run"): - dry_run_result, dry_run_last_locked_times = _filter_manifest( - build_env.publish_artifacts(dry_run=True)[1] - ) - self.assertEqual(dry_run_result, expected_dry_run_result) - self.assertRecentlyLocked(dry_run_last_locked_times, minimum_lock_time) - # Check for expected subprocess argument lookups - for env in self.build_env.all_environments(): - # Source allowed lock & build invocation: - # * install build deps with pip prior to locking - # * lock with uv - # * install build deps and runtime deps with pip - # * remove build deps with pip-sync - # * ensure runtime deps are installed in upper layers with pip - mock_compile = cast(Mock, env.index_config._get_uv_pip_compile_args) - mock_compile.assert_called_once() - mock_compile.reset_mock() - mock_install = cast(Mock, env.index_config._get_pip_install_args) - if env.kind == LayerVariants.RUNTIME: - expected_install_calls = [ - expect_call(True), # Pre-lock install_build_requirements() - expect_call(True), # install_build_requirements() - expect_call(None), # install_requirements() - ] - else: - expected_install_calls = [ - expect_call(True), # install_build_requirements() - expect_call(None), # install_requirements() - expect_call(True), # ensure_runtime_dependencies() - ] - self.assertEqual(mock_install.call_args_list, expected_install_calls) - mock_install.reset_mock() - mock_sync = cast(Mock, env.index_config._get_pip_sync_args) - mock_sync.assert_called_once() - mock_sync.reset_mock() - subtests_passed += 1 - subtests_started += 1 - with self.subTest("Check tagged dry run"): - tagged_dry_run_result, tagged_last_locked_times = _filter_manifest( - build_env.publish_artifacts(dry_run=True, tag_outputs=True)[1] - ) - self.assertEqual(tagged_dry_run_result, expected_tagged_dry_run_result) - self.assertEqual(tagged_last_locked_times, dry_run_last_locked_times) - subtests_passed += 1 - # Test stage: ensure lock timestamps don't change when requirements don't change - build_env.lock_environments() - subtests_started += 1 - with self.subTest("Check lock timestamps don't change for stable requirements"): - stable_dry_run_result, stable_last_locked_times = _filter_manifest( - build_env.publish_artifacts(dry_run=True)[1] - ) - self.assertEqual(stable_dry_run_result, expected_dry_run_result) - self.assertEqual(stable_last_locked_times, dry_run_last_locked_times) - # Check for expected subprocess argument lookups - for env in self.build_env.all_environments(): - # The lock file is recreated, the timestamp metadata just doesn't - # get updated if the hash of the contents doesn't change - mock_compile = cast(Mock, env.index_config._get_uv_pip_compile_args) - mock_compile.assert_called_once() - mock_compile.reset_mock() - mock_install = cast(Mock, env.index_config._get_pip_install_args) - if env.kind == LayerVariants.RUNTIME: - # Pre-lock install_build_requirements() - mock_install.assert_called_once_with(True) - mock_install.reset_mock() - else: - mock_install.assert_not_called() - mock_sync = cast(Mock, env.index_config._get_pip_sync_args) - mock_sync.assert_not_called() - subtests_passed += 1 - # Test stage: ensure lock timestamps *do* change when the requirements "change" - for env in build_env.all_environments(): - # Rather than actually make the hash change, instead change the hash *records* - env_lock = env.env_lock - env_lock._requirements_hash = "ensure requirements appear to have changed" - env_lock._write_lock_metadata() - minimum_relock_time = datetime.now(timezone.utc) - build_env.lock_environments() - subtests_started += 1 - with self.subTest("Check lock timestamps change for updated requirements"): - relocked_dry_run_result, relocked_last_locked_times = _filter_manifest( - build_env.publish_artifacts(dry_run=True)[1] - ) - self.assertEqual(relocked_dry_run_result, expected_dry_run_result) - self.assertGreater(minimum_relock_time, minimum_lock_time) - self.assertRecentlyLocked(relocked_last_locked_times, minimum_relock_time) - # Check for expected subprocess argument lookups - for env in self.build_env.all_environments(): - # Locked, but not rebuilt, so only uv should be called - mock_compile = cast(Mock, env.index_config._get_uv_pip_compile_args) - mock_compile.assert_called_once() - mock_compile.reset_mock() - mock_install = cast(Mock, env.index_config._get_pip_install_args) - if env.kind == LayerVariants.RUNTIME: - # Pre-lock install_build_requirements() - mock_install.assert_called_once_with(True) - mock_install.reset_mock() - else: - mock_install.assert_not_called() - mock_sync = cast(Mock, env.index_config._get_pip_sync_args) - mock_sync.assert_not_called() - subtests_passed += 1 - # Test stage: ensure published archives and manifests have the expected name - subtests_started += 1 - with self.subTest("Check untagged publication"): - publication_result = build_env.publish_artifacts() - self.check_publication_result( - publication_result, dry_run_result, expected_tag=None - ) - subtests_passed += 1 - subtests_started += 1 - with self.subTest("Check tagged publication"): - tagged_publication_result = build_env.publish_artifacts(tag_outputs=True) - self.check_publication_result( - tagged_publication_result, tagged_dry_run_result, expected_tag - ) - subtests_passed += 1 - # TODO: Add another test stage that confirms build versions increment as expected - - # Work aroung pytest-subtests not failing the test case when subtests fail - # https://github.com/pytest-dev/pytest-subtests/issues/76 - self.assertEqual( - subtests_passed, subtests_started, "Fail due to failed subtest(s)" - )