Skip to content

Commit e9c12a0

Browse files
committed
add real Traversable test
1 parent e9d168a commit e9c12a0

File tree

1 file changed

+69
-24
lines changed

1 file changed

+69
-24
lines changed

tests/test_source_json.py

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import importlib.resources
66
import json
7+
from importlib.resources.abc import Traversable
78
from pathlib import Path
89

910
import pytest
@@ -135,31 +136,75 @@ def settings_customise_sources(
135136
assert s.model_dump() == {'hello': 'world', 'nested': {'foo': 3, 'bar': 2 if deep_merge else 0}}
136137

137138

138-
def test_traversable_support():
139-
# get Traversable object
140-
tests_package_dir = importlib.resources.files('tests')
141-
json_config_path = tests_package_dir / 'example_test_config.json'
142-
assert json_config_path.is_file()
139+
class TestTraversableSupport:
140+
FILENAME = 'example_test_config.json'
143141

144-
class Settings(BaseSettings):
145-
foobar: str
142+
@pytest.fixture(params=['importlib_resources', 'custom'])
143+
def json_config_path(self, request, tmp_path):
144+
tests_package_dir = importlib.resources.files('tests')
146145

147-
model_config = SettingsConfigDict(
148-
# Traversable is not added in annotation, but is supported
149-
json_file=json_config_path,
150-
)
146+
if request.param == 'importlib_resources':
147+
# get Traversable object using importlib.resources
148+
return tests_package_dir / self.FILENAME
151149

152-
@classmethod
153-
def settings_customise_sources(
154-
cls,
155-
settings_cls: type[BaseSettings],
156-
init_settings: PydanticBaseSettingsSource,
157-
env_settings: PydanticBaseSettingsSource,
158-
dotenv_settings: PydanticBaseSettingsSource,
159-
file_secret_settings: PydanticBaseSettingsSource,
160-
) -> tuple[PydanticBaseSettingsSource, ...]:
161-
return (JsonConfigSettingsSource(settings_cls),)
150+
# Create a custom Traversable implementation
151+
class CustomTraversable(Traversable):
152+
def __init__(self, path):
153+
self._path = path
162154

163-
s = Settings()
164-
# "test" value in file
165-
assert s.foobar == 'test'
155+
def __truediv__(self, child):
156+
return CustomTraversable(self._path / child)
157+
158+
def is_file(self):
159+
return self._path.is_file()
160+
161+
def is_dir(self):
162+
return self._path.is_dir()
163+
164+
def iterdir(self):
165+
raise NotImplementedError('iterdir not implemented for this test')
166+
167+
def open(self, mode='r', *args, **kwargs):
168+
return self._path.open(mode, *args, **kwargs)
169+
170+
def read_bytes(self):
171+
return self._path.read_bytes()
172+
173+
def read_text(self, encoding=None):
174+
return self._path.read_text(encoding=encoding)
175+
176+
@property
177+
def name(self):
178+
return self._path.name
179+
180+
def joinpath(self, *descendants):
181+
return CustomTraversable(self._path.joinpath(*descendants))
182+
183+
custom_traversable = CustomTraversable(tests_package_dir)
184+
return custom_traversable / self.FILENAME
185+
186+
def test_traversable_support(self, json_config_path: Traversable):
187+
assert json_config_path.is_file()
188+
189+
class Settings(BaseSettings):
190+
foobar: str
191+
192+
model_config = SettingsConfigDict(
193+
# Traversable is not added in annotation, but is supported
194+
json_file=json_config_path,
195+
)
196+
197+
@classmethod
198+
def settings_customise_sources(
199+
cls,
200+
settings_cls: type[BaseSettings],
201+
init_settings: PydanticBaseSettingsSource,
202+
env_settings: PydanticBaseSettingsSource,
203+
dotenv_settings: PydanticBaseSettingsSource,
204+
file_secret_settings: PydanticBaseSettingsSource,
205+
) -> tuple[PydanticBaseSettingsSource, ...]:
206+
return (JsonConfigSettingsSource(settings_cls),)
207+
208+
s = Settings()
209+
# "test" value in file
210+
assert s.foobar == 'test'

0 commit comments

Comments
 (0)