From 6af4fe3104d7201693d61d80065c55094fb033ff Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Mon, 2 Nov 2026 14:20:16 -0700 Subject: [PATCH 1/2] Centralize development dependencies in PEP 735 groups Share test tooling between Nox and the official pipeline while preserving runtime dependency resolution from built wheels. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 5 +- .../workflows/durabletask-azurefunctions.yml | 12 ++--- .../workflows/durabletask-azuremanaged.yml | 8 ++-- .github/workflows/durabletask.yml | 8 ++-- .github/workflows/typecheck.yml | 8 ++-- Makefile | 3 +- dev-requirements.txt | 4 -- docs/development.md | 33 ++++++++++++- eng/templates/build.yml | 6 +-- noxfile.py | 48 ++++++++++--------- pyproject.toml | 42 ++++++++++++++++ requirements.txt | 12 ----- 12 files changed, 124 insertions(+), 65 deletions(-) delete mode 100644 dev-requirements.txt delete mode 100644 requirements.txt diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5c6c4409..83978e8f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -148,7 +148,8 @@ python -m pymarkdown -c .pymarkdown.json scan **/*.md Install the linter via the dev dependencies: ```bash -python -m pip install -r dev-requirements.txt +python -m pip install --upgrade "pip>=25.1" +python -m pip install --group lint ``` ## Building and Testing @@ -203,7 +204,7 @@ activate or modify the per-app `.venv` directories created by Nox. - `azure-functions-durable/` — Azure Durable Functions provider source - `examples/` — example orchestrations (see `examples/README.md`) - `tests/` — test suite -- `dev-requirements.txt` — development dependencies +- `pyproject.toml` `[dependency-groups]` — development and CI tooling dependencies ## External Dependencies diff --git a/.github/workflows/durabletask-azurefunctions.yml b/.github/workflows/durabletask-azurefunctions.yml index accbb5be..14398c5e 100644 --- a/.github/workflows/durabletask-azurefunctions.yml +++ b/.github/workflows/durabletask-azurefunctions.yml @@ -24,8 +24,8 @@ jobs: python-version: 3.13 - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run lint checks run: nox -s lint @@ -47,8 +47,8 @@ jobs: - name: Install Nox run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run unit tests run: nox -s functions_unit-${{ matrix.python-version }} @@ -89,8 +89,8 @@ jobs: - name: Install nox run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run end-to-end tests # nox provisions each sample app's in-app virtual environment (a link to diff --git a/.github/workflows/durabletask-azuremanaged.yml b/.github/workflows/durabletask-azuremanaged.yml index 0c991d59..5986ee74 100644 --- a/.github/workflows/durabletask-azuremanaged.yml +++ b/.github/workflows/durabletask-azuremanaged.yml @@ -24,8 +24,8 @@ jobs: python-version: "3.15" - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run lint checks run: nox -s lint @@ -47,8 +47,8 @@ jobs: - name: Install Nox run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run the tests run: nox -s azuremanaged_tests-${{ matrix.python-version }} diff --git a/.github/workflows/durabletask.yml b/.github/workflows/durabletask.yml index 8b70cff5..57fa5635 100644 --- a/.github/workflows/durabletask.yml +++ b/.github/workflows/durabletask.yml @@ -24,8 +24,8 @@ jobs: python-version: "3.15" - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run lint checks run: nox -s lint @@ -61,8 +61,8 @@ jobs: - name: Install Nox run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Pytest unit tests run: nox -s core_tests-${{ matrix.python-version }} diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 54793cce..920efef6 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -29,8 +29,8 @@ jobs: - name: Install packages and dependencies run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run pyright (strict, Python 3.11) run: nox -s typecheck_core @@ -48,8 +48,8 @@ jobs: - name: Install packages and dependencies run: | - python -m pip install --upgrade pip - pip install nox + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group nox - name: Run pyright (strict, Python 3.13) run: nox -s typecheck_functions diff --git a/Makefile b/Makefile index 56fe20cd..cfa5342a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ init: - pip3 install -r requirements.txt + python3 -m pip install --upgrade "pip>=25.1" + python3 -m pip install --group dev -e ".[azure-blob-payloads,opentelemetry]" -e ./durabletask-azuremanaged test: pytest --verbose diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index b10aeb18..00000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -grpcio-tools -nox -pymarkdownlnt -pyright diff --git a/docs/development.md b/docs/development.md index 3c852ab7..b0f40c7d 100644 --- a/docs/development.md +++ b/docs/development.md @@ -7,10 +7,38 @@ The following is more information about how to develop this project. Use Nox to run the representative local correctness checks: ```sh -python -m pip install -r dev-requirements.txt +python -m pip install --upgrade "pip>=25.1" +python -m pip install --group nox nox -s ci ``` +Development dependencies are defined once in the root `pyproject.toml` under +`[dependency-groups]` (PEP 735). Installing groups requires pip 25.1 or later. +Nox installs the groups required by each session; the official build uses the +same `build`, `lint`, and `test` groups. Add test-only dependencies to `test` +so local and official test environments stay in sync. Keep runtime dependencies +and public extras in each package's `[project]` metadata, not in tooling groups: +the official build resolves them through built wheels to catch missing +declarations. + +For development without Nox, install all tools with `--group dev` and install +the local packages separately. Use Python 3.13 or later for Azure Functions: + +```bash +python -m pip install --group dev \ + -e ".[azure-blob-payloads,opentelemetry]" \ + -e ./durabletask-azuremanaged -e ./azure-functions-durable +``` + +```powershell +python -m pip install --group dev ` + -e ".[azure-blob-payloads,opentelemetry]" ` + -e .\durabletask-azuremanaged -e .\azure-functions-durable +``` + +Sample and Function app `requirements.txt` files remain deployment inputs, not +the source of repository development dependencies. + The `ci` session runs linting, strict type checks, core SDK and Azure Managed tests on Python 3.11, Azure Functions unit tests on Python 3.13, and Azure Functions end-to-end tests on Python 3.13. It intentionally runs one @@ -70,7 +98,8 @@ These commands require `make`. If you're using Windows, you can install `make` using [Chocolatey](https://chocolatey.org/) or use WSL. ```sh -pip3 install -r dev-requirements.txt +python -m pip install --upgrade "pip>=25.1" +python -m pip install --group protobuf make gen-proto ``` diff --git a/eng/templates/build.yml b/eng/templates/build.yml index 6c4f8e40..655833d8 100644 --- a/eng/templates/build.yml +++ b/eng/templates/build.yml @@ -31,8 +31,8 @@ jobs: # that a wheel which accidentally drops a declared dependency surfaces # the missing dep instead of being silently masked. - script: | - python -m pip install --upgrade pip - python -m pip install build flake8 pytest pytest-asyncio + python -m pip install --upgrade "pip>=25.1" + python -m pip install --group build --group lint --group test displayName: "Install build tooling" # Lint core SDK @@ -78,7 +78,7 @@ jobs: # intentional: if a wheel ever accidentally drops a declared # dependency, this step will surface the missing dependency at install # or test time instead of being masked by a preinstalled - # requirements.txt. Test-only tools (pytest, pytest-asyncio) are + # development environment. Test-only tools from the shared test group are # already installed in the "Install build tooling" step above. # # >>> MAINTENANCE NOTE <<< diff --git a/noxfile.py b/noxfile.py index 996f9730..4a5e69a3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -50,6 +50,17 @@ DEFAULT_CI_PYTHON = "3.11" +def _install_groups(session: nox.Session, *groups: str) -> None: + """Install shared tooling without preinstalling SDK runtime dependencies.""" + session.install("pip>=25.1") + arguments = [ + argument + for group in groups + for argument in ("--group", f"{REPO_ROOT / 'pyproject.toml'}:{group}") + ] + session.install(*arguments) + + def _install_packages(session: nox.Session, editable: bool = False) -> None: """Install durabletask and the azure-functions-durable provider. @@ -281,7 +292,7 @@ def _stop_dts_emulator(container_name: str) -> None: @nox.session def lint(session: nox.Session) -> None: """Run the Flake8 checks required by the GitHub Actions workflows.""" - session.install("flake8") + _install_groups(session, "lint") targets = session.posargs or ( "durabletask", "durabletask-azuremanaged", @@ -297,27 +308,19 @@ def lint(session: nox.Session) -> None: @nox.session(python=["3.11"]) def typecheck_core(session: nox.Session) -> None: """Run strict pyright checks for the core and Azure Managed packages.""" - session.install("-r", "requirements.txt") - session.install("-r", "examples/requirements.txt") - for requirements in sorted((REPO_ROOT / "examples").glob("*/requirements.txt")): - session.install("-r", str(requirements)) + _install_groups(session, "test", "typecheck", "examples") session.install( - "--force-reinstall", - "--no-deps", - str(REPO_ROOT), + f"{REPO_ROOT}[azure-blob-payloads,opentelemetry]", str(AZUREMANAGED), ) - session.install(f"{REPO_ROOT}[azure-blob-payloads,opentelemetry]") - session.install("pyright") session.run("pyright", *session.posargs) @nox.session(python=["3.13"]) def typecheck_functions(session: nox.Session) -> None: """Run strict pyright checks for azure-functions-durable.""" - session.install("-r", "requirements.txt") + _install_groups(session, "test", "typecheck") _install_packages(session, editable=True) - session.install("pyright") session.run( "pyright", "-p", @@ -329,7 +332,7 @@ def typecheck_functions(session: nox.Session) -> None: @nox.session(python=["3.13"]) def typecheck_functions_package(session: nox.Session) -> None: """Check the installed Functions and core wheels with strict Pyright.""" - session.install("build", "pyright") + _install_groups(session, "build", "typecheck") temp_dir = Path(session.create_tmp()) artifact_dir = temp_dir / "functions-dist" core_artifact_dir = temp_dir / "core-dist" @@ -389,7 +392,9 @@ def typecheck_functions_package(session: nox.Session) -> None: "-c", "import sys; print(sys.executable)", silent=True, - ).strip() + ) + assert python_path is not None + python_path = python_path.strip() session.run( "pyright", "--pythonpath", @@ -408,8 +413,8 @@ def core_tests(session: nox.Session) -> None: arguments=("--silent", "--blobPort", "10000"), ) try: - session.install("-r", "requirements.txt") - session.install("-e", f"{REPO_ROOT}[azure-blob-payloads]", "aiohttp") + _install_groups(session, "test") + session.install("-e", f"{REPO_ROOT}[azure-blob-payloads,opentelemetry]") arguments = _pytest_arguments("tests/durabletask", session.posargs) session.run( "pytest", @@ -429,8 +434,7 @@ def azuremanaged_tests(session: nox.Session) -> None: try: session.env["TASKHUB"] = "default" session.env["ENDPOINT"] = endpoint - session.install("-r", "requirements.txt") - session.install("-r", "examples/requirements.txt") + _install_groups(session, "test") session.install( "-e", str(REPO_ROOT), @@ -449,9 +453,8 @@ def azuremanaged_tests(session: nox.Session) -> None: @nox.session(python=["3.13", "3.14", "3.15"]) def functions_unit(session: nox.Session) -> None: """Run the azure-functions-durable unit tests (no func/azurite required).""" - session.install("-r", "requirements.txt") + _install_groups(session, "test") _install_packages(session, editable=True) - session.install("pytest") arguments = _pytest_arguments("tests/azure-functions-durable", session.posargs) session.run( "pytest", *arguments, @@ -497,10 +500,9 @@ def functions_e2e(session: nox.Session) -> None: "AzureFunctionsJobHost__extensions__durableTask__hubName" ] = _new_test_namespace("nox") session.env["E2E_PAYLOAD_CONTAINER"] = _new_test_namespace("payloads") - session.install("-r", "requirements.txt") + _install_groups(session, "functions-e2e") _install_packages(session, editable=True) - session.install("-e", f"{REPO_ROOT}[azure-blob-payloads]", "aiohttp") - session.install("pytest", "opentelemetry-exporter-otlp-proto-grpc") + session.install("-e", f"{REPO_ROOT}[azure-blob-payloads]") for app in E2E_APPS: _link_app_venv(session, E2E_APPS_DIR / app) arguments = _pytest_arguments( diff --git a/pyproject.toml b/pyproject.toml index 774e7571..450149f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,48 @@ history-export-azure = [ repository = "https://github.com/microsoft/durabletask-python" changelog = "https://github.com/microsoft/durabletask-python/blob/main/CHANGELOG.md" +# Development tools only: runtime dependencies belong in each package's metadata. +[dependency-groups] +test = [ + "pytest", + "pytest-asyncio", + "pytest-cov", +] +lint = [ + "autopep8", + "flake8", + "pymarkdownlnt", +] +typecheck = [ + "pyright", +] +build = [ + "build", +] +protobuf = [ + "grpcio-tools", +] +nox = [ + "nox", +] +examples = [ + "pydantic>=2", + "opentelemetry-exporter-otlp-proto-grpc", +] +functions-e2e = [ + {include-group = "test"}, + "opentelemetry-exporter-otlp-proto-grpc", +] +dev = [ + {include-group = "test"}, + {include-group = "lint"}, + {include-group = "typecheck"}, + {include-group = "build"}, + {include-group = "protobuf"}, + {include-group = "nox"}, + {include-group = "examples"}, +] + [tool.setuptools.packages.find] include = ["durabletask", "durabletask.*"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b41a0e2f..00000000 --- a/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -autopep8 -grpcio>=1.60.0 # 1.60.0 is the version introducing protobuf 1.25.X support, newer versions are backwards compatible -protobuf -pytest -pytest-asyncio -pytest-cov -azure-identity -azure-functions -typing-extensions -packaging -opentelemetry-api -opentelemetry-sdk \ No newline at end of file From b5b6d4bf9a0ea09c1582c06cfd60d2a94c15fb9d Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Mon, 2 Nov 2026 14:28:09 -0700 Subject: [PATCH 2/2] Use Nox native dependency group resolution Resolve shared groups through nox.project helpers instead of upgrading pip in each session. Require the Nox release that introduced the group helper. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/development.md | 5 +++-- noxfile.py | 9 ++------- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/development.md b/docs/development.md index b0f40c7d..b4ab8979 100644 --- a/docs/development.md +++ b/docs/development.md @@ -13,8 +13,9 @@ nox -s ci ``` Development dependencies are defined once in the root `pyproject.toml` under -`[dependency-groups]` (PEP 735). Installing groups requires pip 25.1 or later. -Nox installs the groups required by each session; the official build uses the +`[dependency-groups]` (PEP 735). Installing groups directly with pip requires +pip 25.1 or later. Nox uses its built-in group resolver to install each session's +tools without upgrading pip inside the session. The official build uses the same `build`, `lint`, and `test` groups. Add test-only dependencies to `test` so local and official test environments stay in sync. Keep runtime dependencies and public extras in each package's `[project]` metadata, not in tooling groups: diff --git a/noxfile.py b/noxfile.py index 4a5e69a3..7dbd8e32 100644 --- a/noxfile.py +++ b/noxfile.py @@ -52,13 +52,8 @@ def _install_groups(session: nox.Session, *groups: str) -> None: """Install shared tooling without preinstalling SDK runtime dependencies.""" - session.install("pip>=25.1") - arguments = [ - argument - for group in groups - for argument in ("--group", f"{REPO_ROOT / 'pyproject.toml'}:{group}") - ] - session.install(*arguments) + pyproject = nox.project.load_toml(REPO_ROOT / "pyproject.toml") + session.install(*nox.project.dependency_groups(pyproject, *groups)) def _install_packages(session: nox.Session, editable: bool = False) -> None: diff --git a/pyproject.toml b/pyproject.toml index 450149f5..2520bab5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,7 +73,7 @@ protobuf = [ "grpcio-tools", ] nox = [ - "nox", + "nox>=2025.2.9", ] examples = [ "pydantic>=2",