Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions bson/decimal128.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@

import decimal
import struct
from decimal import Decimal
from typing import Any, Sequence, Tuple, Type, Union

from bson.codec_options import TypeDecoder, TypeEncoder

_PACK_64 = struct.Struct("<Q").pack
_UNPACK_64 = struct.Struct("<Q").unpack

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


class DecimalEncoder(TypeEncoder):
"""Converts Python :class:`decimal.Decimal` to BSON :class:`Decimal128`.

For example::
opts = CodecOptions(type_registry=TypeRegistry([DecimalEncoder()]))
bson.encode({"d": decimal.Decimal('1.0')}, codec_options=opts)

.. versionadded:: 4.15
"""

@property
def python_type(self) -> Type[Decimal]:
return Decimal

def transform_python(self, value: Any) -> Decimal128:
return Decimal128(value)


class DecimalDecoder(TypeDecoder):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a docstring example showing out to use these classes?

"""Converts BSON :class:`Decimal128` to Python :class:`decimal.Decimal`.

For example::
opts = CodecOptions(type_registry=TypeRegistry([DecimalDecoder()]))
bson.decode(data, codec_options=opts)

.. versionadded:: 4.15
"""

@property
def bson_type(self) -> Type[Decimal128]:
return Decimal128

def transform_bson(self, value: Any) -> decimal.Decimal:
return value.to_decimal()


def create_decimal128_context() -> decimal.Context:
"""Returns an instance of :class:`decimal.Context` appropriate
for working with IEEE-754 128-bit decimal floating point values.
Expand Down
7 changes: 7 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
Changelog
=========
Changes in Version 4.15.0 (XXXX/XX/XX)
--------------------------------------
PyMongo 4.15 brings a number of changes including:

- Added :class:`bson.decimal128.DecimalEncoder` and :class:`bson.decimal128.DecimalDecoder`
to support encoding and decoding of BSON Decimal128 values to decimal.Decimal values using the TypeRegistry API.

Changes in Version 4.14.1 (2025/08/19)
--------------------------------------

Expand Down
25 changes: 2 additions & 23 deletions test/asynchronous/test_custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from random import random
from typing import Any, Tuple, Type, no_type_check

from bson.decimal128 import DecimalDecoder, DecimalEncoder
from gridfs.asynchronous.grid_file import AsyncGridIn, AsyncGridOut

sys.path[0:0] = [""]
Expand Down Expand Up @@ -59,29 +60,7 @@
_IS_SYNC = False


class DecimalEncoder(TypeEncoder):
@property
def python_type(self):
return Decimal

def transform_python(self, value):
return Decimal128(value)


class DecimalDecoder(TypeDecoder):
@property
def bson_type(self):
return Decimal128

def transform_bson(self, value):
return value.to_decimal()


class DecimalCodec(DecimalDecoder, DecimalEncoder):
pass


DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalCodec()]))
DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalEncoder(), DecimalDecoder()]))


class UndecipherableInt64Type:
Expand Down
25 changes: 2 additions & 23 deletions test/test_custom_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from random import random
from typing import Any, Tuple, Type, no_type_check

from bson.decimal128 import DecimalDecoder, DecimalEncoder
from gridfs.synchronous.grid_file import GridIn, GridOut

sys.path[0:0] = [""]
Expand Down Expand Up @@ -59,29 +60,7 @@
_IS_SYNC = True


class DecimalEncoder(TypeEncoder):
@property
def python_type(self):
return Decimal

def transform_python(self, value):
return Decimal128(value)


class DecimalDecoder(TypeDecoder):
@property
def bson_type(self):
return Decimal128

def transform_bson(self, value):
return value.to_decimal()


class DecimalCodec(DecimalDecoder, DecimalEncoder):
pass


DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalCodec()]))
DECIMAL_CODECOPTS = CodecOptions(type_registry=TypeRegistry([DecimalEncoder(), DecimalDecoder()]))


class UndecipherableInt64Type:
Expand Down
Loading