From b83ed7d473e9d6cd68b40bd66a56910df5758877 Mon Sep 17 00:00:00 2001 From: Jaseem Jas Date: Mon, 25 May 2026 18:52:22 +0530 Subject: [PATCH 1/2] [FIX] Replace unmaintained pytest-dotenv with direct dotenv loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pytest-dotenv 0.5.2 has not been released since Feb 2020. It monkey-patches private pytest Config internals to inject its env-loading hook, which makes it a recurring break risk across pytest major upgrades (7 → 8 → 9). It also has open issues against newer pytest versions and no path to maintenance. The plugin's only behavior in this repo is a 1-line side effect: read 'env_files' from [tool.pytest.ini_options] and call python-dotenv before collection. Replacing it with a 4-line top-level conftest.py removes a third-party dependency that touches pytest internals, with no behavior change. Backend: - Remove pytest-dotenv from [dependency-groups].test - Remove 'env_files = "test.env"' from [tool.pytest.ini_options] - Add backend/conftest.py that calls load_dotenv(Path(__file__).parent / 'test.env', override=False) (override=False matches pytest-dotenv's default of letting existing env vars win) Prompt-service: - Remove pytest-dotenv from [dependency-groups].test - Add prompt-service/conftest.py that calls load_dotenv(Path(__file__).parent / '.env', override=False) (pytest-dotenv's default is to load '.env' from rootdir when env_files is unset, so this preserves exact previous behavior) python-dotenv is already present as a direct or transitive dependency in both packages (unstract-sdk1 pins python-dotenv==1.0.1, and prompt-service pins it directly), so no new dependency is added. Verified: - prompt-service: 'uv run pytest --collect-only -q' collects 19/19 tests cleanly, matching pre-change count. - conftest.py round-trip: load_dotenv reads .env and populates os.environ for PG_BE_HOST/PG_BE_PORT as expected. Follow-up to review feedback on #1981. --- backend/conftest.py | 20 ++++++++++++++++++++ backend/pyproject.toml | 5 +++-- backend/uv.lock | 21 ++------------------- prompt-service/conftest.py | 18 ++++++++++++++++++ prompt-service/pyproject.toml | 1 - prompt-service/uv.lock | 17 +---------------- 6 files changed, 44 insertions(+), 38 deletions(-) create mode 100644 backend/conftest.py create mode 100644 prompt-service/conftest.py diff --git a/backend/conftest.py b/backend/conftest.py new file mode 100644 index 0000000000..1eac91349b --- /dev/null +++ b/backend/conftest.py @@ -0,0 +1,20 @@ +"""Top-level pytest conftest for the backend service. + +Loads environment variables from ``test.env`` before pytest collects any +test modules. This replaces the unmaintained ``pytest-dotenv`` plugin +(last release: Feb 2020) which monkey-patched private pytest internals +and was a recurring concern across pytest major upgrades. + +Behavior matches the previous ``env_files = "test.env"`` setting in +``[tool.pytest.ini_options]``: variables already set in the process +environment are preserved (``override=False``). +""" + +from pathlib import Path + +from dotenv import load_dotenv + +# Load test.env from this directory (the backend service root). Missing +# file is silently tolerated, matching pytest-dotenv's behavior — tests +# that need specific vars should assert on them in fixtures. +load_dotenv(Path(__file__).parent / "test.env", override=False) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 536b043329..7b520d3a87 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -71,7 +71,7 @@ dev = [ "responses>=0.25.7", "psutil>=7.0.0", ] -test = ["pytest>=8.0.1", "pytest-dotenv==0.5.2"] +test = ["pytest>=8.0.1"] deploy = [ "gunicorn~=23.0", # For serving the application # Keep versions empty and let uv decide version @@ -98,7 +98,8 @@ constraint-dependencies = [ ] [tool.pytest.ini_options] -env_files = "test.env" # Load env from particular env file +# Note: test.env is loaded by backend/conftest.py via python-dotenv directly +# (replaces the unmaintained pytest-dotenv plugin). addopts = "-s" [tool.poe] diff --git a/backend/uv.lock b/backend/uv.lock index 04d6894c1e..8339b5b61a 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = "==3.12.*" [manifest] @@ -2983,19 +2983,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] -[[package]] -name = "pytest-dotenv" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "python-dotenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/b0/cafee9c627c1bae228eb07c9977f679b3a7cb111b488307ab9594ba9e4da/pytest-dotenv-0.5.2.tar.gz", hash = "sha256:2dc6c3ac6d8764c71c6d2804e902d0ff810fa19692e95fe138aefc9b1aa73732", size = 3782, upload-time = "2020-06-16T12:38:03.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/da/9da67c67b3d0963160e3d2cbc7c38b6fae342670cc8e6d5936644b2cf944/pytest_dotenv-0.5.2-py3-none-any.whl", hash = "sha256:40a2cece120a213898afaa5407673f6bd924b1fa7eafce6bda0e8abffe2f710f", size = 3993, upload-time = "2020-06-16T12:38:01.139Z" }, -] - [[package]] name = "python-crontab" version = "3.3.0" @@ -3745,7 +3732,6 @@ dev = [ ] test = [ { name = "pytest" }, - { name = "pytest-dotenv" }, ] [package.metadata] @@ -3809,10 +3795,7 @@ dev = [ { name = "unstract-tool-sandbox", editable = "../unstract/tool-sandbox" }, { name = "unstract-workflow-execution", editable = "../unstract/workflow-execution" }, ] -test = [ - { name = "pytest", specifier = ">=8.0.1" }, - { name = "pytest-dotenv", specifier = "==0.5.2" }, -] +test = [{ name = "pytest", specifier = ">=8.0.1" }] [[package]] name = "unstract-connectors" diff --git a/prompt-service/conftest.py b/prompt-service/conftest.py new file mode 100644 index 0000000000..4872525d7f --- /dev/null +++ b/prompt-service/conftest.py @@ -0,0 +1,18 @@ +"""Top-level pytest conftest for the prompt-service. + +Loads environment variables from ``.env`` before pytest collects any +test modules. This replaces the unmaintained ``pytest-dotenv`` plugin +(last release: Feb 2020) which monkey-patched private pytest internals +and was a recurring concern across pytest major upgrades. + +The prompt-service pyproject did not configure ``env_files``, so the +plugin's default was to load ``.env`` from the rootdir; this preserves +that exact behavior. Variables already set in the process environment +are preserved (``override=False``). +""" + +from pathlib import Path + +from dotenv import load_dotenv + +load_dotenv(Path(__file__).parent / ".env", override=False) diff --git a/prompt-service/pyproject.toml b/prompt-service/pyproject.toml index ae32c95201..5654af6e53 100644 --- a/prompt-service/pyproject.toml +++ b/prompt-service/pyproject.toml @@ -30,7 +30,6 @@ unstract-sdk1 = { path = "../unstract/sdk1", editable = true } test = [ "pytest~=8.0.1", "pytest-asyncio>=0.23.0", - "pytest-dotenv==0.5.2", "pytest-mock~=3.14.0", "pytest-md-report>=0.6.2", "python-dotenv==1.0.1", diff --git a/prompt-service/uv.lock b/prompt-service/uv.lock index b3a8cf1a68..d4ad83a57f 100644 --- a/prompt-service/uv.lock +++ b/prompt-service/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = "==3.12.*" [[package]] @@ -2200,19 +2200,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/82/62e2d63639ecb0fbe8a7ee59ef0bc69a4669ec50f6d3459f74ad4e4189a2/pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2", size = 17663, upload-time = "2024-07-17T17:39:32.478Z" }, ] -[[package]] -name = "pytest-dotenv" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pytest" }, - { name = "python-dotenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/b0/cafee9c627c1bae228eb07c9977f679b3a7cb111b488307ab9594ba9e4da/pytest-dotenv-0.5.2.tar.gz", hash = "sha256:2dc6c3ac6d8764c71c6d2804e902d0ff810fa19692e95fe138aefc9b1aa73732", size = 3782, upload-time = "2020-06-16T12:38:03.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/da/9da67c67b3d0963160e3d2cbc7c38b6fae342670cc8e6d5936644b2cf944/pytest_dotenv-0.5.2-py3-none-any.whl", hash = "sha256:40a2cece120a213898afaa5407673f6bd924b1fa7eafce6bda0e8abffe2f710f", size = 3993, upload-time = "2020-06-16T12:38:01.139Z" }, -] - [[package]] name = "pytest-md-report" version = "0.7.0" @@ -2780,7 +2767,6 @@ test = [ { name = "flask-wtf" }, { name = "pytest" }, { name = "pytest-asyncio" }, - { name = "pytest-dotenv" }, { name = "pytest-md-report" }, { name = "pytest-mock" }, { name = "python-dotenv" }, @@ -2815,7 +2801,6 @@ test = [ { name = "flask-wtf", specifier = "~=1.1" }, { name = "pytest", specifier = "~=8.0.1" }, { name = "pytest-asyncio", specifier = ">=0.23.0" }, - { name = "pytest-dotenv", specifier = "==0.5.2" }, { name = "pytest-md-report", specifier = ">=0.6.2" }, { name = "pytest-mock", specifier = "~=3.14.0" }, { name = "python-dotenv", specifier = "==1.0.1" }, From 189b0d20effda0d63685806cb2bfe40aa811ffe1 Mon Sep 17 00:00:00 2001 From: Jaseem Jas Date: Mon, 25 May 2026 19:59:56 +0530 Subject: [PATCH 2/2] Address PR review: drop prompt-service conftest, restore lock revision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR review on #1992 surfaced three valid issues: 1. HIGH — prompt-service/conftest.py was skipped by tox's --noconftest anyway (tox.ini:62), and pytest-dotenv was effectively dead code in prompt-service to begin with: with no 'env_files' configured, the plugin defaulted to '/.env', and pytest's rootdir resolves to the repo root (where [tool.pytest.ini_options] lives) — which has no .env file. So nothing was being loaded under any path. Drop the conftest entirely; there is no behavior to preserve. 2. MEDIUM — backend/uv.lock and prompt-service/uv.lock had their 'revision' header bumped 2→3 by local uv 0.9.5, but CI pins uv 0.6.14 in .github/workflows/ci-test.yaml:32. Restored the lockfiles from main (revision 2) and surgically removed only the pytest-dotenv entries — diff is now 15 deletions per lock, no revision change, validated against uv 0.6.14 with 'uv lock --check'. 3. LOW — backend/conftest.py docstring softened (pytest-dotenv used the documented pytest_load_initial_conftests hook, not 'monkey-patched private pytest internals'), and the duplicated comment was removed. Added a small print on missing test.env to make a mis-located file debuggable rather than silently empty ('-s' is set in pyproject so prints surface). Also documents that backend tests don't run under tox in CI today, so this file only affects local/IDE runs. Verified: - prompt-service 'uv run pytest --collect-only -q': 19/19 tests collect - 'uv lock --check' on both locks passes under uv 0.6.14 (CI version) --- backend/conftest.py | 24 ++++++++++++++---------- backend/uv.lock | 6 ++++-- prompt-service/conftest.py | 18 ------------------ prompt-service/uv.lock | 2 +- 4 files changed, 19 insertions(+), 31 deletions(-) delete mode 100644 prompt-service/conftest.py diff --git a/backend/conftest.py b/backend/conftest.py index 1eac91349b..16452bb9ef 100644 --- a/backend/conftest.py +++ b/backend/conftest.py @@ -1,20 +1,24 @@ """Top-level pytest conftest for the backend service. Loads environment variables from ``test.env`` before pytest collects any -test modules. This replaces the unmaintained ``pytest-dotenv`` plugin -(last release: Feb 2020) which monkey-patched private pytest internals -and was a recurring concern across pytest major upgrades. +test modules. This replaces the ``pytest-dotenv`` plugin, which has been +unreleased since Feb 2020 and is a recurring break risk across pytest +majors. -Behavior matches the previous ``env_files = "test.env"`` setting in -``[tool.pytest.ini_options]``: variables already set in the process -environment are preserved (``override=False``). +Behavior matches the previous ``env_files = "test.env"`` setting under +``[tool.pytest.ini_options]``: variables already in the process +environment are preserved (``override=False``); a missing file is +silently tolerated. + +Note: backend tests do not run under tox in CI today (no ``backend`` +testenv); this file only takes effect in local/IDE runs. """ from pathlib import Path from dotenv import load_dotenv -# Load test.env from this directory (the backend service root). Missing -# file is silently tolerated, matching pytest-dotenv's behavior — tests -# that need specific vars should assert on them in fixtures. -load_dotenv(Path(__file__).parent / "test.env", override=False) +# `-s` is set in pyproject so prints surface — log when test.env is absent +# to make a mis-located file debuggable instead of silently empty. +if not load_dotenv(Path(__file__).parent / "test.env", override=False): + print("[conftest] backend/test.env not found; using ambient environment", flush=True) diff --git a/backend/uv.lock b/backend/uv.lock index 8339b5b61a..bc5b7c6e7e 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = "==3.12.*" [manifest] @@ -3795,7 +3795,9 @@ dev = [ { name = "unstract-tool-sandbox", editable = "../unstract/tool-sandbox" }, { name = "unstract-workflow-execution", editable = "../unstract/workflow-execution" }, ] -test = [{ name = "pytest", specifier = ">=8.0.1" }] +test = [ + { name = "pytest", specifier = ">=8.0.1" }, +] [[package]] name = "unstract-connectors" diff --git a/prompt-service/conftest.py b/prompt-service/conftest.py deleted file mode 100644 index 4872525d7f..0000000000 --- a/prompt-service/conftest.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Top-level pytest conftest for the prompt-service. - -Loads environment variables from ``.env`` before pytest collects any -test modules. This replaces the unmaintained ``pytest-dotenv`` plugin -(last release: Feb 2020) which monkey-patched private pytest internals -and was a recurring concern across pytest major upgrades. - -The prompt-service pyproject did not configure ``env_files``, so the -plugin's default was to load ``.env`` from the rootdir; this preserves -that exact behavior. Variables already set in the process environment -are preserved (``override=False``). -""" - -from pathlib import Path - -from dotenv import load_dotenv - -load_dotenv(Path(__file__).parent / ".env", override=False) diff --git a/prompt-service/uv.lock b/prompt-service/uv.lock index d4ad83a57f..30eea4ad05 100644 --- a/prompt-service/uv.lock +++ b/prompt-service/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = "==3.12.*" [[package]]