Skip to content

Commit 5e96353

Browse files
authored
PYTHON-5508 - Add built-in DecimalEncoder and DecimalDecoder (#2499)
1 parent 9a9a65c commit 5e96353

File tree

4 files changed

+50
-46
lines changed

4 files changed

+50
-46
lines changed

bson/decimal128.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020

2121
import decimal
2222
import struct
23+
from decimal import Decimal
2324
from typing import Any, Sequence, Tuple, Type, Union
2425

26+
from bson.codec_options import TypeDecoder, TypeEncoder
27+
2528
_PACK_64 = struct.Struct("<Q").pack
2629
_UNPACK_64 = struct.Struct("<Q").unpack
2730

@@ -58,6 +61,42 @@
5861
_VALUE_OPTIONS = Union[decimal.Decimal, float, str, Tuple[int, Sequence[int], int]]
5962

6063

64+
class DecimalEncoder(TypeEncoder):
65+
"""Converts Python :class:`decimal.Decimal` to BSON :class:`Decimal128`.
66+
67+
For example::
68+
opts = CodecOptions(type_registry=TypeRegistry([DecimalEncoder()]))
69+
bson.encode({"d": decimal.Decimal('1.0')}, codec_options=opts)
70+
71+
.. versionadded:: 4.15
72+
"""
73+
74+
@property
75+
def python_type(self) -> Type[Decimal]:
76+
return Decimal
77+
78+
def transform_python(self, value: Any) -> Decimal128:
79+
return Decimal128(value)
80+
81+
82+
class DecimalDecoder(TypeDecoder):
83+
"""Converts BSON :class:`Decimal128` to Python :class:`decimal.Decimal`.
84+
85+
For example::
86+
opts = CodecOptions(type_registry=TypeRegistry([DecimalDecoder()]))
87+
bson.decode(data, codec_options=opts)
88+
89+
.. versionadded:: 4.15
90+
"""
91+
92+
@property
93+
def bson_type(self) -> Type[Decimal128]:
94+
return Decimal128
95+
96+
def transform_bson(self, value: Any) -> decimal.Decimal:
97+
return value.to_decimal()
98+
99+
61100
def create_decimal128_context() -> decimal.Context:
62101
"""Returns an instance of :class:`decimal.Context` appropriate
63102
for working with IEEE-754 128-bit decimal floating point values.

doc/changelog.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
Changelog
22
=========
3+
Changes in Version 4.15.0 (XXXX/XX/XX)
4+
--------------------------------------
5+
PyMongo 4.15 brings a number of changes including:
6+
7+
- Added :class:`bson.decimal128.DecimalEncoder` and :class:`bson.decimal128.DecimalDecoder`
8+
to support encoding and decoding of BSON Decimal128 values to decimal.Decimal values using the TypeRegistry API.
9+
310
Changes in Version 4.14.1 (2025/08/19)
411
--------------------------------------
512

test/asynchronous/test_custom_types.py

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from random import random
2424
from typing import Any, Tuple, Type, no_type_check
2525

26+
from bson.decimal128 import DecimalDecoder, DecimalEncoder
2627
from gridfs.asynchronous.grid_file import AsyncGridIn, AsyncGridOut
2728

2829
sys.path[0:0] = [""]
@@ -59,29 +60,7 @@
5960
_IS_SYNC = False
6061

6162

62-
class DecimalEncoder(TypeEncoder):
63-
@property
64-
def python_type(self):
65-
return Decimal
66-
67-
def transform_python(self, value):
68-
return Decimal128(value)
69-
70-
71-
class DecimalDecoder(TypeDecoder):
72-
@property
73-
def bson_type(self):
74-
return Decimal128
75-
76-
def transform_bson(self, value):
77-
return value.to_decimal()
78-
79-
80-
class DecimalCodec(DecimalDecoder, DecimalEncoder):
81-
pass
82-
83-
84-
DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalCodec()]))
63+
DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalEncoder(), DecimalDecoder()]))
8564

8665

8766
class UndecipherableInt64Type:

test/test_custom_types.py

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from random import random
2424
from typing import Any, Tuple, Type, no_type_check
2525

26+
from bson.decimal128 import DecimalDecoder, DecimalEncoder
2627
from gridfs.synchronous.grid_file import GridIn, GridOut
2728

2829
sys.path[0:0] = [""]
@@ -59,29 +60,7 @@
5960
_IS_SYNC = True
6061

6162

62-
class DecimalEncoder(TypeEncoder):
63-
@property
64-
def python_type(self):
65-
return Decimal
66-
67-
def transform_python(self, value):
68-
return Decimal128(value)
69-
70-
71-
class DecimalDecoder(TypeDecoder):
72-
@property
73-
def bson_type(self):
74-
return Decimal128
75-
76-
def transform_bson(self, value):
77-
return value.to_decimal()
78-
79-
80-
class DecimalCodec(DecimalDecoder, DecimalEncoder):
81-
pass
82-
83-
84-
DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalCodec()]))
63+
DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalEncoder(), DecimalDecoder()]))
8564

8665

8766
class UndecipherableInt64Type:

0 commit comments

Comments
 (0)