Skip to content

Commit 46673c3

Browse files
authored
PYTHON-3379 Refactored DatetimeConversionOpts to DatetimeConversion (#1031)
1 parent 92a6fa7 commit 46673c3

File tree

11 files changed

+59
-69
lines changed

11 files changed

+59
-69
lines changed

bson/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
from bson.codec_options import (
100100
DEFAULT_CODEC_OPTIONS,
101101
CodecOptions,
102-
DatetimeConversionOpts,
102+
DatetimeConversion,
103103
_DocumentType,
104104
_raw_document_class,
105105
)
@@ -194,7 +194,7 @@
194194
"is_valid",
195195
"BSON",
196196
"has_c",
197-
"DatetimeConversionOpts",
197+
"DatetimeConversion",
198198
"DatetimeMS",
199199
]
200200

bson/codec_options.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def __eq__(self, other: Any) -> Any:
199199
)
200200

201201

202-
class DatetimeConversionOpts(enum.IntEnum):
202+
class DatetimeConversion(enum.IntEnum):
203203
"""Options for decoding BSON datetimes."""
204204

205205
DATETIME = 1
@@ -241,7 +241,7 @@ class _BaseCodecOptions(NamedTuple):
241241
unicode_decode_error_handler: str
242242
tzinfo: Optional[datetime.tzinfo]
243243
type_registry: TypeRegistry
244-
datetime_conversion: Optional[DatetimeConversionOpts]
244+
datetime_conversion: Optional[DatetimeConversion]
245245

246246

247247
class CodecOptions(_BaseCodecOptions):
@@ -335,7 +335,7 @@ def __new__(
335335
unicode_decode_error_handler: str = "strict",
336336
tzinfo: Optional[datetime.tzinfo] = None,
337337
type_registry: Optional[TypeRegistry] = None,
338-
datetime_conversion: Optional[DatetimeConversionOpts] = DatetimeConversionOpts.DATETIME,
338+
datetime_conversion: Optional[DatetimeConversion] = DatetimeConversion.DATETIME,
339339
) -> "CodecOptions":
340340
doc_class = document_class or dict
341341
# issubclass can raise TypeError for generic aliases like SON[str, Any].

bson/codec_options.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class TypeRegistry:
5555

5656
_DocumentType = TypeVar("_DocumentType", bound=Mapping[str, Any])
5757

58-
class DatetimeConversionOpts(int, enum.Enum):
58+
class DatetimeConversion(int, enum.Enum):
5959
DATETIME = ...
6060
DATETIME_CLAMP = ...
6161
DATETIME_MS = ...

bson/datetime_ms.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,7 @@
2222
import functools
2323
from typing import Any, Union, cast
2424

25-
from bson.codec_options import (
26-
DEFAULT_CODEC_OPTIONS,
27-
CodecOptions,
28-
DatetimeConversionOpts,
29-
)
25+
from bson.codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, DatetimeConversion
3026
from bson.tz_util import utc
3127

3228
EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc)
@@ -127,14 +123,14 @@ def _max_datetime_ms(tz=datetime.timezone.utc):
127123
def _millis_to_datetime(millis: int, opts: CodecOptions) -> Union[datetime.datetime, DatetimeMS]:
128124
"""Convert milliseconds since epoch UTC to datetime."""
129125
if (
130-
opts.datetime_conversion == DatetimeConversionOpts.DATETIME
131-
or opts.datetime_conversion == DatetimeConversionOpts.DATETIME_CLAMP
132-
or opts.datetime_conversion == DatetimeConversionOpts.DATETIME_AUTO
126+
opts.datetime_conversion == DatetimeConversion.DATETIME
127+
or opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP
128+
or opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO
133129
):
134130
tz = opts.tzinfo or datetime.timezone.utc
135-
if opts.datetime_conversion == DatetimeConversionOpts.DATETIME_CLAMP:
131+
if opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP:
136132
millis = max(_min_datetime_ms(tz), min(millis, _max_datetime_ms(tz)))
137-
elif opts.datetime_conversion == DatetimeConversionOpts.DATETIME_AUTO:
133+
elif opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO:
138134
if not (_min_datetime_ms(tz) <= millis <= _max_datetime_ms(tz)):
139135
return DatetimeMS(millis)
140136

@@ -149,10 +145,10 @@ def _millis_to_datetime(millis: int, opts: CodecOptions) -> Union[datetime.datet
149145
return dt
150146
else:
151147
return EPOCH_NAIVE + datetime.timedelta(seconds=seconds, microseconds=micros)
152-
elif opts.datetime_conversion == DatetimeConversionOpts.DATETIME_MS:
148+
elif opts.datetime_conversion == DatetimeConversion.DATETIME_MS:
153149
return DatetimeMS(millis)
154150
else:
155-
raise ValueError("datetime_conversion must be an element of DatetimeConversionOpts")
151+
raise ValueError("datetime_conversion must be an element of DatetimeConversion")
156152

157153

158154
def _datetime_to_millis(dtm: datetime.datetime) -> int:

bson/json_util.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696

9797
from bson.binary import ALL_UUID_SUBTYPES, UUID_SUBTYPE, Binary, UuidRepresentation
9898
from bson.code import Code
99-
from bson.codec_options import CodecOptions, DatetimeConversionOpts
99+
from bson.codec_options import CodecOptions, DatetimeConversion
100100
from bson.datetime_ms import (
101101
EPOCH_AWARE,
102102
DatetimeMS,
@@ -662,12 +662,12 @@ def _parse_canonical_datetime(
662662
if json_options.tz_aware:
663663
if json_options.tzinfo:
664664
aware = aware.astimezone(json_options.tzinfo)
665-
if json_options.datetime_conversion == DatetimeConversionOpts.DATETIME_MS:
665+
if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS:
666666
return DatetimeMS(aware)
667667
return aware
668668
else:
669669
aware_tzinfo_none = aware.replace(tzinfo=None)
670-
if json_options.datetime_conversion == DatetimeConversionOpts.DATETIME_MS:
670+
if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS:
671671
return DatetimeMS(aware_tzinfo_none)
672672
return aware_tzinfo_none
673673
return _millis_to_datetime(int(dtm), json_options)

doc/examples/datetimes.rst

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ of milliseconds from the Unix epoch. To deal with this, we can use the
119119
To decode UTC datetime values as :class:`~bson.datetime_ms.DatetimeMS`,
120120
:class:`~bson.codec_options.CodecOptions` should have its
121121
``datetime_conversion`` parameter set to one of the options available in
122-
:class:`bson.datetime_ms.DatetimeConversionOpts`. These include
123-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME`,
124-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME_MS`,
125-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME_AUTO`,
126-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME_CLAMP`.
127-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME` is the default
122+
:class:`bson.datetime_ms.DatetimeConversion`. These include
123+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME`,
124+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME_MS`,
125+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME_AUTO`,
126+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME_CLAMP`.
127+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME` is the default
128128
option and has the behavior of raising an :class:`~builtin.OverflowError` upon
129129
attempting to decode an out-of-range date.
130-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME_MS` will only return
130+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME_MS` will only return
131131
:class:`~bson.datetime_ms.DatetimeMS` objects, regardless of whether the
132132
represented datetime is in- or out-of-range:
133133

@@ -136,13 +136,13 @@ represented datetime is in- or out-of-range:
136136
>>> from datetime import datetime
137137
>>> from bson import encode, decode
138138
>>> from bson.datetime_ms import DatetimeMS
139-
>>> from bson.codec_options import CodecOptions, DatetimeConversionOpts
139+
>>> from bson.codec_options import CodecOptions, DatetimeConversion
140140
>>> x = encode({"x": datetime(1970, 1, 1)})
141-
>>> codec_ms = CodecOptions(datetime_conversion=DatetimeConversionOpts.DATETIME_MS)
141+
>>> codec_ms = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_MS)
142142
>>> decode(x, codec_options=codec_ms)
143143
{'x': DatetimeMS(0)}
144144

145-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME_AUTO` will return
145+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME_AUTO` will return
146146
:class:`~datetime.datetime` if the underlying UTC datetime is within range,
147147
or :class:`~bson.datetime_ms.DatetimeMS` if the underlying datetime
148148
cannot be represented using the builtin Python :class:`~datetime.datetime`:
@@ -151,13 +151,13 @@ cannot be represented using the builtin Python :class:`~datetime.datetime`:
151151

152152
>>> x = encode({"x": datetime(1970, 1, 1)})
153153
>>> y = encode({"x": DatetimeMS(-2**62)})
154-
>>> codec_auto = CodecOptions(datetime_conversion=DatetimeConversionOpts.DATETIME_AUTO)
154+
>>> codec_auto = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_AUTO)
155155
>>> decode(x, codec_options=codec_auto)
156156
{'x': datetime.datetime(1970, 1, 1, 0, 0)}
157157
>>> decode(y, codec_options=codec_auto)
158158
{'x': DatetimeMS(-4611686018427387904)}
159159

160-
:attr:`~bson.datetime_ms.DatetimeConversionOpts.DATETIME_CLAMP` will clamp
160+
:attr:`~bson.datetime_ms.DatetimeConversion.DATETIME_CLAMP` will clamp
161161
resulting :class:`~datetime.datetime` objects to be within
162162
:attr:`~datetime.datetime.min` and :attr:`~datetime.datetime.max`
163163
(trimmed to `999000` microseconds):
@@ -166,7 +166,7 @@ resulting :class:`~datetime.datetime` objects to be within
166166

167167
>>> x = encode({"x": DatetimeMS(2**62)})
168168
>>> y = encode({"x": DatetimeMS(-2**62)})
169-
>>> codec_clamp = CodecOptions(datetime_conversion=DatetimeConversionOpts.DATETIME_CLAMP)
169+
>>> codec_clamp = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_CLAMP)
170170
>>> decode(x, codec_options=codec_clamp)
171171
{'x': datetime.datetime(9999, 12, 31, 23, 59, 59, 999000)}
172172
>>> decode(y, codec_options=codec_clamp)

doc/faq.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ collection, configured to use :class:`~bson.son.SON` instead of dict:
264264
>>> from bson import CodecOptions, SON
265265
>>> opts = CodecOptions(document_class=SON)
266266
>>> opts
267-
CodecOptions(document_class=...SON..., tz_aware=False, uuid_representation=UuidRepresentation.UNSPECIFIED, unicode_decode_error_handler='strict', tzinfo=None, type_registry=TypeRegistry(type_codecs=[], fallback_encoder=None), datetime_conversion=DatetimeConversionOpts.DATETIME)
267+
CodecOptions(document_class=...SON..., tz_aware=False, uuid_representation=UuidRepresentation.UNSPECIFIED, unicode_decode_error_handler='strict', tzinfo=None, type_registry=TypeRegistry(type_codecs=[], fallback_encoder=None), datetime_conversion=DatetimeConversion.DATETIME)
268268
>>> collection_son = collection.with_options(codec_options=opts)
269269

270270
Now, documents and subdocuments in query results are represented with
@@ -495,10 +495,10 @@ be specified using the ``datetime_conversion`` parameter of
495495
:class:`~bson.codec_options.CodecOptions`.
496496

497497
The default option is
498-
:attr:`~bson.codec_options.DatetimeConversionOpts.DATETIME`, which will
498+
:attr:`~bson.codec_options.DatetimeConversion.DATETIME`, which will
499499
attempt to decode as a :class:`datetime.datetime`, allowing
500500
:class:`~builtin.OverflowError` to occur upon out-of-range dates.
501-
:attr:`~bson.codec_options.DatetimeConversionOpts.DATETIME_AUTO` alters
501+
:attr:`~bson.codec_options.DatetimeConversion.DATETIME_AUTO` alters
502502
this behavior to instead return :class:`~bson.datetime_ms.DatetimeMS` when
503503
representations are out-of-range, while returning :class:`~datetime.datetime`
504504
objects as before:
@@ -507,9 +507,9 @@ objects as before:
507507

508508
>>> from datetime import datetime
509509
>>> from bson.datetime_ms import DatetimeMS
510-
>>> from bson.codec_options import DatetimeConversionOpts
510+
>>> from bson.codec_options import DatetimeConversion
511511
>>> from pymongo import MongoClient
512-
>>> client = MongoClient(datetime_conversion=DatetimeConversionOpts.DATETIME_AUTO)
512+
>>> client = MongoClient(datetime_conversion=DatetimeConversion.DATETIME_AUTO)
513513
>>> client.db.collection.insert_one({"x": datetime(1970, 1, 1)})
514514
<pymongo.results.InsertOneResult object at 0x...>
515515
>>> client.db.collection.insert_one({"x": DatetimeMS(2**62)})
@@ -520,7 +520,7 @@ objects as before:
520520
{'_id': ObjectId('...'), 'x': DatetimeMS(4611686018427387904)}
521521

522522
For other options, please refer to
523-
:class:`~bson.codec_options.DatetimeConversionOpts`.
523+
:class:`~bson.codec_options.DatetimeConversion`.
524524

525525
Another option that does not involve setting `datetime_conversion` is to to
526526
filter out documents values outside of the range supported by

pymongo/common.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
from bson import SON
3838
from bson.binary import UuidRepresentation
39-
from bson.codec_options import CodecOptions, DatetimeConversionOpts, TypeRegistry
39+
from bson.codec_options import CodecOptions, DatetimeConversion, TypeRegistry
4040
from bson.raw_bson import RawBSONDocument
4141
from pymongo.auth import MECHANISMS
4242
from pymongo.compression_support import (
@@ -620,19 +620,19 @@ def validate_auto_encryption_opts_or_none(option: Any, value: Any) -> Optional[A
620620
return value
621621

622622

623-
def validate_datetime_conversion(option: Any, value: Any) -> Optional[DatetimeConversionOpts]:
624-
"""Validate a DatetimeConversionOpts string."""
623+
def validate_datetime_conversion(option: Any, value: Any) -> Optional[DatetimeConversion]:
624+
"""Validate a DatetimeConversion string."""
625625
if value is None:
626-
return DatetimeConversionOpts.DATETIME
626+
return DatetimeConversion.DATETIME
627627

628628
if isinstance(value, str):
629629
if value.isdigit():
630-
return DatetimeConversionOpts(int(value))
631-
return DatetimeConversionOpts[value]
630+
return DatetimeConversion(int(value))
631+
return DatetimeConversion[value]
632632
elif isinstance(value, int):
633-
return DatetimeConversionOpts(value)
633+
return DatetimeConversion(value)
634634

635-
raise TypeError("%s must be a str or int representing DatetimeConversionOpts" % (option,))
635+
raise TypeError("%s must be a str or int representing DatetimeConversion" % (option,))
636636

637637

638638
# Dictionary where keys are the names of public URI options, and values

test/test_bson.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
)
5151
from bson.binary import Binary, UuidRepresentation
5252
from bson.code import Code
53-
from bson.codec_options import CodecOptions, DatetimeConversionOpts
53+
from bson.codec_options import CodecOptions, DatetimeConversion
5454
from bson.dbref import DBRef
5555
from bson.errors import InvalidBSON, InvalidDocument
5656
from bson.int64 import Int64
@@ -981,7 +981,7 @@ def test_codec_options_repr(self):
981981
"unicode_decode_error_handler='strict', "
982982
"tzinfo=None, type_registry=TypeRegistry(type_codecs=[], "
983983
"fallback_encoder=None), "
984-
"datetime_conversion=DatetimeConversionOpts.DATETIME)"
984+
"datetime_conversion=DatetimeConversion.DATETIME)"
985985
)
986986
self.assertEqual(r, repr(CodecOptions()))
987987

@@ -1189,14 +1189,14 @@ def test_class_conversions(self):
11891189
self.assertNotEqual(type(dtr1), type(dec1["x"]))
11901190

11911191
# Test encode and decode with codec options. Expect: UTCDateimteRaw => DatetimeMS
1192-
opts1 = CodecOptions(datetime_conversion=DatetimeConversionOpts.DATETIME_MS)
1192+
opts1 = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_MS)
11931193
enc1 = encode({"x": dtr1})
11941194
dec1 = decode(enc1, opts1)
11951195
self.assertEqual(type(dtr1), type(dec1["x"]))
11961196
self.assertEqual(dtr1, dec1["x"])
11971197

11981198
# Expect: datetime => DatetimeMS
1199-
opts1 = CodecOptions(datetime_conversion=DatetimeConversionOpts.DATETIME_MS)
1199+
opts1 = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_MS)
12001200
dt1 = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
12011201
enc1 = encode({"x": dt1})
12021202
dec1 = decode(enc1, opts1)
@@ -1206,7 +1206,7 @@ def test_class_conversions(self):
12061206
def test_clamping(self):
12071207
# Test clamping from below and above.
12081208
opts1 = CodecOptions(
1209-
datetime_conversion=DatetimeConversionOpts.DATETIME_CLAMP,
1209+
datetime_conversion=DatetimeConversion.DATETIME_CLAMP,
12101210
tz_aware=True,
12111211
tzinfo=datetime.timezone.utc,
12121212
)
@@ -1225,9 +1225,7 @@ def test_clamping(self):
12251225

12261226
def test_tz_clamping(self):
12271227
# Naive clamping to local tz.
1228-
opts1 = CodecOptions(
1229-
datetime_conversion=DatetimeConversionOpts.DATETIME_CLAMP, tz_aware=False
1230-
)
1228+
opts1 = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_CLAMP, tz_aware=False)
12311229
below = encode({"x": DatetimeMS(_datetime_to_millis(datetime.datetime.min) - 24 * 60 * 60)})
12321230

12331231
dec_below = decode(below, opts1)
@@ -1241,9 +1239,7 @@ def test_tz_clamping(self):
12411239
)
12421240

12431241
# Aware clamping.
1244-
opts2 = CodecOptions(
1245-
datetime_conversion=DatetimeConversionOpts.DATETIME_CLAMP, tz_aware=True
1246-
)
1242+
opts2 = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_CLAMP, tz_aware=True)
12471243
below = encode({"x": DatetimeMS(_datetime_to_millis(datetime.datetime.min) - 24 * 60 * 60)})
12481244
dec_below = decode(below, opts2)
12491245
self.assertEqual(
@@ -1259,7 +1255,7 @@ def test_tz_clamping(self):
12591255

12601256
def test_datetime_auto(self):
12611257
# Naive auto, in range.
1262-
opts1 = CodecOptions(datetime_conversion=DatetimeConversionOpts.DATETIME_AUTO)
1258+
opts1 = CodecOptions(datetime_conversion=DatetimeConversion.DATETIME_AUTO)
12631259
inr = encode({"x": datetime.datetime(1970, 1, 1)}, codec_options=opts1)
12641260
dec_inr = decode(inr)
12651261
self.assertEqual(dec_inr["x"], datetime.datetime(1970, 1, 1))
@@ -1281,7 +1277,7 @@ def test_datetime_auto(self):
12811277

12821278
# Aware auto, in range.
12831279
opts2 = CodecOptions(
1284-
datetime_conversion=DatetimeConversionOpts.DATETIME_AUTO,
1280+
datetime_conversion=DatetimeConversion.DATETIME_AUTO,
12851281
tz_aware=True,
12861282
tzinfo=datetime.timezone.utc,
12871283
)

test/test_client.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
from bson import encode
6868
from bson.codec_options import (
6969
CodecOptions,
70-
DatetimeConversionOpts,
70+
DatetimeConversion,
7171
TypeEncoder,
7272
TypeRegistry,
7373
)
@@ -412,17 +412,15 @@ def test_uri_codec_options(self):
412412
)
413413
self.assertEqual(c.codec_options.unicode_decode_error_handler, unicode_decode_error_handler)
414414
self.assertEqual(
415-
c.codec_options.datetime_conversion, DatetimeConversionOpts[datetime_conversion]
415+
c.codec_options.datetime_conversion, DatetimeConversion[datetime_conversion]
416416
)
417417

418418
# Change the passed datetime_conversion to a number and re-assert.
419-
uri = uri.replace(
420-
datetime_conversion, f"{int(DatetimeConversionOpts[datetime_conversion])}"
421-
)
419+
uri = uri.replace(datetime_conversion, f"{int(DatetimeConversion[datetime_conversion])}")
422420
c = MongoClient(uri, connect=False)
423421

424422
self.assertEqual(
425-
c.codec_options.datetime_conversion, DatetimeConversionOpts[datetime_conversion]
423+
c.codec_options.datetime_conversion, DatetimeConversion[datetime_conversion]
426424
)
427425

428426
def test_uri_option_precedence(self):

0 commit comments

Comments
 (0)