Skip to content

Commit 00aaca1

Browse files
anandoleecopybara-github
authored andcommitted
Breaking change: raise TypeError when convert non-timedelta to Duration, or convert non-datetime to Timestamp in python proto. (Original code may raise ArributeError)
https://protobuf.dev/news/2025-09-19/#python-type-error-timestamp-duration PiperOrigin-RevId: 824803296
1 parent 1270e45 commit 00aaca1

File tree

4 files changed

+18
-4
lines changed

4 files changed

+18
-4
lines changed

python/google/protobuf/internal/duration_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def test_duration_add_annotation(self):
104104

105105
def test_assign_datetime_to_duration(self):
106106
message = well_known_types_test_pb2.WKTMessage()
107-
with self.assertRaises((TypeError, AttributeError)):
107+
with self.assertRaises((TypeError)):
108108
message.optional_duration = datetime.datetime.now()
109109

110110

python/google/protobuf/internal/timestamp_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def test_timestamp_add_annotation(self):
111111

112112
def test_assign_duration_to_timestamp(self):
113113
message = well_known_types_test_pb2.WKTMessage()
114-
with self.assertRaises((TypeError, AttributeError)):
114+
with self.assertRaises((TypeError)):
115115
message.optional_timestamp = datetime.timedelta(microseconds=123)
116116

117117

python/google/protobuf/internal/well_known_types.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,13 @@ def FromDatetime(self, dt):
279279
# manipulated into a long value of seconds. During the conversion from
280280
# struct_time to long, the source date in UTC, and so it follows that the
281281
# correct transformation is calendar.timegm()
282+
if type(dt).__name__ != 'datetime' and not isinstance(
283+
dt, datetime.datetime
284+
):
285+
raise TypeError(
286+
'Fail to convert to Timestamp. Expected a datetime object '
287+
'got {0}'.format(type(dt).__name__)
288+
)
282289
try:
283290
seconds = calendar.timegm(dt.utctimetuple())
284291
nanos = dt.microsecond * _NANOS_PER_MICROSECOND
@@ -445,6 +452,13 @@ def ToTimedelta(self) -> datetime.timedelta:
445452

446453
def FromTimedelta(self, td):
447454
"""Converts timedelta to Duration."""
455+
if type(td).__name__ != 'timedelta' and not isinstance(
456+
td, datetime.timedelta
457+
):
458+
raise TypeError(
459+
'Fail to convert to Duration. Expected a timedelta object '
460+
'got {0}'.format(type(td).__name__)
461+
)
448462
try:
449463
self._NormalizeDuration(
450464
td.seconds + td.days * _SECONDS_PER_DAY,

python/google/protobuf/internal/well_known_types_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,10 +540,10 @@ def testInvalidTimestamp(self):
540540
self.assertRaisesRegex(ValueError, 'Timestamp is not valid',
541541
message.FromSeconds, -62135596801)
542542
msg = well_known_types_test_pb2.WKTMessage()
543-
with self.assertRaises((TypeError, AttributeError)):
543+
with self.assertRaises((TypeError)):
544544
msg.optional_timestamp = 1
545545

546-
with self.assertRaises((TypeError, AttributeError)):
546+
with self.assertRaises((TypeError)):
547547
msg2 = well_known_types_test_pb2.WKTMessage(optional_timestamp=1)
548548

549549
with self.assertRaises(TypeError):

0 commit comments

Comments
 (0)