Skip to content

Commit a6f6fa4

Browse files
authored
Ensure extra='forbid' is enforced in DotEnvSettingsSource when env_prefix is specified (#218)
1 parent cc6dc25 commit a6f6fa4

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

pydantic_settings/sources.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,12 +640,17 @@ def __call__(self) -> dict[str, Any]:
640640
data: dict[str, Any] = super().__call__()
641641

642642
data_lower_keys: list[str] = []
643+
is_extra_allowed = self.config.get('extra') != 'forbid'
643644
if not self.case_sensitive:
644645
data_lower_keys = [x.lower() for x in data.keys()]
645-
646646
# As `extra` config is allowed in dotenv settings source, We have to
647647
# update data with extra env variabels from dotenv file.
648648
for env_name, env_value in self.env_vars.items():
649+
if not is_extra_allowed and not env_name.startswith(self.env_prefix):
650+
raise SettingsError(
651+
"unable to load environment variables from dotenv file "
652+
f"due to the presence of variables without the specified prefix - '{self.env_prefix}'"
653+
)
649654
if env_name.startswith(self.env_prefix) and env_value is not None:
650655
env_name_without_prefix = env_name[self.env_prefix_len :]
651656
first_key, *_ = env_name_without_prefix.split(self.env_nested_delimiter)

tests/test_settings.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,6 @@ class Settings(BaseSettings):
767767
768768
prefix_b='better string'
769769
prefix_c="best string"
770-
f="random value"
771770
"""
772771

773772

@@ -790,6 +789,54 @@ class Settings(BaseSettings):
790789
assert s.c == 'best string'
791790

792791

792+
prefix_test_env_invalid_file = """\
793+
# this is a comment
794+
prefix_A=good string
795+
# another one, followed by whitespace
796+
797+
prefix_b='better string'
798+
prefix_c="best string"
799+
f="random value"
800+
"""
801+
802+
803+
def test_env_file_with_env_prefix_invalid(tmp_path):
804+
p = tmp_path / '.env'
805+
p.write_text(prefix_test_env_invalid_file)
806+
807+
class Settings(BaseSettings):
808+
a: str
809+
b: str
810+
c: str
811+
812+
model_config = SettingsConfigDict(env_file=p, env_prefix='prefix_')
813+
814+
err_msg = (
815+
"unable to load environment variables from dotenv file "
816+
"due to the presence of variables without the specified prefix - 'prefix_'"
817+
)
818+
with pytest.raises(SettingsError, match=err_msg):
819+
Settings()
820+
821+
822+
def test_ignore_env_file_with_env_prefix_invalid(tmp_path):
823+
p = tmp_path / '.env'
824+
p.write_text(prefix_test_env_invalid_file)
825+
826+
class Settings(BaseSettings):
827+
a: str
828+
b: str
829+
c: str
830+
831+
model_config = SettingsConfigDict(env_file=p, env_prefix='prefix_', extra='ignore')
832+
833+
s = Settings()
834+
835+
assert s.a == 'good string'
836+
assert s.b == 'better string'
837+
assert s.c == 'best string'
838+
839+
793840
def test_env_file_config_case_sensitive(tmp_path):
794841
p = tmp_path / '.env'
795842
p.write_text(test_env_file)

0 commit comments

Comments
 (0)