Coverage for tests/serializers/pydantic/test_json_backend.py: 100%

40 statements  

« prev     ^ index     » next       coverage.py v7.17.3, created at 2026-11-13 05:19 +0000

1"""`fast_depends.pydantic._compat` picks its JSON backend at import time. 

2 

3Which branch runs depends on what happens to be installed, so the module is 

4re-executed here against stubbed `sys.modules` entries instead. Coverage keys 

5line data off the file, so re-executing the real file from disk does measure 

6the real branches, and nothing is imported into the live module tree. 

7""" 

8 

9import importlib.util 1abcdefghijklmno

10import json 1abcdefghijklmno

11import sys 1abcdefghijklmno

12from types import ModuleType 1abcdefghijklmno

13 

14import pytest 1abcdefghijklmno

15 

16import fast_depends.pydantic._compat as live_compat 1abcdefghijklmno

17 

18BACKENDS = ("orjson", "ujson") 1abcdefghijklmno

19 

20 

21def make_backend(name: str) -> ModuleType: 1abcdefghijklmno

22 stub = ModuleType(name) 1abcdefghijklmno

23 stub.loads = json.loads # type: ignore[attr-defined] 1abcdefghijklmno

24 stub.dumps = json.dumps # type: ignore[attr-defined] 1abcdefghijklmno

25 return stub 1abcdefghijklmno

26 

27 

28def reload_compat( 1abcdefghijklmno

29 monkeypatch: pytest.MonkeyPatch, 

30 installed: dict[str, ModuleType], 

31) -> ModuleType: 

32 for name in BACKENDS: 1abcdefghijklmno

33 # a `None` entry makes the import fail the same way a missing 

34 # distribution would 

35 monkeypatch.setitem(sys.modules, name, installed.get(name)) 1abcdefghijklmno

36 

37 spec = importlib.util.spec_from_file_location( 1abcdefghijklmno

38 "fast_depends_compat_probe", 

39 live_compat.__file__, 

40 ) 

41 assert spec is not None 1abcdefghijklmno

42 assert spec.loader is not None 1abcdefghijklmno

43 

44 module = importlib.util.module_from_spec(spec) 1abcdefghijklmno

45 spec.loader.exec_module(module) 1abcdefghijklmno

46 return module 1abcdefghijklmno

47 

48 

49def test_orjson_is_preferred(monkeypatch: pytest.MonkeyPatch) -> None: 1abcdefghijklmno

50 orjson, ujson = make_backend("orjson"), make_backend("ujson") 1abcdefghijklmno

51 

52 compat = reload_compat(monkeypatch, {"orjson": orjson, "ujson": ujson}) 1abcdefghijklmno

53 

54 assert compat.orjson is orjson 1abcdefghijklmno

55 assert compat.ujson is ujson 1abcdefghijklmno

56 assert compat.json_loads is orjson.loads 1abcdefghijklmno

57 # orjson already returns `bytes`, so it is used as is 

58 assert compat.json_dumps is orjson.dumps 1abcdefghijklmno

59 

60 

61def test_ujson_is_used_without_orjson(monkeypatch: pytest.MonkeyPatch) -> None: 1abcdefghijklmno

62 ujson = make_backend("ujson") 1abcdefghijklmno

63 

64 compat = reload_compat(monkeypatch, {"ujson": ujson}) 1abcdefghijklmno

65 

66 assert compat.orjson is None 1abcdefghijklmno

67 assert compat.json_loads is ujson.loads 1abcdefghijklmno

68 # ujson returns `str`, so the wrapper has to encode it 

69 assert compat.json_dumps({"a": 1}) == b'{"a": 1}' 1abcdefghijklmno

70 

71 

72def test_stdlib_json_is_the_fallback(monkeypatch: pytest.MonkeyPatch) -> None: 1abcdefghijklmno

73 compat = reload_compat(monkeypatch, {}) 1abcdefghijklmno

74 

75 assert compat.orjson is None 1abcdefghijklmno

76 assert compat.ujson is None 1abcdefghijklmno

77 assert compat.json_loads is json.loads 1abcdefghijklmno

78 assert compat.json_dumps({"a": 1}) == b'{"a": 1}' 1abcdefghijklmno