Nextgen Proto Pythonic API: Timestamp/Duration assignment, creation and calculation

Timestamp and Duration are now have more support with datetime and timedelta:
- Allows assign python datetime to protobuf DateTime field in addition to current FromDatetime/ToDatetime (Note: will throw exceptions for the differences in supported ranges)
- Allows assign python timedelta to protobuf Duration field in addition to current FromTimedelta/ToTimedelta
- Calculation between Timestamp, Duration, datetime and timedelta will also be supported.

example usage:

from datetime import datetime, timedelta
from event_pb2 import Event
e = Event(start_time=datetime(year=2112, month=2, day=3),
          duration=timedelta(hours=10))
duration = timedelta(hours=10))
end_time = e.start_time + timedelta(hours=4)
e.duration = end_time - e.start_time
PiperOrigin-RevId: 640639168
diff --git a/python/message.c b/python/message.c
index 42bf902..f31aa5e 100644
--- a/python/message.c
+++ b/python/message.c
@@ -433,6 +433,7 @@
 }
 
 static bool PyUpb_Message_InitMessageAttribute(PyObject* _self, PyObject* name,
+                                               const upb_FieldDef* field,
                                                PyObject* value) {
   PyObject* submsg = PyUpb_Message_GetAttr(_self, name);
   if (!submsg) return -1;
@@ -446,10 +447,24 @@
     assert(!PyErr_Occurred());
     ok = PyUpb_Message_InitAttributes(submsg, NULL, value) >= 0;
   } else {
-    const upb_MessageDef* m = PyUpb_Message_GetMsgdef(_self);
-    PyErr_Format(PyExc_TypeError, "Message must be initialized with a dict: %s",
-                 upb_MessageDef_FullName(m));
-    ok = false;
+    const upb_MessageDef* msgdef = upb_FieldDef_MessageSubDef(field);
+    switch (upb_MessageDef_WellKnownType(msgdef)) {
+      case kUpb_WellKnown_Timestamp: {
+        ok = PyObject_CallMethod(submsg, "FromDatetime", "O", value);
+        break;
+      }
+      case kUpb_WellKnown_Duration: {
+        ok = PyObject_CallMethod(submsg, "FromTimedelta", "O", value);
+        break;
+      }
+      default: {
+        const upb_MessageDef* m = PyUpb_Message_GetMsgdef(_self);
+        PyErr_Format(PyExc_TypeError,
+                     "Message must be initialized with a dict: %s",
+                     upb_MessageDef_FullName(m));
+        ok = false;
+      }
+    }
   }
   Py_DECREF(submsg);
   return ok;
@@ -502,7 +517,7 @@
     } else if (upb_FieldDef_IsRepeated(f)) {
       if (!PyUpb_Message_InitRepeatedAttribute(_self, name, value)) return -1;
     } else if (upb_FieldDef_IsSubMessage(f)) {
-      if (!PyUpb_Message_InitMessageAttribute(_self, name, value)) return -1;
+      if (!PyUpb_Message_InitMessageAttribute(_self, name, f, value)) return -1;
     } else {
       if (!PyUpb_Message_InitScalarAttribute(msg, f, value, arena)) return -1;
     }
@@ -935,9 +950,9 @@
   PyUpb_Message* self = (void*)_self;
   assert(value);
 
-  if (upb_FieldDef_IsSubMessage(field) || upb_FieldDef_IsRepeated(field)) {
+  if (upb_FieldDef_IsRepeated(field)) {
     PyErr_Format(exc,
-                 "Assignment not allowed to message, map, or repeated "
+                 "Assignment not allowed to map, or repeated "
                  "field \"%s\" in protocol message object.",
                  upb_FieldDef_Name(field));
     return -1;
@@ -945,6 +960,34 @@
 
   PyUpb_Message_EnsureReified(self);
 
+  if (upb_FieldDef_IsSubMessage(field)) {
+    const upb_MessageDef* msgdef = upb_FieldDef_MessageSubDef(field);
+    switch (upb_MessageDef_WellKnownType(msgdef)) {
+      case kUpb_WellKnown_Timestamp: {
+        PyObject* sub_message = PyUpb_Message_GetFieldValue(_self, field);
+        PyObject* ok =
+            PyObject_CallMethod(sub_message, "FromDatetime", "O", value);
+        if (!ok) return -1;
+        Py_DECREF(ok);
+        return 0;
+      }
+      case kUpb_WellKnown_Duration: {
+        PyObject* sub_message = PyUpb_Message_GetFieldValue(_self, field);
+        PyObject* ok =
+            PyObject_CallMethod(sub_message, "FromTimedelta", "O", value);
+        if (!ok) return -1;
+        Py_DECREF(ok);
+        return 0;
+      }
+      default:
+        PyErr_Format(exc,
+                     "Assignment not allowed to message "
+                     "field \"%s\" in protocol message object.",
+                     upb_FieldDef_Name(field));
+        return -1;
+    }
+  }
+
   upb_MessageValue val;
   upb_Arena* arena = PyUpb_Arena_Get(self->arena);
   if (!PyUpb_PyToUpb(value, field, &val, arena)) {