From 034bd4f484bbb4fc397a5a167d54730b0dce9bcd Mon Sep 17 00:00:00 2001 From: John Beisley Date: Wed, 1 Apr 2026 16:55:03 +0100 Subject: [PATCH 1/4] Add `uv` workspace options for sub package installation --- README.md | 2 + src/nox_uv/__init__.py | 13 + tests/subworkspace/.python-version | 1 + tests/subworkspace/README.md | 0 tests/subworkspace/noxfile.py | 57 ++++ tests/subworkspace/package-one/README.md | 0 tests/subworkspace/package-one/pyproject.toml | 23 ++ .../package-one/src/package_one/__init__.py | 0 .../package-one/src/package_one/main.py | 6 + tests/subworkspace/package-two/README.md | 0 tests/subworkspace/package-two/pyproject.toml | 23 ++ .../package-two/src/package_two/__init__.py | 0 .../package-two/src/package_two/main.py | 6 + tests/subworkspace/pyproject.toml | 26 ++ tests/subworkspace/uv.lock | 315 ++++++++++++++++++ tests/test_lib.py | 18 +- tests/test_workspace.py | 45 +++ 17 files changed, 525 insertions(+), 10 deletions(-) create mode 100644 tests/subworkspace/.python-version create mode 100644 tests/subworkspace/README.md create mode 100644 tests/subworkspace/noxfile.py create mode 100644 tests/subworkspace/package-one/README.md create mode 100644 tests/subworkspace/package-one/pyproject.toml create mode 100644 tests/subworkspace/package-one/src/package_one/__init__.py create mode 100644 tests/subworkspace/package-one/src/package_one/main.py create mode 100644 tests/subworkspace/package-two/README.md create mode 100644 tests/subworkspace/package-two/pyproject.toml create mode 100644 tests/subworkspace/package-two/src/package_two/__init__.py create mode 100644 tests/subworkspace/package-two/src/package_two/main.py create mode 100644 tests/subworkspace/pyproject.toml create mode 100644 tests/subworkspace/uv.lock create mode 100644 tests/test_workspace.py diff --git a/README.md b/README.md index 7671d65..5afddd0 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ def lint(s: Session) -> None: - `uv_all_groups`: boolean to install all _dependency-groups_ - `uv_no_groups`: list of groups to exclude during install of `uv_all_groups` - `uv_no_install_project`: boolean to not install the current project +- `uv_packages`: list of `uv` _packages_ to install, only valid in a `uv` workspace. +- `uv_all_packages`: boolean to install all `uv` _packages_, only valid in a `uv` workspace. - `uv_sync_locked`: boolean to validate that `uv.lock` is up to date - `uv_quiet`: boolean to silence `uv sync` command diff --git a/src/nox_uv/__init__.py b/src/nox_uv/__init__.py index 2cf64c5..31bcfff 100644 --- a/src/nox_uv/__init__.py +++ b/src/nox_uv/__init__.py @@ -31,6 +31,8 @@ def session( uv_no_install_project: bool = False, uv_sync_locked: bool = True, uv_quiet: bool = False, + uv_packages: Sequence[str] = (), + uv_all_packages: bool = False, **kwargs: dict[str, Any], ) -> Callable[..., Callable[..., R]]: """Drop-in replacement for the :func:`nox.session` decorator to add support for `uv`. @@ -62,6 +64,8 @@ def session( uv_no_groups=uv_no_groups, uv_only_groups=uv_only_groups, uv_no_install_project=uv_no_install_project, + uv_packages=uv_packages, + uv_all_packages=uv_all_packages, uv_sync_locked=uv_sync_locked, uv_quiet=uv_quiet, **kwargs, @@ -103,10 +107,19 @@ def session( if uv_no_install_project: extended_cmd.append("--no-install-project") + for pkg in uv_packages: + extended_cmd.append(f"--package={pkg}") + + if uv_all_packages: + extended_cmd.append("--all-packages") + sync_cmd += extended_cmd @functools.wraps(function) def wrapper(s: nox.Session, *_args: Any, **_kwargs: Any) -> None: + if uv_packages and uv_all_packages: + raise s.error("uv_packages and uv_all_packages are mututally exclusive") + if s.venv_backend == "uv": s.env["UV_PROJECT_ENVIRONMENT"] = s.virtualenv.location diff --git a/tests/subworkspace/.python-version b/tests/subworkspace/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/tests/subworkspace/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/tests/subworkspace/README.md b/tests/subworkspace/README.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/subworkspace/noxfile.py b/tests/subworkspace/noxfile.py new file mode 100644 index 0000000..f9ffa12 --- /dev/null +++ b/tests/subworkspace/noxfile.py @@ -0,0 +1,57 @@ +from nox import Session, options + +from nox_uv import session + +options.default_venv_backend = "uv" + +PKG_ONE = "package-one" +PKG_TWO = "package-two" +PKG_ONE_BIN = "package-one-bin" +PKG_TWO_BIN = "package-two-bin" + + +@session(uv_packages=[PKG_ONE]) +def run_package_one_with_its_package(s: Session) -> None: + s.run(PKG_ONE_BIN) + + +@session(uv_packages=[PKG_TWO]) +def run_package_two_with_its_package(s: Session) -> None: + s.run(PKG_TWO_BIN) + + +@session(uv_packages=[PKG_ONE, PKG_TWO]) +def run_both_packages_with_both_explicitly_listed(s: Session) -> None: + s.run(PKG_ONE_BIN) + s.run(PKG_TWO_BIN) + + +@session(uv_all_packages=True) +def run_both_packages_with_all_packages(s: Session) -> None: + s.run(PKG_ONE_BIN) + s.run(PKG_TWO_BIN) + + +@session +def run_package_one_with_no_packages(s: Session) -> None: + s.run(PKG_ONE_BIN) + + +@session +def run_package_two_with_no_packages(s: Session) -> None: + s.run(PKG_TWO_BIN) + + +@session(uv_packages=[PKG_TWO]) +def run_package_one_with_package_two(s: Session) -> None: + s.run(PKG_ONE_BIN) + + +@session(uv_packages=[PKG_ONE]) +def run_package_two_with_package_one(s: Session) -> None: + s.run(PKG_TWO_BIN) + + +@session(uv_packages=[PKG_ONE], uv_all_packages=True) +def run_explicit_and_all_packages(s: Session) -> None: + s.run(PKG_TWO_BIN) diff --git a/tests/subworkspace/package-one/README.md b/tests/subworkspace/package-one/README.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/subworkspace/package-one/pyproject.toml b/tests/subworkspace/package-one/pyproject.toml new file mode 100644 index 0000000..b5c07e9 --- /dev/null +++ b/tests/subworkspace/package-one/pyproject.toml @@ -0,0 +1,23 @@ +[build-system] +build-backend = "uv_build" +requires = [ "uv-build>=0.9,<0.10" ] + +[project] +name = "package-one" +version = "0.1.0" +description = "Project for testing nox-uv" +readme = "README.md" +requires-python = ">=3.9" +classifiers = [ + "Private :: Do Not Upload", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] + +[project.scripts] +package-one-bin = "package_one.main:main" diff --git a/tests/subworkspace/package-one/src/package_one/__init__.py b/tests/subworkspace/package-one/src/package_one/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/subworkspace/package-one/src/package_one/main.py b/tests/subworkspace/package-one/src/package_one/main.py new file mode 100644 index 0000000..d4f625d --- /dev/null +++ b/tests/subworkspace/package-one/src/package_one/main.py @@ -0,0 +1,6 @@ +def main() -> None: + print("Hello from package-one!") + + +if __name__ == "__main__": + main() diff --git a/tests/subworkspace/package-two/README.md b/tests/subworkspace/package-two/README.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/subworkspace/package-two/pyproject.toml b/tests/subworkspace/package-two/pyproject.toml new file mode 100644 index 0000000..9c24e50 --- /dev/null +++ b/tests/subworkspace/package-two/pyproject.toml @@ -0,0 +1,23 @@ +[build-system] +build-backend = "uv_build" +requires = [ "uv-build>=0.9,<0.10" ] + +[project] +name = "package-two" +version = "0.1.0" +description = "Project for testing nox-uv" +readme = "README.md" +requires-python = ">=3.9" +classifiers = [ + "Private :: Do Not Upload", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] + +[project.scripts] +package-two-bin = "package_two.main:main" diff --git a/tests/subworkspace/package-two/src/package_two/__init__.py b/tests/subworkspace/package-two/src/package_two/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/subworkspace/package-two/src/package_two/main.py b/tests/subworkspace/package-two/src/package_two/main.py new file mode 100644 index 0000000..7922468 --- /dev/null +++ b/tests/subworkspace/package-two/src/package_two/main.py @@ -0,0 +1,6 @@ +def main() -> None: + print("Hello from package-two!") + + +if __name__ == "__main__": + main() diff --git a/tests/subworkspace/pyproject.toml b/tests/subworkspace/pyproject.toml new file mode 100644 index 0000000..1c051b2 --- /dev/null +++ b/tests/subworkspace/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "subworkspace" +version = "0.1.0" +description = "Workspace for testing nox-uv" +readme = "README.md" +requires-python = ">=3.9" +classifiers = [ + "Private :: Do Not Upload", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] +dependencies = ["nox-uv"] + +[tool.uv] +sources.nox-uv = { path = "../../", editable = true } + +[tool.uv.workspace] +members = [ + "package-one", + "package-two", +] diff --git a/tests/subworkspace/uv.lock b/tests/subworkspace/uv.lock new file mode 100644 index 0000000..076d5ac --- /dev/null +++ b/tests/subworkspace/uv.lock @@ -0,0 +1,315 @@ +version = 1 +revision = 3 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] + +[manifest] +members = [ + "package-one", + "package-two", + "subworkspace", +] + +[[package]] +name = "argcomplete" +version = "3.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/61/0b9ae6399dd4a58d8c1b1dc5a27d6f2808023d0b5dd3104bb99f45a33ff6/argcomplete-3.6.3.tar.gz", hash = "sha256:62e8ed4fd6a45864acc8235409461b72c9a28ee785a2011cc5eb78318786c89c", size = 73754, upload-time = "2025-10-20T03:33:34.741Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/f5/9373290775639cb67a2fce7f629a1c240dce9f12fe927bc32b2736e16dfc/argcomplete-3.6.3-py3-none-any.whl", hash = "sha256:f5007b3a600ccac5d25bbce33089211dfd49eab4a7718da3f10e3082525a92ce", size = 43846, upload-time = "2025-10-20T03:33:33.021Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "colorlog" +version = "6.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, +] + +[[package]] +name = "dependency-groups" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/55/f054de99871e7beb81935dea8a10b90cd5ce42122b1c3081d5282fdb3621/dependency_groups-1.3.1.tar.gz", hash = "sha256:78078301090517fd938c19f64a53ce98c32834dfe0dee6b88004a569a6adfefd", size = 10093, upload-time = "2025-05-02T00:34:29.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/c7/d1ec24fb280caa5a79b6b950db565dab30210a66259d17d5bb2b3a9f878d/dependency_groups-1.3.1-py3-none-any.whl", hash = "sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030", size = 8664, upload-time = "2025-05-02T00:34:27.085Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, +] + +[[package]] +name = "filelock" +version = "3.25.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, +] + +[[package]] +name = "humanize" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/98/1d/3062fcc89ee05a715c0b9bfe6490c00c576314f27ffee3a704122c6fd259/humanize-4.13.0.tar.gz", hash = "sha256:78f79e68f76f0b04d711c4e55d32bebef5be387148862cb1ef83d2b58e7935a0", size = 81884, upload-time = "2025-08-25T09:39:20.04Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/c7/316e7ca04d26695ef0635dc81683d628350810eb8e9b2299fc08ba49f366/humanize-4.13.0-py3-none-any.whl", hash = "sha256:b810820b31891813b1673e8fec7f1ed3312061eab2f26e3fa192c393d11ed25f", size = 128869, upload-time = "2025-08-25T09:39:18.54Z" }, +] + +[[package]] +name = "humanize" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/66/a3921783d54be8a6870ac4ccffcd15c4dc0dd7fcce51c6d63b8c63935276/humanize-4.15.0.tar.gz", hash = "sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10", size = 83599, upload-time = "2025-12-20T20:16:13.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" }, +] + +[[package]] +name = "nox" +version = "2026.2.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argcomplete" }, + { name = "attrs" }, + { name = "colorlog" }, + { name = "dependency-groups" }, + { name = "humanize", version = "4.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "humanize", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/8e/55a9679b31f1efc48facedd2448eb53c7f1e647fb592aa1403c9dd7a4590/nox-2026.2.9.tar.gz", hash = "sha256:1bc8a202ee8cd69be7aaada63b2a7019126899a06fc930a7aee75585bf8ee41b", size = 4031165, upload-time = "2026-02-10T04:38:58.878Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/58/0d5e5a044f1868bdc45f38afdc2d90ff9867ce398b4e8fa9e666bfc9bfba/nox-2026.2.9-py3-none-any.whl", hash = "sha256:1b7143bc8ecdf25f2353201326152c5303ae4ae56ca097b1fb6179ad75164c47", size = 74615, upload-time = "2026-02-10T04:38:57.266Z" }, +] + +[[package]] +name = "nox-uv" +version = "0.7.2.dev0" +source = { editable = "../../" } +dependencies = [ + { name = "nox" }, +] + +[package.metadata] +requires-dist = [{ name = "nox", specifier = ">=2026.2.9" }] + +[package.metadata.requires-dev] +lint = [ + { name = "pyproject-fmt", specifier = ">=2.8" }, + { name = "ruff", specifier = ">=0.9.7" }, +] +test = [ + { name = "pytest", specifier = ">=8.4" }, + { name = "pytest-cov", specifier = "<7" }, +] +type-check = [{ name = "mypy", specifier = ">=1.15" }] + +[[package]] +name = "package-one" +version = "0.1.0" +source = { editable = "package-one" } + +[[package]] +name = "package-two" +version = "0.1.0" +source = { editable = "package-two" } + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "filelock", version = "3.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "platformdirs", version = "4.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/88/815e53084c5079a59df912825a279f41dd2e0df82281770eadc732f5352c/python_discovery-1.2.1.tar.gz", hash = "sha256:180c4d114bff1c32462537eac5d6a332b768242b76b69c0259c7d14b1b680c9e", size = 58457, upload-time = "2026-03-26T22:30:44.496Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/0f/019d3949a40280f6193b62bc010177d4ce702d0fce424322286488569cd3/python_discovery-1.2.1-py3-none-any.whl", hash = "sha256:b6a957b24c1cd79252484d3566d1b49527581d46e789aaf43181005e56201502", size = 31674, upload-time = "2026-03-26T22:30:43.396Z" }, +] + +[[package]] +name = "subworkspace" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "nox-uv" }, +] + +[package.metadata] +requires-dist = [{ name = "nox-uv", editable = "../../" }] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "filelock", version = "3.25.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "platformdirs", version = "4.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "python-discovery" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, +] diff --git a/tests/test_lib.py b/tests/test_lib.py index b3942bf..7f51df7 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,25 +1,25 @@ -import os from pathlib import Path import subprocess +import pytest + + +@pytest.fixture(autouse=True) +def chdir_workspace(monkeypatch: pytest.MonkeyPatch) -> None: + testing_folder = Path(__file__).parent / "subproject" + monkeypatch.chdir(testing_folder) + def test_1() -> None: assert 5 == 5 def test_run_uv_nox() -> None: - cur_folder = Path.cwd() - testing_folder = Path(__file__).parent / "subproject" - os.chdir(testing_folder) a = subprocess.run(["uv", "run", "python", "-m", "nox"]) assert a.returncode == 0 - os.chdir(cur_folder) def test_run_failed_uv_venv() -> None: - cur_folder = Path.cwd() - testing_folder = Path(__file__).parent / "subproject" - os.chdir(testing_folder) a = subprocess.run( ["uv", "run", "python", "-m", "nox", "-s", "failed_virtualenv"], capture_output=True ) @@ -31,5 +31,3 @@ def test_run_failed_uv_venv() -> None: ) assert a.returncode == 1 # This test is expected to fail with a `Session.error` raised. assert "is not allowed" in a.stderr.decode() - - os.chdir(cur_folder) diff --git a/tests/test_workspace.py b/tests/test_workspace.py new file mode 100644 index 0000000..3d638ab --- /dev/null +++ b/tests/test_workspace.py @@ -0,0 +1,45 @@ +from pathlib import Path +import subprocess + +import pytest + + +@pytest.fixture(autouse=True) +def chdir_workspace(monkeypatch: pytest.MonkeyPatch) -> None: + testing_folder = Path(__file__).parent / "subworkspace" + monkeypatch.chdir(testing_folder) + + +@pytest.mark.parametrize( + "session_name", + [ + "run_package_one_with_its_package", + "run_package_two_with_its_package", + "run_both_packages_with_both_explicitly_listed", + "run_both_packages_with_all_packages", + ], +) +def test_expected_success(session_name: str) -> None: + a = subprocess.run(["uv", "run", "python", "-m", "nox", "-s", session_name]) + assert a.returncode == 0 + + +@pytest.mark.parametrize( + ("session_name", "expect_in_error"), + [ + ("run_package_one_with_no_packages", "package-one-bin not found."), + ("run_package_two_with_no_packages", "package-two-bin not found."), + ("run_package_one_with_package_two", "package-one-bin not found."), + ("run_package_two_with_package_one", "package-two-bin not found."), + ( + "run_explicit_and_all_packages", + "uv_packages and uv_all_packages are mututally exclusive", + ), + ], +) +def test_expected_failure(session_name: str, expect_in_error: str) -> None: + a = subprocess.run( + ["uv", "run", "python", "-m", "nox", "-s", session_name], capture_output=True, text=True + ) + assert a.returncode == 1 + assert expect_in_error in a.stderr From 238c19548e6ed8ac02ed7ddebba2cdbac9b2bcf0 Mon Sep 17 00:00:00 2001 From: John Beisley Date: Wed, 1 Apr 2026 17:43:24 +0100 Subject: [PATCH 2/4] refactor subworkspace tests for composability --- tests/subworkspace/noxfile.py | 52 ++++++++++++--------------------- tests/test_workspace.py | 55 +++++++++++++++++++++++++---------- 2 files changed, 58 insertions(+), 49 deletions(-) diff --git a/tests/subworkspace/noxfile.py b/tests/subworkspace/noxfile.py index f9ffa12..56b1742 100644 --- a/tests/subworkspace/noxfile.py +++ b/tests/subworkspace/noxfile.py @@ -6,52 +6,38 @@ PKG_ONE = "package-one" PKG_TWO = "package-two" -PKG_ONE_BIN = "package-one-bin" -PKG_TWO_BIN = "package-two-bin" -@session(uv_packages=[PKG_ONE]) -def run_package_one_with_its_package(s: Session) -> None: - s.run(PKG_ONE_BIN) - - -@session(uv_packages=[PKG_TWO]) -def run_package_two_with_its_package(s: Session) -> None: - s.run(PKG_TWO_BIN) - - -@session(uv_packages=[PKG_ONE, PKG_TWO]) -def run_both_packages_with_both_explicitly_listed(s: Session) -> None: - s.run(PKG_ONE_BIN) - s.run(PKG_TWO_BIN) - - -@session(uv_all_packages=True) -def run_both_packages_with_all_packages(s: Session) -> None: - s.run(PKG_ONE_BIN) - s.run(PKG_TWO_BIN) +def _run_binaries(s: Session) -> None: + for run_binary in s.posargs: + s.run(run_binary) @session -def run_package_one_with_no_packages(s: Session) -> None: - s.run(PKG_ONE_BIN) +def run_with_no_packages(s: Session) -> None: + _run_binaries(s) -@session -def run_package_two_with_no_packages(s: Session) -> None: - s.run(PKG_TWO_BIN) +@session(uv_packages=[PKG_ONE]) +def run_with_package_one(s: Session) -> None: + _run_binaries(s) @session(uv_packages=[PKG_TWO]) -def run_package_one_with_package_two(s: Session) -> None: - s.run(PKG_ONE_BIN) +def run_with_package_two(s: Session) -> None: + _run_binaries(s) -@session(uv_packages=[PKG_ONE]) -def run_package_two_with_package_one(s: Session) -> None: - s.run(PKG_TWO_BIN) +@session(uv_packages=[PKG_ONE, PKG_TWO]) +def run_with_packages_one_and_two(s: Session) -> None: + _run_binaries(s) + + +@session(uv_all_packages=True) +def run_with_all_packages(s: Session) -> None: + _run_binaries(s) @session(uv_packages=[PKG_ONE], uv_all_packages=True) def run_explicit_and_all_packages(s: Session) -> None: - s.run(PKG_TWO_BIN) + _run_binaries(s) diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 3d638ab..4061466 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -3,6 +3,11 @@ import pytest +PKG_ONE_BIN = "package-one-bin" +PKG_TWO_BIN = "package-two-bin" +PKG_ONE_GREETING = "Hello from package-one!" +PKG_TWO_GREETING = "Hello from package-two!" + @pytest.fixture(autouse=True) def chdir_workspace(monkeypatch: pytest.MonkeyPatch) -> None: @@ -11,35 +16,53 @@ def chdir_workspace(monkeypatch: pytest.MonkeyPatch) -> None: @pytest.mark.parametrize( - "session_name", + ("session_name", "run_binary", "expect_in_stdout"), [ - "run_package_one_with_its_package", - "run_package_two_with_its_package", - "run_both_packages_with_both_explicitly_listed", - "run_both_packages_with_all_packages", + ("run_with_package_one", PKG_ONE_BIN, PKG_ONE_GREETING), + ("run_with_package_two", PKG_TWO_BIN, PKG_TWO_GREETING), + ("run_with_packages_one_and_two", PKG_ONE_BIN, PKG_ONE_GREETING), + ("run_with_packages_one_and_two", PKG_TWO_BIN, PKG_TWO_GREETING), + ("run_with_all_packages", PKG_ONE_BIN, PKG_ONE_GREETING), + ("run_with_all_packages", PKG_TWO_BIN, PKG_TWO_GREETING), ], ) -def test_expected_success(session_name: str) -> None: - a = subprocess.run(["uv", "run", "python", "-m", "nox", "-s", session_name]) - assert a.returncode == 0 +def test_expected_success( + session_name: str, + run_binary: str, + expect_in_stdout: str, +) -> None: + a = subprocess.run( + ["uv", "run", "python", "-m", "nox", "-s", session_name, "--", run_binary], + capture_output=True, + text=True, + ) + assert a.returncode == 0, a.stderr + assert expect_in_stdout in a.stdout @pytest.mark.parametrize( - ("session_name", "expect_in_error"), + ("session_name", "run_binary", "expect_in_stderr"), [ - ("run_package_one_with_no_packages", "package-one-bin not found."), - ("run_package_two_with_no_packages", "package-two-bin not found."), - ("run_package_one_with_package_two", "package-one-bin not found."), - ("run_package_two_with_package_one", "package-two-bin not found."), + ("run_with_no_packages", PKG_ONE_BIN, "package-one-bin not found."), + ("run_with_no_packages", PKG_TWO_BIN, "package-two-bin not found."), + ("run_with_package_one", PKG_TWO_BIN, "package-two-bin not found."), + ("run_with_package_two", PKG_ONE_BIN, "package-one-bin not found."), ( "run_explicit_and_all_packages", + PKG_ONE_BIN, "uv_packages and uv_all_packages are mututally exclusive", ), ], ) -def test_expected_failure(session_name: str, expect_in_error: str) -> None: +def test_expected_failure( + session_name: str, + run_binary: str, + expect_in_stderr: str, +) -> None: a = subprocess.run( - ["uv", "run", "python", "-m", "nox", "-s", session_name], capture_output=True, text=True + ["uv", "run", "python", "-m", "nox", "-s", session_name, "--", run_binary], + capture_output=True, + text=True, ) assert a.returncode == 1 - assert expect_in_error in a.stderr + assert expect_in_stderr in a.stderr From 492b67de59742f96ec23422c491ff360ec354f03 Mon Sep 17 00:00:00 2001 From: John Beisley Date: Thu, 2 Apr 2026 08:06:27 +0100 Subject: [PATCH 3/4] Refactor tests to use subprocess cwd rather than calling chdir. --- tests/test_lib.py | 21 +++++++++++---------- tests/test_workspace.py | 8 +++----- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/test_lib.py b/tests/test_lib.py index 7f51df7..ffe29ca 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -1,13 +1,7 @@ from pathlib import Path import subprocess -import pytest - - -@pytest.fixture(autouse=True) -def chdir_workspace(monkeypatch: pytest.MonkeyPatch) -> None: - testing_folder = Path(__file__).parent / "subproject" - monkeypatch.chdir(testing_folder) +TESTING_FOLDER = Path(__file__).parent / "subproject" def test_1() -> None: @@ -15,19 +9,26 @@ def test_1() -> None: def test_run_uv_nox() -> None: - a = subprocess.run(["uv", "run", "python", "-m", "nox"]) + a = subprocess.run( + ["uv", "run", "python", "-m", "nox"], + cwd=TESTING_FOLDER, + ) assert a.returncode == 0 def test_run_failed_uv_venv() -> None: a = subprocess.run( - ["uv", "run", "python", "-m", "nox", "-s", "failed_virtualenv"], capture_output=True + ["uv", "run", "python", "-m", "nox", "-s", "failed_virtualenv"], + capture_output=True, + cwd=TESTING_FOLDER, ) assert a.returncode == 1 # This test is expected to fail with a `Session.error` raised. assert "is not allowed" in a.stderr.decode() a = subprocess.run( - ["uv", "run", "python", "-m", "nox", "-s", "failed_venv_none"], capture_output=True + ["uv", "run", "python", "-m", "nox", "-s", "failed_venv_none"], + capture_output=True, + cwd=TESTING_FOLDER, ) assert a.returncode == 1 # This test is expected to fail with a `Session.error` raised. assert "is not allowed" in a.stderr.decode() diff --git a/tests/test_workspace.py b/tests/test_workspace.py index 4061466..0695009 100644 --- a/tests/test_workspace.py +++ b/tests/test_workspace.py @@ -8,11 +8,7 @@ PKG_ONE_GREETING = "Hello from package-one!" PKG_TWO_GREETING = "Hello from package-two!" - -@pytest.fixture(autouse=True) -def chdir_workspace(monkeypatch: pytest.MonkeyPatch) -> None: - testing_folder = Path(__file__).parent / "subworkspace" - monkeypatch.chdir(testing_folder) +TESTING_FOLDER = Path(__file__).parent / "subworkspace" @pytest.mark.parametrize( @@ -35,6 +31,7 @@ def test_expected_success( ["uv", "run", "python", "-m", "nox", "-s", session_name, "--", run_binary], capture_output=True, text=True, + cwd=TESTING_FOLDER, ) assert a.returncode == 0, a.stderr assert expect_in_stdout in a.stdout @@ -63,6 +60,7 @@ def test_expected_failure( ["uv", "run", "python", "-m", "nox", "-s", session_name, "--", run_binary], capture_output=True, text=True, + cwd=TESTING_FOLDER, ) assert a.returncode == 1 assert expect_in_stderr in a.stderr From f0ff1dbb1abba38b63e5c9477098a9bdb7b6eeb4 Mon Sep 17 00:00:00 2001 From: John Beisley Date: Wed, 6 May 2026 14:04:12 +0100 Subject: [PATCH 4/4] chore: bump all build-systems to later uv-build version --- pyproject.toml | 2 +- tests/subproject/pyproject.toml | 2 +- tests/subworkspace/package-one/pyproject.toml | 2 +- tests/subworkspace/package-two/pyproject.toml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ba3673e..6eb02d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] build-backend = "uv_build" -requires = [ "uv-build>=0.9,<0.12" ] +requires = [ "uv-build>=0.11,<0.12" ] [project] name = "nox-uv" diff --git a/tests/subproject/pyproject.toml b/tests/subproject/pyproject.toml index 4276574..5369d1c 100644 --- a/tests/subproject/pyproject.toml +++ b/tests/subproject/pyproject.toml @@ -1,6 +1,6 @@ [build-system] build-backend = "uv_build" -requires = [ "uv-build>=0.9,<0.12" ] +requires = [ "uv-build>=0.11,<0.12" ] [project] name = "subproject" diff --git a/tests/subworkspace/package-one/pyproject.toml b/tests/subworkspace/package-one/pyproject.toml index b5c07e9..54080b0 100644 --- a/tests/subworkspace/package-one/pyproject.toml +++ b/tests/subworkspace/package-one/pyproject.toml @@ -1,6 +1,6 @@ [build-system] build-backend = "uv_build" -requires = [ "uv-build>=0.9,<0.10" ] +requires = [ "uv-build>=0.11,<0.12" ] [project] name = "package-one" diff --git a/tests/subworkspace/package-two/pyproject.toml b/tests/subworkspace/package-two/pyproject.toml index 9c24e50..5aac2a9 100644 --- a/tests/subworkspace/package-two/pyproject.toml +++ b/tests/subworkspace/package-two/pyproject.toml @@ -1,6 +1,6 @@ [build-system] build-backend = "uv_build" -requires = [ "uv-build>=0.9,<0.10" ] +requires = [ "uv-build>=0.11,<0.12" ] [project] name = "package-two"