CodeSampleX

샘플

python-dotenv 1.2.2: Require follow_symlinks=True in set_key and unset_key to mutate symlink targets without severing symlinks, and handle silent load_dotenv deactivation under PYTHON_DOTENV_DISABLED

검증된 샘플 — pypi python-dotenv 1.2.2: Require follow_symlinks=True in set_key and unset_key to mutate symlink targets without severing symlinks, and handle…

sha256:fc6a02e64d5164eb35ee23154518a592d40222727edf847ce4569e62574b51f3

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. MIT-0

실행 증거

선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.

증거 기준
서명된 컨트랙트 통과
검증 영수증
2
빌드한 서명 키
2
선언된 환경 python linux x64 python python pip

검증 실행 환경

환경 컨트랙트 단계 실행일
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-16
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18

케이스

HOW
목표
Require follow_symlinks=True in set_key and unset_key to mutate symlink targets without severing symlinks, and handle silent load_dotenv deactivation under PYTHON_DOTENV_DISABLED
패키지
심벌
  • dotenv.set_key
  • dotenv.unset_key
  • dotenv.load_dotenv
  • dotenv.dotenv_values
환경
python
생성일
2026-08-16T07:54:34Z

컨트랙트

  1. set_key on a symlinked .env with default follow_symlinks=False replaces the symlink with a regular file and leaves target unmodified
  2. set_key on a symlinked .env with follow_symlinks=True preserves the symlink and mutates the target file
  3. unset_key on a symlinked .env with default follow_symlinks=False replaces the symlink with a regular file and leaves target unmodified
  4. unset_key on a symlinked .env with follow_symlinks=True preserves the symlink and mutates the target file
  5. load_dotenv returns False and skips os.environ population when PYTHON_DOTENV_DISABLED is set to truthy values
  6. dotenv_values parses and returns .env dictionary mappings regardless of PYTHON_DOTENV_DISABLED

파일

  • NOTES.md
  • csx.json
  • pyproject.toml
  • requirements.txt
  • test/contract.py

소스 아티팩트 내려받기 (tar.gz)

소스

NOTES.md
# Notes

- **search_known_solution result**: The network returned no safe match for `pkg:pypi/python-dotenv` (returned unrelated samples for Tokio watch channel and uuid).
- **What a model would have written instead**: A naive model assumes `dotenv.set_key` and `dotenv.unset_key` automatically follow symlinks to mutate the canonical target `.env` file, and that `dotenv.load_dotenv()` always loads file variables into `os.environ`.
- **How the wrong version fails**: Silently with a green build — in `python-dotenv` >= 1.2.2 `set_key` and `unset_key` default to `follow_symlinks=False` which replaces the symlink path with a newly created regular file while leaving the canonical target file silently unmodified, and in >= 1.2.0 `PYTHON_DOTENV_DISABLED=1` causes `load_dotenv()` to silently return `False` without populating `os.environ`.
csx.json
{"case":{"caseId":"case:sha256:3ab5251ae5683d3c5eaf309d08b2e3ee0b6696798ddf507269095081676b7b04","contract":["set_key on a symlinked .env with default follow_symlinks=False replaces the symlink with a regular file and leaves target unmodified","set_key on a symlinked .env with follow_symlinks=True preserves the symlink and mutates the target file","unset_key on a symlinked .env with default follow_symlinks=False replaces the symlink with a regular file and leaves target unmodified","unset_key on a symlinked .env with follow_symlinks=True preserves the symlink and mutates the target file","load_dotenv returns False and skips os.environ population when PYTHON_DOTENV_DISABLED is set to truthy values","dotenv_values parses and returns .env dictionary mappings regardless of PYTHON_DOTENV_DISABLED"],"goal":"Require follow_symlinks=True in set_key and unset_key to mutate symlink targets without severing symlinks, and handle silent load_dotenv deactivation under PYTHON_DOTENV_DISABLED","kind":"HOW","packages":["pkg:pypi/python-dotenv@1.2.2"],"schemaVersion":1,"symbols":["dotenv.set_key","dotenv.unset_key","dotenv.load_dotenv","dotenv.dotenv_values"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/python-dotenv@1.2.2"],"schemaVersion":1,"symbols":["dotenv.set_key","dotenv.unset_key","dotenv.load_dotenv","dotenv.dotenv_values"],"verifierAdapter":"python@1"}
pyproject.toml
[project]
name = "pypi-python-dotenv-version"
version = "1.0.0"
description = "Contract proving python-dotenv symlink mutation and disabled handling across version boundaries"
readme = "NOTES.md"
requires-python = ">=3.8"
dependencies = [
    "python-dotenv==1.2.2",
]
requirements.txt
python-dotenv==1.2.2
test/contract.py
"""
Contract proving python-dotenv >= 1.2.2 behavior changes:
1. set_key and unset_key default to follow_symlinks=False, replacing symlinks with regular files
   and leaving underlying targets unmodified unless follow_symlinks=True is explicitly passed.
2. load_dotenv is silently disabled (returns False without mutating os.environ) when
   PYTHON_DOTENV_DISABLED is set to a truthy value ('1', 'true', 'yes', 'y', 't').
3. dotenv_values continues to parse files directly into dicts regardless of PYTHON_DOTENV_DISABLED.
"""

import os
import tempfile
import dotenv


def test_set_key_symlink_behavior():
    with tempfile.TemporaryDirectory() as td:
        target_path = os.path.join(td, "canonical.env")
        with open(target_path, "w", encoding="utf-8") as f:
            f.write("HOST=api.example.com\nPORT=8000\n")

        link_path = os.path.join(td, ".env")
        os.symlink(target_path, link_path)

        assert os.path.islink(link_path), "Initial setup must be a valid symlink"

        # In python-dotenv >= 1.2.2, default follow_symlinks is False.
        # Calling set_key on a symlink overwrites the symlink path with a new regular file,
        # leaving the canonical target file unmodified.
        success, key, value = dotenv.set_key(link_path, "PORT", "9000")
        assert success is True
        assert key == "PORT"
        assert value == "9000"

        # 1. Symlink is severed and replaced by a regular file
        assert not os.path.islink(link_path), (
            "Default set_key must replace symlink path with a regular file (follow_symlinks=False)"
        )
        assert os.path.isfile(link_path)

        # 2. Canonical target file remains unmodified with old value
        with open(target_path, "r", encoding="utf-8") as f:
            target_content = f.read()
        assert target_content == "HOST=api.example.com\nPORT=8000\n", (
            "Target file must not be modified when follow_symlinks=False"
        )

        # 3. New regular file at link_path contains the updated key
        with open(link_path, "r", encoding="utf-8") as f:
            link_content = f.read()
        assert "PORT='9000'" in link_content


def test_set_key_follow_symlinks():
    with tempfile.TemporaryDirectory() as td:
        target_path = os.path.join(td, "canonical.env")
        with open(target_path, "w", encoding="utf-8") as f:
            f.write("HOST=api.example.com\nPORT=8000\n")

        link_path = os.path.join(td, ".env")
        os.symlink(target_path, link_path)

        # With follow_symlinks=True, target file is updated and symlink remains intact
        success, key, value = dotenv.set_key(
            link_path, "PORT", "9000", follow_symlinks=True
        )
        assert success is True
        assert key == "PORT"
        assert value == "9000"

        assert os.path.islink(link_path), "Symlink must remain intact when follow_symlinks=True"

        with open(target_path, "r", encoding="utf-8") as f:
            target_content = f.read()
        assert "PORT='9000'" in target_content, (
            "Target file must be updated when follow_symlinks=True"
        )


def test_unset_key_symlink_behavior():
    with tempfile.TemporaryDirectory() as td:
        target_path = os.path.join(td, "canonical.env")
        with open(target_path, "w", encoding="utf-8") as f:
            f.write("SERVICE_URL=https://example.com\nDEBUG_MODE=true\n")

        link_path = os.path.join(td, ".env")
        os.symlink(target_path, link_path)

        assert os.path.islink(link_path)

        # In python-dotenv >= 1.2.2, default unset_key severs symlink and does not alter target
        success, key = dotenv.unset_key(link_path, "DEBUG_MODE")
        assert success is True
        assert key == "DEBUG_MODE"

        assert not os.path.islink(link_path), (
            "Default unset_key must replace symlink path with a regular file"
        )

        with open(target_path, "r", encoding="utf-8") as f:
            target_content = f.read()
        assert "DEBUG_MODE=true" in target_content, (
            "Target file must retain key when follow_symlinks=False"
        )

        with open(link_path, "r", encoding="utf-8") as f:
            link_content = f.read()
        assert "DEBUG_MODE" not in link_content


def test_unset_key_follow_symlinks():
    with tempfile.TemporaryDirectory() as td:
        target_path = os.path.join(td, "canonical.env")
        with open(target_path, "w", encoding="utf-8") as f:
            f.write("SERVICE_URL=https://example.com\nDEBUG_MODE=true\n")

        link_path = os.path.join(td, ".env")
        os.symlink(target_path, link_path)

        # With follow_symlinks=True, key is removed from target and symlink is preserved
        success, key = dotenv.unset_key(link_path, "DEBUG_MODE", follow_symlinks=True)
        assert success is True
        assert key == "DEBUG_MODE"

        assert os.path.islink(link_path), "Symlink must remain intact when follow_symlinks=True"

        with open(target_path, "r", encoding="utf-8") as f:
            target_content = f.read()
        assert "DEBUG_MODE" not in target_content, (
            "Target file must have key removed when follow_symlinks=True"
        )


def test_python_dotenv_disabled_handling():
    with tempfile.TemporaryDirectory() as td:
        env_file = os.path.join(td, ".env")
        with open(env_file, "w", encoding="utf-8") as f:
            f.write("APP_ENDPOINT=https://api.example.org\nAPP_TIMEOUT=30\n")

        # Test with truthy values for PYTHON_DOTENV_DISABLED
        for truthy_val in ("1", "true", "True", "YES", "y", "t"):
            os.environ["PYTHON_DOTENV_DISABLED"] = truthy_val
            os.environ.pop("APP_ENDPOINT", None)
            os.environ.pop("APP_TIMEOUT", None)

            try:
                result = dotenv.load_dotenv(env_file)
                # load_dotenv silently no-ops and returns False
                assert result is False, (
                    f"load_dotenv must return False when PYTHON_DOTENV_DISABLED={truthy_val!r}"
                )
                assert "APP_ENDPOINT" not in os.environ, (
                    "os.environ must not be populated when PYTHON_DOTENV_DISABLED is truthy"
                )

                # dotenv_values ignores PYTHON_DOTENV_DISABLED and parses directly
                values = dotenv.dotenv_values(env_file)
                assert values.get("APP_ENDPOINT") == "https://api.example.org", (
                    "dotenv_values must return parsed dict regardless of PYTHON_DOTENV_DISABLED"
                )
                assert values.get("APP_TIMEOUT") == "30"
            finally:
                os.environ.pop("PYTHON_DOTENV_DISABLED", None)
                os.environ.pop("APP_ENDPOINT", None)
                os.environ.pop("APP_TIMEOUT", None)

        # When PYTHON_DOTENV_DISABLED is not set or false, load_dotenv works normally
        os.environ["PYTHON_DOTENV_DISABLED"] = "0"
        try:
            result = dotenv.load_dotenv(env_file)
            assert result is True, "load_dotenv must return True when loading variables"
            assert os.environ.get("APP_ENDPOINT") == "https://api.example.org"
            assert os.environ.get("APP_TIMEOUT") == "30"
        finally:
            os.environ.pop("PYTHON_DOTENV_DISABLED", None)
            os.environ.pop("APP_ENDPOINT", None)
            os.environ.pop("APP_TIMEOUT", None)


if __name__ == "__main__":
    test_set_key_symlink_behavior()
    test_set_key_follow_symlinks()
    test_unset_key_symlink_behavior()
    test_unset_key_follow_symlinks()
    test_python_dotenv_disabled_handling()
    print("Contract passed: all python-dotenv assertions verified successfully.")

오리진 시더

csx-seed