diff --git a/changes/1067-kierandarcy.md b/changes/1067-kierandarcy.md new file mode 100644 index 0000000..e279e16 --- /dev/null +++ b/changes/1067-kierandarcy.md @@ -0,0 +1 @@ +Change `BaseModel.parse_file` to use `Config.json_loads` diff --git a/pydantic/main.py b/pydantic/main.py index ba49347..1e0c43b 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -425,7 +425,14 @@ class BaseModel(metaclass=ModelMetaclass): proto: Protocol = None, allow_pickle: bool = False, ) -> 'Model': - obj = load_file(path, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle) + obj = load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=cls.__config__.json_loads, + ) return cls.parse_obj(obj) @classmethod diff --git a/pydantic/parse.py b/pydantic/parse.py index e4f3c1a..3d39c3f 100644 --- a/pydantic/parse.py +++ b/pydantic/parse.py @@ -51,6 +51,7 @@ def load_file( encoding: str = 'utf8', proto: Protocol = None, allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, ) -> Any: path = Path(path) b = path.read_bytes() @@ -60,4 +61,6 @@ def load_file( elif path.suffix == '.pkl': proto = Protocol.pickle - return load_str_bytes(b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle) + return load_str_bytes( + b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle, json_loads=json_loads, + ) diff --git a/pydantic/tools.py b/pydantic/tools.py index 1bc4ad9..0f70ea6 100644 --- a/pydantic/tools.py +++ b/pydantic/tools.py @@ -1,3 +1,4 @@ +import json from functools import lru_cache from pathlib import Path from typing import Any, Callable, Optional, Type, TypeVar, Union @@ -42,7 +43,15 @@ def parse_file_as( encoding: str = 'utf8', proto: Protocol = None, allow_pickle: bool = False, + json_loads: Callable[[str], Any] = json.loads, type_name: Optional[NameFactory] = None, ) -> T: - obj = load_file(path, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle) + obj = load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + json_loads=json_loads, + ) return parse_obj_as(type_, obj, type_name=type_name) diff --git a/tests/test_parse.py b/tests/test_parse.py index 7f90179..ba854aa 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -1,3 +1,4 @@ +import json import pickle from typing import List, Union @@ -106,6 +107,24 @@ def test_file_json_no_ext(tmpdir): assert Model.parse_file(str(p)) == Model(a=12, b=8) +def test_file_json_loads(tmp_path): + def custom_json_loads(*args, **kwargs): + data = json.loads(*args, **kwargs) + data['a'] = 99 + return data + + class Example(BaseModel): + a: int + + class Config: + json_loads = custom_json_loads + + p = tmp_path / 'test_json_loads.json' + p.write_text('{"a": 12}') + + assert Example.parse_file(p) == Example(a=99) + + def test_file_pickle(tmpdir): p = tmpdir.join('test.pkl') p.write_binary(pickle.dumps(dict(a=12, b=8))) diff --git a/tests/test_tools.py b/tests/test_tools.py index df4a4db..14854dd 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -1,3 +1,4 @@ +import json from typing import Dict, List, Mapping import pytest @@ -76,3 +77,14 @@ def test_parse_file_as(tmp_path): p = tmp_path / 'test.json' p.write_text('{"1": "2"}') assert parse_file_as(Dict[int, int], p) == {1: 2} + + +def test_parse_file_as_json_loads(tmp_path): + def custom_json_loads(*args, **kwargs): + data = json.loads(*args, **kwargs) + data[1] = 99 + return data + + p = tmp_path / 'test_json_loads.json' + p.write_text('{"1": "2"}') + assert parse_file_as(Dict[int, int], p, json_loads=custom_json_loads) == {1: 99}