Skip to content

Commit 2dc4de4

Browse files
Allow untyped dicts in get_file_contents (#112)
* Fix docstring and allow untyped dicts in get_file_contents
1 parent 3370164 commit 2dc4de4

File tree

3 files changed

+65
-18
lines changed

3 files changed

+65
-18
lines changed

src/daq_config_server/client.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import operator
2-
from collections import defaultdict
32
from logging import Logger, getLogger
43
from pathlib import Path
5-
from typing import Any, TypeVar
4+
from typing import Any, TypeVar, get_origin
65

76
import requests
87
from cachetools import TTLCache, cachedmethod
@@ -17,17 +16,16 @@
1716
T = TypeVar("T", str, bytes, dict[Any, Any])
1817

1918

20-
return_type_to_mime_type: dict[type, ValidAcceptHeaders] = defaultdict(
21-
lambda: ValidAcceptHeaders.PLAIN_TEXT,
22-
{
23-
dict[Any, Any]: ValidAcceptHeaders.JSON,
24-
str: ValidAcceptHeaders.PLAIN_TEXT,
25-
bytes: ValidAcceptHeaders.RAW_BYTES,
26-
},
27-
)
19+
class TypeConversionException(Exception): ...
2820

2921

30-
class TypeConversionException(Exception): ...
22+
def _get_mime_type(requested_return_type: type[T]) -> ValidAcceptHeaders:
23+
# Get correct mapping for typed dict or plain dict
24+
if get_origin(requested_return_type) is dict or requested_return_type is dict:
25+
return ValidAcceptHeaders.JSON
26+
elif requested_return_type is bytes:
27+
return ValidAcceptHeaders.RAW_BYTES
28+
return ValidAcceptHeaders.PLAIN_TEXT
3129

3230

3331
class ConfigServer:
@@ -143,22 +141,24 @@ def get_file_contents(
143141
Get contents of a file from the config server in the format specified.
144142
Optionally look for cached result before making request.
145143
146-
Current supported return types are: str, bytes, dict[str, str]. This option will
147-
determine how the server attempts to decode the file
144+
Current supported return types are: str, bytes, dict. This option will
145+
determine how the server attempts to decode the file. Note that only untyped
146+
dictionaries are currently supported
148147
149148
Args:
150149
file_path: Path to the file.
151-
requested_response_format: Specify how to parse the response.
152-
desired_return_type: If true, make a request and store response in cache,
150+
desired_return_type: Specify how to parse the response.
151+
reset_cached_result: If true, make a request and store response in cache,
153152
otherwise look for cached response before making
154153
new request
155154
Returns:
156155
The file contents, in the format specified.
157156
"""
158157
file_path = Path(file_path)
159-
accept_header = return_type_to_mime_type[desired_return_type]
160158

161-
return TypeAdapter(desired_return_type).validate_python( # type: ignore - to allow any dict
159+
accept_header = _get_mime_type(desired_return_type)
160+
161+
return TypeAdapter(desired_return_type).validate_python(
162162
self._get(
163163
ENDPOINTS.CONFIG,
164164
accept_header,

tests/system_tests/test_client.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ def test_read_good_json_as_dict(server: ConfigServer):
6363
)
6464

6565

66+
@pytest.mark.requires_local_server
67+
def test_read_good_json_as_untyped_dict(server: ConfigServer):
68+
with open(TestDataPaths.TEST_GOOD_JSON_PATH) as f:
69+
expected_response = json.loads(f.read())
70+
71+
assert (
72+
server.get_file_contents(
73+
ServerFilePaths.GOOD_JSON_FILE,
74+
dict,
75+
)
76+
== expected_response
77+
)
78+
79+
6680
@pytest.mark.requires_local_server
6781
def test_bad_json_gives_http_error_with_details(server: ConfigServer):
6882
file_path = ServerFilePaths.BAD_JSON_FILE

tests/unit_tests/test_client.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
from httpx import Response
99

1010
from daq_config_server.app import ValidAcceptHeaders
11-
from daq_config_server.client import ConfigServer, TypeConversionException
11+
from daq_config_server.client import (
12+
ConfigServer,
13+
T,
14+
TypeConversionException,
15+
_get_mime_type,
16+
)
1217
from daq_config_server.constants import ENDPOINTS
1318
from daq_config_server.testing import make_test_response
1419

@@ -109,3 +114,31 @@ def test_bad_responses_with_details_raises_error(mock_request: MagicMock):
109114
with pytest.raises(requests.exceptions.HTTPError):
110115
server.get_file_contents(test_path)
111116
server._log.error.assert_called_once_with(detail)
117+
118+
119+
@patch("daq_config_server.client.requests.get")
120+
def test_get_file_contents_with_untyped_dict(mock_request: MagicMock):
121+
content_type = ValidAcceptHeaders.JSON
122+
good_json = '{"good_dict":"test"}'
123+
mock_request.return_value = make_test_response(
124+
good_json, content_type=content_type, json_value=good_json
125+
)
126+
url = "url"
127+
server = ConfigServer(url)
128+
assert server.get_file_contents(test_path, desired_return_type=dict) == {
129+
"good_dict": "test"
130+
}
131+
132+
133+
@pytest.mark.parametrize(
134+
"input, expected",
135+
[
136+
(dict, ValidAcceptHeaders.JSON),
137+
(dict[str, bytes], ValidAcceptHeaders.JSON),
138+
(dict[Any, Any], ValidAcceptHeaders.JSON),
139+
(str, ValidAcceptHeaders.PLAIN_TEXT),
140+
(bytes, ValidAcceptHeaders.RAW_BYTES),
141+
],
142+
)
143+
def test_get_mime_type(input: type[T], expected: ValidAcceptHeaders):
144+
assert _get_mime_type(input) == expected

0 commit comments

Comments
 (0)