From 35fb3332d1471585114917d03cbb081aac88f42c Mon Sep 17 00:00:00 2001 From: amadfida <2160973+amadfida@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:41:04 -0700 Subject: [PATCH] Release settings class during interpreter cleanup --- src/dependency_injector/providers.pyx | 2 +- .../configuration/test_settings_shutdown.py | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/unit/providers/configuration/test_settings_shutdown.py diff --git a/src/dependency_injector/providers.pyx b/src/dependency_injector/providers.pyx index 92ea4f37..d583aa60 100644 --- a/src/dependency_injector/providers.pyx +++ b/src/dependency_injector/providers.pyx @@ -45,7 +45,7 @@ except ImportError: has_pydantic_settings = False cdef bint pydantic_v1 = False -cdef object PydanticSettings = None +PydanticSettings = None cdef str pydantic_module = "pydantic_settings" cdef str pydantic_extra = "pydantic2" diff --git a/tests/unit/providers/configuration/test_settings_shutdown.py b/tests/unit/providers/configuration/test_settings_shutdown.py new file mode 100644 index 00000000..445d943d --- /dev/null +++ b/tests/unit/providers/configuration/test_settings_shutdown.py @@ -0,0 +1,60 @@ +"""Imported settings classes must participate in interpreter cleanup.""" + +import subprocess +import sys +import textwrap + +import pytest + + +@pytest.mark.pydantic +def test_settings_released_after_exit_callbacks(): + result = subprocess.run( + [ + sys.executable, + "-u", + "-c", + textwrap.dedent( + """ + import atexit + import os + + def check_provider(): + config.from_pydantic(Settings(value=43)) + assert config.value() == 43 + print("EXIT_CALLBACK_OK") + + atexit.register(check_provider) + from dependency_injector import providers + try: + from pydantic_settings import BaseSettings + except ImportError: + from pydantic import BaseSettings + + class Marker: + def __del__(self, write=os.write): + write(1, b"SETTINGS_RELEASED\\n") + + BaseSettings.shutdown_test_marker = Marker() + + class Settings(BaseSettings): + value: int = 42 + + config = providers.Configuration() + config.from_pydantic(Settings) + assert config.value() == 42 + atexit.register(check_provider) + """ + ), + ], + capture_output=True, + text=True, + timeout=30, + check=True, + ) + assert result.stdout.splitlines() == [ + "EXIT_CALLBACK_OK", + "EXIT_CALLBACK_OK", + "SETTINGS_RELEASED", + ] + assert result.stderr == ""