compressedfhir 1.0.0__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of compressedfhir might be problematic. Click here for more details.

@@ -1,30 +1,71 @@
1
1
  import dataclasses
2
2
  import json
3
- from datetime import datetime, date
3
+ import uuid
4
+ from datetime import datetime, date, time
4
5
  from decimal import Decimal
5
6
  from enum import Enum
7
+ from pathlib import Path
6
8
  from typing import Any
7
9
 
10
+ # Optional: Import for additional type support
11
+ try:
12
+ import ipaddress
13
+ except ImportError:
14
+ ipaddress = None # type:ignore[assignment]
15
+
8
16
 
9
17
  class FhirJSONEncoder(json.JSONEncoder):
10
18
  def default(self, o: Any) -> Any:
19
+ # Existing type handlers
11
20
  if dataclasses.is_dataclass(o):
12
21
  return dataclasses.asdict(o) # type:ignore
22
+
13
23
  if isinstance(o, Enum):
14
24
  return o.value
25
+
15
26
  if isinstance(o, Decimal):
16
27
  # Custom Decimal conversion
17
28
  if o == o.to_integral_value():
18
- # If Decimal is a whole number, return as int
19
29
  return int(o)
20
30
  else:
21
- # If Decimal has a non-zero decimal part, return as float
22
31
  return float(o)
32
+
23
33
  if isinstance(o, bytes):
24
34
  return o.decode("utf-8")
35
+
25
36
  if isinstance(o, (datetime, date)):
26
37
  return o.isoformat()
38
+
39
+ if isinstance(o, time):
40
+ return o.isoformat()
41
+
27
42
  if hasattr(o, "to_dict"):
28
- return o.dict()
43
+ return o.to_dict()
44
+
45
+ # New type handlers
46
+
47
+ # UUID handling
48
+ if isinstance(o, uuid.UUID):
49
+ return str(o)
50
+
51
+ # Set and frozenset handling
52
+ if isinstance(o, (set, frozenset)):
53
+ return list(o)
54
+
55
+ # Complex number handling
56
+ if isinstance(o, complex):
57
+ return {"real": o.real, "imag": o.imag}
58
+
59
+ # Path-like objects
60
+ if isinstance(o, (Path, Path)):
61
+ return str(o)
62
+
63
+ # IP Address handling (if ipaddress module is available)
64
+ if ipaddress and isinstance(o, (ipaddress.IPv4Address, ipaddress.IPv6Address)):
65
+ return str(o)
66
+
67
+ # Custom object serialization fallback
68
+ if hasattr(o, "__dict__"):
69
+ return o.__dict__
29
70
 
30
71
  return super().default(o)
File without changes
@@ -0,0 +1,177 @@
1
+ import dataclasses
2
+ import json
3
+ import uuid
4
+ from datetime import datetime, date, time
5
+ from decimal import Decimal
6
+ from enum import Enum
7
+ from pathlib import Path
8
+ from typing import Optional, List
9
+
10
+ import pytest
11
+
12
+ from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
13
+
14
+ # Optional: Import for additional type support
15
+ try:
16
+ import ipaddress
17
+ except ImportError:
18
+ ipaddress = None # type:ignore[assignment]
19
+
20
+
21
+ # Test support classes and enums
22
+ class TestEnum(Enum):
23
+ OPTION1 = "value1"
24
+ OPTION2 = "value2"
25
+
26
+
27
+ @dataclasses.dataclass
28
+ class TestDataclass:
29
+ name: str
30
+ age: int
31
+ optional_field: Optional[str] = None
32
+
33
+
34
+ class TestClassWithToDict:
35
+ # noinspection PyMethodMayBeStatic
36
+ def to_dict(self) -> dict[str, str]:
37
+ return {"key": "value"}
38
+
39
+
40
+ def test_fhir_json_encoder_dataclass() -> None:
41
+ """Test serialization of dataclass"""
42
+ test_obj = TestDataclass(name="John", age=30)
43
+ encoded = json.dumps(test_obj, cls=FhirJSONEncoder)
44
+ decoded = json.loads(encoded)
45
+
46
+ assert decoded == {"name": "John", "age": 30, "optional_field": None}
47
+
48
+
49
+ def test_fhir_json_encoder_enum() -> None:
50
+ """Test serialization of Enum"""
51
+ encoded = json.dumps(TestEnum.OPTION1, cls=FhirJSONEncoder)
52
+ assert encoded == '"value1"'
53
+
54
+
55
+ def test_fhir_json_encoder_decimal() -> None:
56
+ """Test Decimal conversion"""
57
+ # Whole number Decimal
58
+ whole_decimal = Decimal("10")
59
+ encoded_whole = json.dumps(whole_decimal, cls=FhirJSONEncoder)
60
+ assert encoded_whole == "10"
61
+
62
+ # Decimal with fractional part
63
+ frac_decimal = Decimal("10.5")
64
+ encoded_frac = json.dumps(frac_decimal, cls=FhirJSONEncoder)
65
+ assert encoded_frac == "10.5"
66
+
67
+
68
+ def test_fhir_json_encoder_bytes() -> None:
69
+ """Test bytes conversion"""
70
+ test_bytes = b"hello world"
71
+ encoded = json.dumps(test_bytes, cls=FhirJSONEncoder)
72
+ assert encoded == '"hello world"'
73
+
74
+
75
+ def test_fhir_json_encoder_datetime() -> None:
76
+ """Test datetime serialization"""
77
+ test_datetime = datetime(2023, 1, 15, 12, 30, 45)
78
+ encoded = json.dumps(test_datetime, cls=FhirJSONEncoder)
79
+ assert encoded == '"2023-01-15T12:30:45"'
80
+
81
+
82
+ def test_fhir_json_encoder_date() -> None:
83
+ """Test date serialization"""
84
+ test_date = date(2023, 1, 15)
85
+ encoded = json.dumps(test_date, cls=FhirJSONEncoder)
86
+ assert encoded == '"2023-01-15"'
87
+
88
+
89
+ def test_fhir_json_encoder_time() -> None:
90
+ """Test time serialization"""
91
+ test_time = time(12, 30, 45)
92
+ encoded = json.dumps(test_time, cls=FhirJSONEncoder)
93
+ assert encoded == '"12:30:45"'
94
+
95
+
96
+ def test_fhir_json_encoder_to_dict() -> None:
97
+ """Test objects with to_dict method"""
98
+ test_obj = TestClassWithToDict()
99
+ encoded = json.dumps(test_obj, cls=FhirJSONEncoder)
100
+ assert encoded == '{"key": "value"}'
101
+
102
+
103
+ def test_fhir_json_encoder_unsupported_type() -> None:
104
+ """Test fallback for unsupported types"""
105
+
106
+ class UnsupportedType:
107
+ __slots__: List[str] = []
108
+
109
+ with pytest.raises(TypeError):
110
+ json.dumps(UnsupportedType(), cls=FhirJSONEncoder)
111
+
112
+
113
+ def test_extended_json_encoder_uuid() -> None:
114
+ """Test UUID serialization"""
115
+ test_uuid = uuid.uuid4()
116
+ encoded = json.dumps(test_uuid, cls=FhirJSONEncoder)
117
+ assert isinstance(json.loads(encoded), str)
118
+ assert len(json.loads(encoded)) == 36 # Standard UUID string length
119
+
120
+
121
+ def test_extended_json_encoder_set() -> None:
122
+ """Test set and frozenset serialization"""
123
+ test_set = {1, 2, 3}
124
+ test_frozenset = frozenset([4, 5, 6])
125
+
126
+ encoded_set = json.dumps(test_set, cls=FhirJSONEncoder)
127
+ encoded_frozenset = json.dumps(test_frozenset, cls=FhirJSONEncoder)
128
+
129
+ decoded_set = json.loads(encoded_set)
130
+ decoded_frozenset = json.loads(encoded_frozenset)
131
+
132
+ assert set(decoded_set) == test_set
133
+ assert set(decoded_frozenset) == test_frozenset
134
+
135
+
136
+ def test_extended_json_encoder_complex() -> None:
137
+ """Test complex number serialization"""
138
+ test_complex = 3 + 4j
139
+ encoded = json.dumps(test_complex, cls=FhirJSONEncoder)
140
+ decoded = json.loads(encoded)
141
+
142
+ assert decoded == {"real": 3.0, "imag": 4.0}
143
+
144
+
145
+ def test_extended_json_encoder_path() -> None:
146
+ """Test Path object serialization"""
147
+ test_path = Path("/test/path")
148
+ encoded = json.dumps(test_path, cls=FhirJSONEncoder)
149
+ assert json.loads(encoded) == str(test_path)
150
+
151
+
152
+ def test_extended_json_encoder_ip_address() -> None:
153
+ """Test IP Address serialization (if ipaddress module is available)"""
154
+ if ipaddress:
155
+ ipv4 = ipaddress.IPv4Address("192.168.0.1")
156
+ ipv6 = ipaddress.IPv6Address("2001:0db8:85a3:0000:0000:8a2e:0370:7334")
157
+
158
+ encoded_ipv4 = json.dumps(ipv4, cls=FhirJSONEncoder)
159
+ encoded_ipv6 = json.dumps(ipv6, cls=FhirJSONEncoder)
160
+
161
+ assert json.loads(encoded_ipv4) == str(ipv4)
162
+ assert json.loads(encoded_ipv6) == str(ipv6)
163
+
164
+
165
+ def test_extended_json_encoder_custom_object() -> None:
166
+ """Test custom object serialization"""
167
+
168
+ class CustomObject:
169
+ def __init__(self) -> None:
170
+ self.x = 1
171
+ self.y = 2
172
+
173
+ obj = CustomObject()
174
+ encoded = json.dumps(obj, cls=FhirJSONEncoder)
175
+ decoded = json.loads(encoded)
176
+
177
+ assert decoded == {"x": 1, "y": 2}
@@ -0,0 +1,99 @@
1
+ import json
2
+ from datetime import datetime, date
3
+ from typing import List, Dict, Any
4
+
5
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
6
+
7
+
8
+ class TestFhirClientJsonHelpers:
9
+ def test_json_serial(self) -> None:
10
+ # Test datetime serialization
11
+ dt = datetime(2023, 1, 15, 12, 30)
12
+ assert FhirClientJsonHelpers.json_serial(dt) == "2023-01-15T12:30:00"
13
+
14
+ # Test date serialization
15
+ d = date(2023, 1, 15)
16
+ assert FhirClientJsonHelpers.json_serial(d) == "2023-01-15"
17
+
18
+ # Test other types
19
+ assert FhirClientJsonHelpers.json_serial(123) == "123"
20
+ assert FhirClientJsonHelpers.json_serial("test") == "test"
21
+
22
+ def test_remove_empty_elements(self) -> None:
23
+ # Test dictionary removal
24
+ input_dict = {"a": 1, "b": "", "c": None, "d": [], "e": {}, "f": [1, 2, 3]}
25
+ expected_dict = {"a": 1, "f": [1, 2, 3]}
26
+ assert FhirClientJsonHelpers.remove_empty_elements(input_dict) == expected_dict
27
+
28
+ # Test list of dictionaries
29
+ input_list: List[Dict[str, Any]] = [
30
+ {"a": 1, "b": None},
31
+ {"c": [], "d": "test"},
32
+ {"e": {}},
33
+ ]
34
+ expected_list: List[Dict[str, Any]] = [{"a": 1}, {"d": "test"}]
35
+ assert FhirClientJsonHelpers.remove_empty_elements(input_list) == expected_list
36
+
37
+ def test_remove_empty_elements_from_ordered_dict(self) -> None:
38
+ from collections import OrderedDict
39
+
40
+ # Test OrderedDict removal
41
+ input_dict = OrderedDict(
42
+ [("a", 1), ("b", ""), ("c", None), ("d", []), ("e", {}), ("f", [1, 2, 3])]
43
+ )
44
+ expected_dict = OrderedDict([("a", 1), ("f", [1, 2, 3])])
45
+ result: List[OrderedDict[str, Any]] | OrderedDict[str, Any] = (
46
+ FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(input_dict)
47
+ )
48
+ assert result == expected_dict
49
+
50
+ # Test list of OrderedDicts
51
+ input_list: List[OrderedDict[str, Any]] = [
52
+ OrderedDict([("a", 1), ("b", None)]),
53
+ OrderedDict([("c", []), ("d", "test")]),
54
+ OrderedDict([("e", {})]),
55
+ ]
56
+ expected_list: List[OrderedDict[str, Any]] = [
57
+ OrderedDict([("a", 1)]),
58
+ OrderedDict([("d", "test")]),
59
+ ]
60
+ result = FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(
61
+ input_list
62
+ )
63
+ assert result == expected_list
64
+
65
+ def test_convert_dict_to_fhir_json(self) -> None:
66
+ input_dict = {"name": "John Doe", "age": 30, "address": None, "hobbies": []}
67
+ result = FhirClientJsonHelpers.convert_dict_to_fhir_json(input_dict)
68
+ parsed_result = json.loads(result)
69
+ assert parsed_result == {"name": "John Doe", "age": 30}
70
+
71
+ def test_orjson_dumps(self) -> None:
72
+ # Test basic serialization
73
+ data = {"a": 1, "b": "test"}
74
+ result = FhirClientJsonHelpers.orjson_dumps(data)
75
+ assert json.loads(result) == data
76
+
77
+ # Test sorting keys
78
+ result_sorted = FhirClientJsonHelpers.orjson_dumps(data, sort_keys=True)
79
+ assert result_sorted == '{"a":1,"b":"test"}'
80
+
81
+ # Test indentation (limited support)
82
+ result_indent = FhirClientJsonHelpers.orjson_dumps(data, indent=2)
83
+ assert isinstance(result_indent, str)
84
+
85
+ def test_orjson_loads(self) -> None:
86
+ # Test string input
87
+ json_str = '{"a": 1, "b": "test"}'
88
+ result = FhirClientJsonHelpers.orjson_loads(json_str)
89
+ assert result == {"a": 1, "b": "test"}
90
+
91
+ # Test bytes input
92
+ json_bytes = b'{"a": 1, "b": "test"}'
93
+ result = FhirClientJsonHelpers.orjson_loads(json_bytes)
94
+ assert result == {"a": 1, "b": "test"}
95
+
96
+ # Test invalid JSON
97
+ invalid_json = "{invalid json}"
98
+ result = FhirClientJsonHelpers.orjson_loads(invalid_json)
99
+ assert result is None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: compressedfhir
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: Stores FHIR JSON resources in compressed form in memory
5
5
  Home-page: https://github.com/icanbwell/compressed-fhir
6
6
  Author: Imran Qureshi
@@ -24,7 +24,7 @@ compressedfhir/fhir/test/test_fhir_resource.py,sha256=4Fl6QaqjW4CsYqkxVj2WRXITv_
24
24
  compressedfhir/fhir/test/test_fhir_resource_list.py,sha256=SrSPJ1yWU4UgMUCht6JwgKh2Y5JeTS4-Wky0kWZOXH8,5664
25
25
  compressedfhir/fhir/test/test_fhir_resource_map.py,sha256=jtQ5fq_jhmFfhHGyK5mdiwIQiO-Sfp2eG9mco_Tr9Qk,10995
26
26
  compressedfhir/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- compressedfhir/utilities/fhir_json_encoder.py,sha256=vUx0hdrdbo9mVgH7mpwKG1Th96QiDIoPkvL_0ba5SBM,954
27
+ compressedfhir/utilities/fhir_json_encoder.py,sha256=E_g4TI5KOCJcJQ1lkA7WIGwO7wyskP8c80LUqHueI3w,1846
28
28
  compressedfhir/utilities/json_helpers.py,sha256=lEiPapLN0p-kLu6PFm-h971ieXRxwPB2M-8FCZ2Buo8,5642
29
29
  compressedfhir/utilities/compressed_dict/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  compressedfhir/utilities/compressed_dict/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -33,9 +33,12 @@ compressedfhir/utilities/compressed_dict/v1/compressed_dict_access_error.py,sha2
33
33
  compressedfhir/utilities/compressed_dict/v1/compressed_dict_storage_mode.py,sha256=GmwvQX2e9DbdKq2oKnW-VaqbigeJfVqDEhnOBfcbpdA,1402
34
34
  compressedfhir/utilities/compressed_dict/v1/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py,sha256=7AsOX1Nw7Woo9C7OzdBXMXFQhgEBAZZ8py1aHfFh-4k,11970
36
- compressedfhir-1.0.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
36
+ compressedfhir/utilities/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ compressedfhir/utilities/test/test_fhir_json_encoder.py,sha256=6pbNmZp5eBWY66bHjgjm_pZVhs5HDKP8hCGnwNFzpEw,5171
38
+ compressedfhir/utilities/test/test_json_helpers.py,sha256=V0R9oHDQAs0m0012niEz50sHJxMSUQvA3km7kK8HgjE,3860
39
+ compressedfhir-1.0.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
37
40
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- compressedfhir-1.0.0.dist-info/METADATA,sha256=jSzq5lPflqkkcm_NwsiP7W_wXK1EJvZMoGr9HzDHrkc,828
39
- compressedfhir-1.0.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
40
- compressedfhir-1.0.0.dist-info/top_level.txt,sha256=YMKdvBBdiCzFbpI9fG8BUDjaRd-f4R0qAvUoVETpoWw,21
41
- compressedfhir-1.0.0.dist-info/RECORD,,
41
+ compressedfhir-1.0.1.dist-info/METADATA,sha256=MIM533rOXd8pmRe2tNAiqAS_k1H7ccyhIwpBQV5xAvA,828
42
+ compressedfhir-1.0.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
43
+ compressedfhir-1.0.1.dist-info/top_level.txt,sha256=YMKdvBBdiCzFbpI9fG8BUDjaRd-f4R0qAvUoVETpoWw,21
44
+ compressedfhir-1.0.1.dist-info/RECORD,,