[py/C++] Implement Make GetOptions() return immutable options. C++ will raise a TypeError when options returned GetOptions() by is mutated.

PiperOrigin-RevId: 926215827
This commit is contained in:
Runze Wang 2026-06-03 13:01:32 -07:00 committed by Copybara-Service
parent 075955729a
commit eebe9b7737
14 changed files with 410 additions and 135 deletions

View file

@ -280,18 +280,19 @@ class DescriptorTest(unittest.TestCase):
self.my_service.GetOptions(), descriptor_pb2.ServiceOptions()
)
@unittest.skipIf(api_implementation.Type() == 'cpp', 'Not fixed yet in C++')
@unittest.skipIf(
api_implementation.Type() == 'upb',
'Needs to wait for a breaking change release in OSS'
)
@unittest.skipIf(
api_implementation.Type() == 'cpp',
'Needs to wait for a breaking change release in OSS'
)
@unittest.skipIf(
api_implementation.Type() == 'python',
'Needs to wait for a breaking change release in OSS'
)
def testModifyFrozenMessage(self):
# At least upb raises TypeError Other 2 implementations will likely be
# fixed to be consistent with upb.
immutability_error = TypeError
message_options = self.my_message.GetOptions()
other_options = descriptor_pb2.MessageOptions()
@ -348,6 +349,23 @@ class DescriptorTest(unittest.TestCase):
non_empty_repeated.clear()
with self.assertRaises(immutability_error):
non_empty_repeated.sort()
with self.assertRaises(immutability_error):
non_empty_repeated.remove(99)
with self.assertRaises(immutability_error):
non_empty_repeated.pop()
with self.assertRaises(immutability_error):
non_empty_repeated.reverse()
# Non-empty repeated composite field item access
complex_opt2 = unittest_custom_options_pb2.complex_opt2
non_empty_repeated_composite = complex_options_msg.Extensions[
complex_opt2
].barney
self.assertEqual(len(non_empty_repeated_composite), 2)
first_barney = non_empty_repeated_composite[0]
self.assertEqual(first_barney.waldo, 101)
with self.assertRaises(immutability_error):
first_barney.waldo = 999
# Extension dict mutation
with self.assertRaises(immutability_error):

View file

@ -26,6 +26,7 @@
#include "google/protobuf/dynamic_message.h"
#include "google/protobuf/internal_feature_helper.h"
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/breaking_changes.h"
#include "google/protobuf/pyext/descriptor_containers.h"
#include "google/protobuf/pyext/descriptor_pool.h"
#include "google/protobuf/pyext/free_threading_mutex.h"
@ -69,6 +70,9 @@ static PyObject* PyFrame_GetGlobals(PyFrameObject* frame) {
}
#endif
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace python {
@ -292,18 +296,27 @@ static PyObject* GetOrBuildMessageInDefaultPool(
}
CMessage* cmsg = reinterpret_cast<CMessage*>(value.get());
Message* cmsg_message = cmessage::AssureWritable(cmsg);
if (cmsg_message == nullptr) {
return nullptr;
}
const Reflection* reflection = message.GetReflection();
const UnknownFieldSet& unknown_fields(reflection->GetUnknownFields(message));
if (unknown_fields.empty()) {
cmsg->message->CopyFrom(message);
cmsg_message->CopyFrom(message);
} else {
// Reparse options string! XXX call cmessage::MergeFromString
if (!Reparse(message_factory, message, cmsg->message)) {
if (!Reparse(message_factory, message, cmsg_message)) {
PyErr_Format(PyExc_ValueError, "Error reparsing Options message");
return nullptr;
}
}
#if PROTOBUF_PY_FUTURE_FREEZE_OPTIONS
cmsg->state = MESSAGE_FROZEN;
#endif
// Cache the result.
{
FreeThreadingLockGuard lock(*cache_mutex);
@ -360,9 +373,12 @@ static PyObject* CopyToPythonProto(const DescriptorClass* descriptor,
std::string(self_descriptor->full_name()).c_str());
return nullptr;
}
cmessage::AssureWritable(message);
Message* mutable_message = cmessage::AssureWritable(message);
if (mutable_message == nullptr) {
return nullptr;
}
DescriptorProtoClass* descriptor_message =
static_cast<DescriptorProtoClass*>(message->message);
static_cast<DescriptorProtoClass*>(mutable_message);
descriptor->CopyTo(descriptor_message);
// Custom options might in unknown extensions. Reparse
// the descriptor_message. Can't skip reparse when options unknown
@ -2130,3 +2146,5 @@ bool InitDescriptor() {
} // namespace python
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"

View file

@ -57,7 +57,7 @@ static bool GetFileDescriptorProto(PyObject* py_descriptor,
if (PyObject_TypeCheck(py_descriptor, CMessage_Type) &&
message->message->GetDescriptor() == filedescriptor_descriptor) {
// Fast path: Just use the pointer.
FileDescriptorProto* file_proto =
const FileDescriptorProto* file_proto =
google::protobuf::DownCastMessage<FileDescriptorProto>(message->message);
*output = *file_proto;
return true;

View file

@ -591,7 +591,7 @@ static PyObject* SetFeatureSetDefaults(PyObject* pself, PyObject* pdefaults) {
absl::Status status =
const_cast<DescriptorPool*>(self->pool)
->SetFeatureSetDefaults(
*reinterpret_cast<FeatureSetDefaults*>(defaults->message));
*reinterpret_cast<const FeatureSetDefaults*>(defaults->message));
if (!status.ok()) {
PyErr_SetString(PyExc_ValueError, std::string(status.message()).c_str());
return nullptr;

View file

@ -206,7 +206,7 @@ int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) {
"type");
return -1;
}
cmessage::AssureWritable(self->parent);
if (cmessage::AssureWritable(self->parent) == nullptr) return -1;
if (cmessage::InternalSetScalar(self->parent, descriptor, value) < 0) {
return -1;
}

View file

@ -78,9 +78,10 @@ struct MapIterator {
uint64_t version;
};
const Message* MapContainer::GetReadOnlyMessage() { return parent->message; }
Message* MapContainer::GetMutableMessage() {
cmessage::AssureWritable(parent);
return parent->message;
return cmessage::AssureWritable(parent);
}
// Consumes a reference on the Python string object.
@ -171,7 +172,8 @@ static PyObject* MapKeyToPython(MapContainer* self, const MapKey& key) {
// This is only used for ScalarMap, so we don't need to handle the
// CPPTYPE_MESSAGE case.
PyObject* MapValueRefToPython(MapContainer* self, const MapValueRef& value) {
PyObject* MapValueRefToPython(MapContainer* self,
const MapValueConstRef& value) {
const FieldDescriptor* field_descriptor =
self->parent_field_descriptor->message_type()->map_value();
switch (field_descriptor->cpp_type()) {
@ -294,6 +296,7 @@ Py_ssize_t MapReflectionFriend::Length(PyObject* _self) {
PyObject* Clear(PyObject* _self) {
MapContainer* self = GetMap(_self);
Message* message = self->GetMutableMessage();
if (message == nullptr) return nullptr;
const Reflection* reflection = message->GetReflection();
reflection->ClearField(message, self->parent_field_descriptor);
@ -319,6 +322,7 @@ PyObject* MapReflectionFriend::MergeFrom(PyObject* _self, PyObject* arg) {
}
MapContainer* other_map = GetMap(arg);
Message* message = self->GetMutableMessage();
if (message == nullptr) return nullptr;
const Message* other_message = other_map->parent->message;
const Reflection* reflection = message->GetReflection();
const Reflection* other_reflection = other_message->GetReflection();
@ -380,6 +384,7 @@ PyObject* MapReflectionFriend::ScalarMapGetItem(PyObject* _self,
MapContainer* self = GetMap(_self);
Message* message = self->GetMutableMessage();
if (message == nullptr) return nullptr;
const Reflection* reflection = message->GetReflection();
std::string map_key_string;
MapKey map_key;
@ -402,6 +407,7 @@ int MapReflectionFriend::ScalarMapSetItem(PyObject* _self, PyObject* key,
MapContainer* self = GetMap(_self);
Message* message = self->GetMutableMessage();
if (message == nullptr) return -1;
const Reflection* reflection = message->GetReflection();
std::string map_key_string;
MapKey map_key;
@ -505,11 +511,12 @@ PyObject* MapReflectionFriend::ScalarMapToStr(PyObject* _self) {
ScopedPyObjectPtr value;
MapContainer* self = GetMap(_self);
Message* message = self->GetMutableMessage();
const Message* message = self->GetReadOnlyMessage();
const Reflection* reflection = message->GetReflection();
for (google::protobuf::MapIterator it =
reflection->MapBegin(message, self->parent_field_descriptor);
it != reflection->MapEnd(message, self->parent_field_descriptor); ++it) {
for (google::protobuf::ConstMapIterator it =
reflection->ConstMapBegin(message, self->parent_field_descriptor);
it != reflection->ConstMapEnd(message, self->parent_field_descriptor);
++it) {
key.reset(MapKeyToPython(self, it.GetKey()));
if (key == nullptr) {
return nullptr;
@ -579,7 +586,8 @@ static MessageMapContainer* GetMessageMap(PyObject* obj) {
return reinterpret_cast<MessageMapContainer*>(obj);
}
static PyObject* GetCMessage(MessageMapContainer* self, Message* message) {
static PyObject* GetCMessage(MessageMapContainer* self,
const Message* message) {
// Get or create the CMessage object corresponding to this message.
return self->parent
->BuildSubMessageFromPointer(self->parent_field_descriptor, message,
@ -625,6 +633,7 @@ int MapReflectionFriend::MessageMapSetItem(PyObject* _self, PyObject* key,
MessageMapContainer* self = GetMessageMap(_self);
Message* message = self->GetMutableMessage();
if (message == nullptr) return -1;
const Reflection* reflection = message->GetReflection();
std::string map_key_string;
MapKey map_key;
@ -647,9 +656,11 @@ int MapReflectionFriend::MessageMapSetItem(PyObject* _self, PyObject* key,
// otherwise we just discard the C++ value.
if (CMessage* released =
self->parent->MaybeReleaseSubMessage(sub_message)) {
Message* msg = released->message;
released->message = msg->New();
msg->GetReflection()->Swap(msg, released->message);
Message* msg = cmessage::AssureWritable(released);
if (msg == nullptr) return 0;
Message* new_msg = msg->New();
released->message = new_msg;
msg->GetReflection()->Swap(msg, new_msg);
}
// Delete key from map.
@ -666,6 +677,7 @@ PyObject* MapReflectionFriend::MessageMapGetItem(PyObject* _self,
MessageMapContainer* self = GetMessageMap(_self);
Message* message = self->GetMutableMessage();
if (message == nullptr) return nullptr;
const Reflection* reflection = message->GetReflection();
std::string map_key_string;
MapKey map_key;
@ -692,16 +704,17 @@ PyObject* MapReflectionFriend::MessageMapToStr(PyObject* _self) {
ScopedPyObjectPtr value;
MessageMapContainer* self = GetMessageMap(_self);
Message* message = self->GetMutableMessage();
const Message* message = self->GetReadOnlyMessage();
const Reflection* reflection = message->GetReflection();
for (google::protobuf::MapIterator it =
reflection->MapBegin(message, self->parent_field_descriptor);
it != reflection->MapEnd(message, self->parent_field_descriptor); ++it) {
for (google::protobuf::ConstMapIterator it =
reflection->ConstMapBegin(message, self->parent_field_descriptor);
it != reflection->ConstMapEnd(message, self->parent_field_descriptor);
++it) {
key.reset(MapKeyToPython(self, it.GetKey()));
if (key == nullptr) {
return nullptr;
}
value.reset(GetCMessage(self, it.MutableValueRef()->MutableMessageValue()));
value.reset(GetCMessage(self, &it.GetValueRef().GetMessageValue()));
if (value == nullptr) {
return nullptr;
}
@ -818,11 +831,11 @@ PyObject* MapReflectionFriend::GetIterator(PyObject* _self) {
iter->parent = self->parent;
if (MapReflectionFriend::Length(_self) > 0) {
Message* message = self->GetMutableMessage();
const Message* message = self->GetReadOnlyMessage();
const Reflection* reflection = message->GetReflection();
iter->iter.reset(new ::google::protobuf::MapIterator(
reflection->MapBegin(message, self->parent_field_descriptor)));
iter->iter.reset(new ::google::protobuf::MapIterator(reflection->MapBegin(
const_cast<Message*>(message), self->parent_field_descriptor)));
}
return obj.release();
@ -844,11 +857,12 @@ PyObject* MapReflectionFriend::IterNext(PyObject* _self) {
return nullptr;
}
Message* message = self->container->GetMutableMessage();
const Message* message = self->container->GetReadOnlyMessage();
const Reflection* reflection = message->GetReflection();
if (*self->iter ==
reflection->MapEnd(message, self->container->parent_field_descriptor)) {
reflection->MapEnd(const_cast<Message*>(message),
self->container->parent_field_descriptor)) {
return nullptr;
}

View file

@ -29,6 +29,9 @@ struct CMessageClass;
// This struct is used directly for ScalarMap, and is the base class of
// MessageMapContainer, which is used for MessageMap.
struct MapContainer : public ContainerBase {
// Use to get a message for read-only operations.
const Message* GetReadOnlyMessage();
// Use to get a mutable message when necessary.
Message* GetMutableMessage();

View file

@ -736,7 +736,8 @@ PyMessageFactory* GetFactoryForMessage(CMessage* message) {
static int MaybeReleaseOverlappingOneofField(CMessage* cmessage,
const FieldDescriptor* field) {
Message* message = cmessage->message;
Message* message = AssureWritable(cmessage);
if (message == nullptr) return -1;
const Reflection* reflection = message->GetReflection();
if (!field->containing_oneof() ||
!reflection->HasOneof(*message, field->containing_oneof()) ||
@ -765,7 +766,8 @@ int MaybeReleaseOneofBeforeMerge(CMessage* self, const Message& other) {
return 0;
}
Message* message = self->message;
Message* message = AssureWritable(self);
if (message == nullptr) return -1;
const Reflection* reflection = message->GetReflection();
PyMessageFactory* factory = GetFactoryForMessage(self);
std::vector<const FieldDescriptor*> fields_to_release;
@ -823,18 +825,19 @@ void FixupMessageAfterMerge(CMessage* self) {
if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
!descriptor->is_repeated()) {
CMessage* cmsg = reinterpret_cast<CMessage*>(value);
if (cmsg->read_only == false) {
if (cmsg->state != MESSAGE_MUTABLE_DEFAULT) {
return;
}
Message* message = self->message;
Message* message = AssureWritable(self);
if (message == nullptr) return;
const Reflection* reflection = message->GetReflection();
if (reflection->HasField(*message, descriptor)) {
// Message used to be read_only, but is no longer. Get the new pointer
// and record it.
// Message used to be a default instance, but is no longer. Get the new
// pointer and record it.
Message* mutable_message = reflection->MutableMessage(
message, descriptor, factory->message_factory);
cmsg->message = mutable_message;
cmsg->read_only = false;
cmsg->state = MESSAGE_MUTABLE;
FixupMessageAfterMerge(cmsg);
}
}
@ -844,37 +847,47 @@ void FixupMessageAfterMerge(CMessage* self) {
// ---------------------------------------------------------------------
// Making a message writable
int AssureWritable(CMessage* self) {
if (self == nullptr || !self->read_only) {
return 0;
Message* AssureWritable(CMessage* self) {
if (self == nullptr) {
return nullptr;
}
// Toplevel messages are always mutable.
switch (self->state) {
case MESSAGE_MUTABLE:
return const_cast<Message*>(self->message);
case MESSAGE_FROZEN:
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
case MESSAGE_MUTABLE_DEFAULT:
break;
}
// Toplevel messages are never default instances.
ABSL_DCHECK(self->parent);
if (AssureWritable(self->parent) == -1) {
return -1;
Message* parent_message = AssureWritable(self->parent);
if (parent_message == nullptr) {
return nullptr;
}
// If this message is part of a oneof, there might be a field to release in
// the parent.
if (MaybeReleaseOverlappingOneofField(self->parent,
self->parent_field_descriptor) < 0) {
return -1;
return nullptr;
}
// Make self->message writable.
Message* parent_message = self->parent->message;
const Reflection* reflection = parent_message->GetReflection();
Message* mutable_message = reflection->MutableMessage(
parent_message, self->parent_field_descriptor,
GetFactoryForMessage(self->parent)->message_factory);
if (mutable_message == nullptr) {
return -1;
return nullptr;
}
self->message = mutable_message;
self->read_only = false;
self->state = MESSAGE_MUTABLE;
return 0;
return mutable_message;
}
// --- Globals:
@ -922,7 +935,8 @@ static PyObject* GetIntegerEnumValue(const FieldDescriptor& descriptor,
void DeleteLastRepeatedWithSize(CMessage* self,
const FieldDescriptor* field_descriptor,
Py_ssize_t n) {
Message* message = self->message;
Message* message = AssureWritable(self);
if (message == nullptr) return;
const Reflection* reflection = message->GetReflection();
ABSL_DCHECK(reflection->FieldSize(*message, field_descriptor) >= n);
Arena* arena = message->GetArena();
@ -962,7 +976,8 @@ void DeleteLastRepeatedWithSize(CMessage* self,
int DeleteRepeatedField(CMessage* self, const FieldDescriptor* field_descriptor,
PyObject* slice) {
Py_ssize_t length, from, to, step, slice_length;
Message* message = self->message;
Message* message = AssureWritable(self);
if (message == nullptr) return -1;
const Reflection* reflection = message->GetReflection();
int min, max;
length = reflection->FieldSize(*message, field_descriptor);
@ -1029,10 +1044,49 @@ int DeleteRepeatedField(CMessage* self, const FieldDescriptor* field_descriptor,
return 0;
}
int CheckRepeatedFieldDeletion(CMessage* parent,
const FieldDescriptor* field_descriptor,
PyObject* slice) {
if (parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return -1;
}
const Message* message = parent->message;
const Reflection* reflection = message->GetReflection();
Py_ssize_t length = reflection->FieldSize(*message, field_descriptor);
if (PySlice_Check(slice)) {
Py_ssize_t from, to, step, slicelength;
if (PySlice_GetIndicesEx(slice, length, &from, &to, &step, &slicelength) ==
-1) {
return -1;
}
if (slicelength == 0) {
return 1;
}
} else {
Py_ssize_t index = PyLong_AsLong(slice);
if (index == -1 && PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "list indices must be integers");
return -1;
}
if (index < 0) {
index = length + index;
}
if (index < 0 || index >= length) {
PyErr_Format(PyExc_IndexError, "list assignment index out of range");
return -1;
}
}
return 0;
}
int InitWKTOrMerge(const Descriptor* descriptor, PyObject* py_message,
PyObject* value) {
CMessage* cmessage = reinterpret_cast<CMessage*>(py_message);
if (AssureWritable(cmessage) < 0) return -1;
if (AssureWritable(cmessage) == nullptr) return -1;
if (PyObject_TypeCheck(value, CMessage_Type)) {
ScopedPyObjectPtr merged(MergeFrom(cmessage, value));
if (merged == nullptr) {
@ -1241,7 +1295,7 @@ int InitAttributes(CMessage* self, PyObject* args, PyObject* kwargs) {
(descriptor->message_type()->well_known_type() !=
Descriptor::WELLKNOWNTYPE_STRUCT)) {
// Make the message exist even if the dict is empty.
if (AssureWritable(cmessage) < 0) return -1;
if (AssureWritable(cmessage) == nullptr) return -1;
if (InitAttributes(cmessage, nullptr, value) < 0) {
return -1;
}
@ -1280,7 +1334,7 @@ CMessage* NewEmptyMessage(CMessageClass* type) {
self->message = nullptr;
self->parent = nullptr;
self->parent_field_descriptor = nullptr;
self->read_only = false;
self->state = MESSAGE_MUTABLE;
// Construct the lazy unique pointers using placement new.
new (&self->composite_fields) LazyUniquePtr<CMessage::CompositeFieldsMap>();
@ -1406,7 +1460,7 @@ PyObject* IsInitialized(CMessage* self, PyObject* args) {
int HasFieldByDescriptor(CMessage* self,
const FieldDescriptor* field_descriptor) {
Message* message = self->message;
const Message* message = self->message;
if (!CheckFieldBelongsToMessage(field_descriptor, message)) {
return -1;
}
@ -1462,7 +1516,7 @@ PyObject* HasField(CMessage* self, PyObject* arg) {
char* field_name;
Py_ssize_t size;
field_name = const_cast<char*>(PyUnicode_AsUTF8AndSize(arg, &size));
Message* message = self->message;
const Message* message = self->message;
if (!field_name) {
PyErr_Format(PyExc_ValueError,
@ -1597,14 +1651,19 @@ static int InternalReparentFields(
to_release->AsPyObject());
}
if (self->message->GetArena() == new_message->message->GetArena()) {
Message* mutable_self = AssureWritable(self);
if (mutable_self == nullptr) return -1;
Message* mutable_new = AssureWritable(new_message);
if (mutable_new == nullptr) return -1;
if (mutable_self->GetArena() == mutable_new->GetArena()) {
MessageReflectionFriend::UnsafeShallowSwapFields(
self->message, new_message->message,
mutable_self, mutable_new,
std::vector<const FieldDescriptor*>(fields_to_swap.begin(),
fields_to_swap.end()));
} else {
self->message->GetReflection()->SwapFields(
self->message, new_message->message,
mutable_self->GetReflection()->SwapFields(
mutable_self, mutable_new,
std::vector<const FieldDescriptor*>(fields_to_swap.begin(),
fields_to_swap.end()));
}
@ -1655,8 +1714,8 @@ int ClearFieldByDescriptor(CMessage* self,
if (InternalReleaseFieldByDescriptor(self, field_descriptor) < 0) {
return -1;
}
if (AssureWritable(self) < 0) return -1;
Message* message = self->message;
Message* message = AssureWritable(self);
if (message == nullptr) return -1;
message->GetReflection()->ClearField(message, field_descriptor);
return 0;
}
@ -1667,7 +1726,7 @@ PyObject* ClearField(CMessage* self, PyObject* arg) {
if (PyString_AsStringAndSize(arg, &field_name, &field_size) < 0) {
return nullptr;
}
if (AssureWritable(self) < 0) return nullptr;
if (AssureWritable(self) == nullptr) return nullptr;
bool is_in_oneof;
const FieldDescriptor* field_descriptor = FindFieldWithOneofs(
self->message, absl::string_view(field_name, field_size), &is_in_oneof);
@ -1689,7 +1748,8 @@ PyObject* ClearField(CMessage* self, PyObject* arg) {
}
PyObject* Clear(CMessage* self) {
if (AssureWritable(self) < 0) return nullptr;
Message* message = AssureWritable(self);
if (message == nullptr) return nullptr;
// Detach all current fields of this message
std::vector<ScopedPyObjectPtr> messages_to_release;
std::vector<ScopedPyObjectPtr> containers_to_release;
@ -1710,7 +1770,7 @@ PyObject* Clear(CMessage* self) {
0) {
return nullptr;
}
self->message->Clear();
message->Clear();
Py_RETURN_NONE;
}
@ -1912,13 +1972,14 @@ PyObject* MergeFrom(CMessage* self, PyObject* arg) {
.c_str());
return nullptr;
}
if (AssureWritable(self) < 0) return nullptr;
Message* message = AssureWritable(self);
if (message == nullptr) return nullptr;
if (MaybeReleaseOneofBeforeMerge(self, *other_message->message) < 0) {
return nullptr;
}
self->message->MergeFrom(*other_message->message);
message->MergeFrom(*other_message->message);
// Child message might be lazily created before MergeFrom. Make sure they
// are mutable at this point if child messages are really created.
FixupMessageAfterMerge(self);
@ -1956,13 +2017,14 @@ static PyObject* CopyFrom(CMessage* self, PyObject* arg) {
return nullptr;
}
if (AssureWritable(self) < 0) return nullptr;
Message* message = AssureWritable(self);
if (message == nullptr) return nullptr;
// CopyFrom on the message will not clean up self->composite_fields,
// which can leave us in an inconsistent state, so clear it out here.
(void)ScopedPyObjectPtr(Clear(self));
self->message->CopyFrom(*other_message->message);
message->CopyFrom(*other_message->message);
Py_RETURN_NONE;
}
@ -1989,7 +2051,8 @@ static PyObject* MergeFromString(CMessage* self, PyObject* arg) {
return nullptr;
}
if (AssureWritable(self) < 0) {
Message* message = AssureWritable(self);
if (message == nullptr) {
PyBuffer_Release(&data);
return nullptr;
}
@ -2006,7 +2069,7 @@ static PyObject* MergeFromString(CMessage* self, PyObject* arg) {
ctx.data().pool = factory->pool->pool;
ctx.data().factory = factory->message_factory;
ptr = self->message->_InternalParse(ptr, &ctx);
ptr = message->_InternalParse(ptr, &ctx);
// Child message might be lazily created before MergeFrom. Make sure they
// are mutable at this point if child messages are really created.
@ -2060,7 +2123,7 @@ static PyObject* ByteSize(CMessage* self, PyObject* args) {
}
static PyObject* SetInParent(CMessage* self, PyObject* args) {
if (AssureWritable(self) < 0) return nullptr;
if (AssureWritable(self) == nullptr) return nullptr;
Py_RETURN_NONE;
}
@ -2168,13 +2231,14 @@ static PyObject* ListFields(CMessage* self) {
}
static PyObject* DiscardUnknownFields(CMessage* self) {
if (AssureWritable(self) < 0) return nullptr;
self->message->DiscardUnknownFields();
Message* message = AssureWritable(self);
if (message == nullptr) return nullptr;
message->DiscardUnknownFields();
Py_RETURN_NONE;
}
PyObject* FindInitializationErrors(CMessage* self) {
Message* message = self->message;
const Message* message = self->message;
std::vector<std::string> errors;
message->FindInitializationErrors(&errors);
@ -2326,22 +2390,33 @@ CMessage* InternalGetSubMessage(CMessage* self,
Py_INCREF(self);
cmsg->parent = self;
cmsg->parent_field_descriptor = field_descriptor;
if (self->state == MESSAGE_FROZEN) {
cmsg->state = MESSAGE_FROZEN;
const Message& sub_message = reflection->GetMessage(
*self->message, field_descriptor, factory->message_factory);
cmsg->message = &sub_message;
return cmsg;
}
if (reflection->HasField(*self->message, field_descriptor)) {
// Force triggering MutableMessage to set the lazy message 'Dirty'
if (MessageReflectionFriend::IsLazyField(reflection, *self->message,
field_descriptor)) {
Message* sub_message = reflection->MutableMessage(
self->message, field_descriptor, factory->message_factory);
cmsg->read_only = false;
Message* mutable_self = cmessage::AssureWritable(self);
if (mutable_self == nullptr) {
return nullptr;
}
Message* sub_message = mutable_self->GetReflection()->MutableMessage(
mutable_self, field_descriptor, factory->message_factory);
cmsg->state = MESSAGE_MUTABLE;
cmsg->message = sub_message;
return cmsg;
}
} else {
cmsg->read_only = true;
cmsg->state = MESSAGE_MUTABLE_DEFAULT;
}
const Message& sub_message = reflection->GetMessage(
*self->message, field_descriptor, factory->message_factory);
cmsg->message = const_cast<Message*>(&sub_message);
cmsg->message = &sub_message;
return cmsg;
}
@ -2426,7 +2501,10 @@ int InternalSetNonOneofScalar(Message* message,
int InternalSetScalar(CMessage* self, const FieldDescriptor* field_descriptor,
PyObject* arg) {
if (!CheckFieldBelongsToMessage(field_descriptor, self->message)) {
Message* message = cmessage::AssureWritable(self);
if (message == nullptr) return -1;
if (!CheckFieldBelongsToMessage(field_descriptor, message)) {
return -1;
}
@ -2434,7 +2512,7 @@ int InternalSetScalar(CMessage* self, const FieldDescriptor* field_descriptor,
return -1;
}
return InternalSetNonOneofScalar(self->message, field_descriptor, arg);
return InternalSetNonOneofScalar(message, field_descriptor, arg);
}
PyObject* FromString(PyTypeObject* cls, PyObject* serialized) {
@ -2500,7 +2578,7 @@ PyObject* ToUnicode(CMessage* self) {
}
PyObject* Contains(CMessage* self, PyObject* arg) {
Message* message = self->message;
const Message* message = self->message;
const Descriptor* descriptor = message->GetDescriptor();
switch (descriptor->well_known_type()) {
case Descriptor::WELLKNOWNTYPE_STRUCT: {
@ -2757,7 +2835,7 @@ int SetFieldValue(CMessage* self, const FieldDescriptor* field_descriptor,
Descriptor::WELLKNOWNTYPE_UNSPECIFIED) {
ScopedPyObjectPtr sub_message(GetFieldValue(self, field_descriptor));
if (PyObject_HasAttrString(sub_message.get(), "_internal_assign")) {
if (AssureWritable(self) < 0) return -1;
if (AssureWritable(self) == nullptr) return -1;
ScopedPyObjectPtr ok(PyObject_CallMethod(
sub_message.get(), "_internal_assign", "O", value));
if (ok.get() == nullptr) {
@ -2772,7 +2850,7 @@ int SetFieldValue(CMessage* self, const FieldDescriptor* field_descriptor,
std::string(field_descriptor->name()).c_str());
return -1;
} else {
if (AssureWritable(self) < 0) return -1;
if (AssureWritable(self) == nullptr) return -1;
return InternalSetScalar(self, field_descriptor, value);
}
}
@ -2797,7 +2875,9 @@ PyObject* ContainerBase::DeepCopy() {
// call the right read/write field functions.
std::unique_ptr<Message> tmp(this->parent->message->New(nullptr));
tmp->MergeFrom(*this->parent->message);
tmp->GetReflection()->SwapFields(tmp.get(), new_parent->message,
Message* mutable_new = cmessage::AssureWritable(new_parent);
if (mutable_new == nullptr) return nullptr;
tmp->GetReflection()->SwapFields(tmp.get(), mutable_new,
{this->parent_field_descriptor});
PyObject* result =
@ -2818,7 +2898,7 @@ void ContainerBase::RemoveFromParentCache() {
}
CMessage* CMessage::BuildSubMessageFromPointer(
const FieldDescriptor* field_descriptor, Message* sub_message,
const FieldDescriptor* field_descriptor, const Message* sub_message,
CMessageClass* message_class) {
if (PyObject* value =
this->child_submessages.Get()->Get(sub_message, nullptr)) {
@ -2834,11 +2914,14 @@ CMessage* CMessage::BuildSubMessageFromPointer(
Py_INCREF(this);
cmsg->parent = this;
cmsg->parent_field_descriptor = field_descriptor;
if (this->state == MESSAGE_FROZEN) {
cmsg->state = MESSAGE_FROZEN;
}
cmessage::SetSubmessage(this, cmsg);
return cmsg;
}
CMessage* CMessage::MaybeReleaseSubMessage(Message* sub_message) {
CMessage* CMessage::MaybeReleaseSubMessage(const Message* sub_message) {
CMessage::SubMessagesMap* sub_messages = this->child_submessages.TryGet();
if (sub_messages == nullptr) {
return nullptr;
@ -2850,7 +2933,7 @@ CMessage* CMessage::MaybeReleaseSubMessage(Message* sub_message) {
// The target message will now own its content.
Py_CLEAR(released->parent);
released->parent_field_descriptor = nullptr;
released->read_only = false;
released->state = MESSAGE_MUTABLE;
// Delete it from the cache.
sub_messages->Erase(sub_message);
// child_submessages->Get returned a new reference.
@ -2958,8 +3041,9 @@ Message* PyMessage_GetMutableMessagePointer(PyObject* msg) {
"to a message with extra references");
return nullptr;
}
if (cmessage::AssureWritable(cmsg) < 0) return nullptr;
return cmsg->message;
Message* message = cmessage::AssureWritable(cmsg);
if (message == nullptr) return nullptr;
return message;
}
// Returns a new reference to the MessageClass to use for message creation.

View file

@ -45,6 +45,23 @@ struct CMessageClass;
// ExtensionDicts and UnknownFields containers do NOT follow this rule. They
// don't store any data, and always refer to their parent message.
// Defines the mutability and allocation state of a CMessage.
// A default instance can be either mutable (MESSAGE_MUTABLE_DEFAULT) or frozen
// (MESSAGE_FROZEN).
enum MessageMutabilityState {
// Backed by a fully allocated, mutable C++ Message object.
MESSAGE_MUTABLE = 0,
// Backed by a const default instance that is mutable on write (not frozen).
// Acts as a "stub".
// Will automatically transition to MESSAGE_MUTABLE upon first mutation.
MESSAGE_MUTABLE_DEFAULT = 1,
// Permanently read-only (e.g., Descriptor Options).
// Any attempt to mutate will raise a Python TypeError.
MESSAGE_FROZEN = 2,
};
struct ContainerBase {
// clang-format off
PyObject_HEAD
@ -80,12 +97,10 @@ typedef struct CMessage : public ContainerBase {
// Pointer to the C++ Message object for this CMessage.
// - If this object has no parent, we own this pointer.
// - If this object has a parent message, the parent owns this pointer.
Message* message;
const Message* message;
// Indicates this submessage is pointing to a default instance of a message.
// Submessages are always first created as read only messages and are then
// made writable, at which point this field is set to false.
bool read_only;
// Indicates the mutability state of this CMessage wrapper.
MessageMutabilityState state;
// A mapping indexed by field, containing weak references to contained objects
// which need to implement the "Release" mechanism:
@ -113,9 +128,9 @@ typedef struct CMessage : public ContainerBase {
// For container containing messages, return a Python object for the given
// pointer to a message.
CMessage* BuildSubMessageFromPointer(const FieldDescriptor* field_descriptor,
Message* sub_message,
const Message* sub_message,
CMessageClass* message_class);
CMessage* MaybeReleaseSubMessage(Message* sub_message);
CMessage* MaybeReleaseSubMessage(const Message* sub_message);
} CMessage;
// The (meta) type of all Messages classes.
@ -176,6 +191,15 @@ void DeleteLastRepeatedWithSize(CMessage* self,
int DeleteRepeatedField(CMessage* self, const FieldDescriptor* field_descriptor,
PyObject* slice);
// Check if a deletion operation on a repeated field is a no-op, valid or error.
// Returns:
// 1 if the deletion is a no-op (empty slice deletion).
// 0 if the deletion is valid and requires mutating the container.
// -1 if an error occurred.
int CheckRepeatedFieldDeletion(CMessage* parent,
const FieldDescriptor* field_descriptor,
PyObject* slice);
// Sets the specified scalar value to the message.
int InternalSetScalar(CMessage* self, const FieldDescriptor* field_descriptor,
PyObject* value);
@ -238,7 +262,7 @@ int SetFieldValue(CMessage* self, const FieldDescriptor* field_descriptor,
PyObject* FindInitializationErrors(CMessage* self);
int AssureWritable(CMessage* self);
Message* AssureWritable(CMessage* self);
// Returns the message factory for the given message.
// This is equivalent to message.MESSAGE_FACTORY

View file

@ -35,7 +35,7 @@ static Py_ssize_t Length(PyObject* pself) {
RepeatedCompositeContainer* self =
reinterpret_cast<RepeatedCompositeContainer*>(pself);
Message* message = self->parent->message;
const Message* message = self->parent->message;
return message->GetReflection()->FieldSize(*message,
self->parent_field_descriptor);
}
@ -45,8 +45,8 @@ static Py_ssize_t Length(PyObject* pself) {
PyObject* Add(RepeatedCompositeContainer* self, PyObject* args,
PyObject* kwargs) {
if (cmessage::AssureWritable(self->parent) == -1) return nullptr;
Message* message = self->parent->message;
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return nullptr;
Message* sub_message = message->GetReflection()->AddMessage(
message, self->parent_field_descriptor,
@ -72,9 +72,9 @@ static PyObject* AddMethod(PyObject* self, PyObject* args, PyObject* kwargs) {
// append()
static PyObject* AddMessage(RepeatedCompositeContainer* self, PyObject* value) {
cmessage::AssureWritable(self->parent);
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return nullptr;
PyObject* py_cmsg;
Message* message = self->parent->message;
const Reflection* reflection = message->GetReflection();
py_cmsg = Add(self, nullptr, nullptr);
if (py_cmsg == nullptr) return nullptr;
@ -116,7 +116,8 @@ static PyObject* Insert(PyObject* pself, PyObject* args) {
}
// Swap the element to right position.
Message* message = self->parent->message;
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return nullptr;
const Reflection* reflection = message->GetReflection();
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
Py_ssize_t length = reflection->FieldSize(*message, field_descriptor) - 1;
@ -134,7 +135,7 @@ static PyObject* Insert(PyObject* pself, PyObject* args) {
// extend()
PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value) {
cmessage::AssureWritable(self->parent);
if (cmessage::AssureWritable(self->parent) == nullptr) return nullptr;
ScopedPyObjectPtr iter(PyObject_GetIter(value));
if (iter == nullptr) {
PyErr_SetString(PyExc_TypeError, "Value must be iterable");
@ -178,7 +179,7 @@ static PyObject* MergeFromMethod(PyObject* self, PyObject* other) {
static PyObject* GetItem(RepeatedCompositeContainer* self, Py_ssize_t index,
Py_ssize_t length = -1) {
if (length == -1) {
Message* message = self->parent->message;
const Message* message = self->parent->message;
const Reflection* reflection = message->GetReflection();
length = reflection->FieldSize(*message, self->parent_field_descriptor);
}
@ -186,9 +187,21 @@ static PyObject* GetItem(RepeatedCompositeContainer* self, Py_ssize_t index,
PyErr_Format(PyExc_IndexError, "list index (%zd) out of range", index);
return nullptr;
}
Message* message = self->parent->message;
Message* sub_message = message->GetReflection()->MutableRepeatedMessage(
message, self->parent_field_descriptor, index);
const Message* message = self->parent->message;
const Reflection* reflection = message->GetReflection();
const Message* sub_message = nullptr;
const int int_index = static_cast<int>(index);
if (self->parent->state == python::MESSAGE_FROZEN) {
sub_message = &reflection->GetRepeatedMessage(
*message, self->parent_field_descriptor, int_index);
} else {
Message* mutable_parent = cmessage::AssureWritable(self->parent);
if (mutable_parent == nullptr) {
return nullptr;
}
sub_message = mutable_parent->GetReflection()->MutableRepeatedMessage(
mutable_parent, self->parent_field_descriptor, int_index);
}
return self->parent
->BuildSubMessageFromPointer(self->parent_field_descriptor, sub_message,
self->child_message_class)
@ -196,7 +209,7 @@ static PyObject* GetItem(RepeatedCompositeContainer* self, Py_ssize_t index,
}
PyObject* Subscript(RepeatedCompositeContainer* self, PyObject* item) {
Message* message = self->parent->message;
const Message* message = self->parent->message;
const Reflection* reflection = message->GetReflection();
Py_ssize_t length =
reflection->FieldSize(*message, self->parent_field_descriptor);
@ -246,6 +259,14 @@ int AssignSubscript(RepeatedCompositeContainer* self, PyObject* slice,
return -1;
}
// TODO: b/517235198 - Reify even for empty sequences.
int status = cmessage::CheckRepeatedFieldDeletion(
self->parent, self->parent_field_descriptor, slice);
if (status < 0) return -1;
if (status > 0) return 0;
if (cmessage::AssureWritable(self->parent) == nullptr) return -1;
return cmessage::DeleteRepeatedField(self->parent,
self->parent_field_descriptor, slice);
}
@ -259,6 +280,12 @@ static int AssignSubscriptMethod(PyObject* self, PyObject* slice,
static PyObject* Remove(PyObject* pself, PyObject* value) {
RepeatedCompositeContainer* self =
reinterpret_cast<RepeatedCompositeContainer*>(pself);
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
Py_ssize_t len = Length(reinterpret_cast<PyObject*>(self));
for (Py_ssize_t i = 0; i < len; i++) {
@ -333,18 +360,23 @@ static PyObject* ToStr(PyObject* pself) {
static void ReorderAttached(RepeatedCompositeContainer* self,
PyObject* child_list) {
Message* message = self->parent->message;
const Py_ssize_t length = Length(reinterpret_cast<PyObject*>(self));
if (length == 0) return;
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return;
const Reflection* reflection = message->GetReflection();
const FieldDescriptor* descriptor = self->parent_field_descriptor;
const Py_ssize_t length = Length(reinterpret_cast<PyObject*>(self));
// We need to rearrange things to match python's sort order.
for (Py_ssize_t i = 0; i < length; ++i) {
reflection->UnsafeArenaReleaseLast(message, descriptor);
}
for (Py_ssize_t i = 0; i < length; ++i) {
Message* child_message =
reinterpret_cast<CMessage*>(PyList_GET_ITEM(child_list, i))->message;
CMessage* child_cmsg =
reinterpret_cast<CMessage*>(PyList_GET_ITEM(child_list, i));
Message* child_message = cmessage::AssureWritable(child_cmsg);
if (child_message == nullptr) return;
reflection->UnsafeArenaAddAllocatedMessage(message, descriptor,
child_message);
}
@ -371,6 +403,11 @@ static PyObject* Sort(PyObject* pself, PyObject* args, PyObject* kwds) {
RepeatedCompositeContainer* self =
reinterpret_cast<RepeatedCompositeContainer*>(pself);
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
// Support the old sort_function argument for backwards
// compatibility.
if (kwds != nullptr) {
@ -383,6 +420,11 @@ static PyObject* Sort(PyObject* pself, PyObject* args, PyObject* kwds) {
}
}
// TODO: b/517235198 - Reify even for empty sequences.
if (Length(pself) == 0) {
Py_RETURN_NONE;
}
if (SortPythonMessages(self, args, kwds) < 0) {
return nullptr;
}
@ -411,6 +453,16 @@ static PyObject* Reverse(PyObject* pself) {
RepeatedCompositeContainer* self =
reinterpret_cast<RepeatedCompositeContainer*>(pself);
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
// TODO: b/517235198 - Reify even for empty sequences.
if (Length(pself) == 0) {
Py_RETURN_NONE;
}
if (ReversePythonMessages(self) < 0) {
return nullptr;
}
@ -421,8 +473,14 @@ static PyObject* Reverse(PyObject* pself) {
static PyObject* Clear(PyObject* pself) {
RepeatedCompositeContainer* self =
reinterpret_cast<RepeatedCompositeContainer*>(pself);
// TODO: b/517235198 - Reify even for empty sequences.
if (Length(pself) == 0) {
Py_RETURN_NONE;
}
CMessage* cmessage = self->parent;
Message* message = cmessage->message;
Message* message = cmessage::AssureWritable(cmessage);
if (message == nullptr) return nullptr;
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
const Reflection* reflection = message->GetReflection();
Py_ssize_t length = reflection->FieldSize(*message, field_descriptor);
@ -440,6 +498,11 @@ static PyObject* Pop(PyObject* pself, PyObject* args) {
RepeatedCompositeContainer* self =
reinterpret_cast<RepeatedCompositeContainer*>(pself);
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
Py_ssize_t index = -1;
if (!PyArg_ParseTuple(args, "|n", &index)) {
return nullptr;

View file

@ -34,8 +34,8 @@ namespace repeated_scalar_container {
static int InternalAssignRepeatedField(RepeatedScalarContainer* self,
PyObject* list) {
cmessage::AssureWritable(self->parent);
Message* message = self->parent->message;
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return -1;
message->GetReflection()->ClearField(message, self->parent_field_descriptor);
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(list); ++i) {
PyObject* value = PyList_GET_ITEM(list, i);
@ -49,7 +49,7 @@ static int InternalAssignRepeatedField(RepeatedScalarContainer* self,
static Py_ssize_t Len(PyObject* pself) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
Message* message = self->parent->message;
const Message* message = self->parent->message;
return message->GetReflection()->FieldSize(*message,
self->parent_field_descriptor);
}
@ -58,8 +58,8 @@ static int AssignItem(PyObject* pself, Py_ssize_t index, PyObject* arg) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
cmessage::AssureWritable(self->parent);
Message* message = self->parent->message;
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return -1;
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
const Reflection* reflection = message->GetReflection();
@ -163,7 +163,7 @@ static PyObject* Item(PyObject* pself, Py_ssize_t index) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
Message* message = self->parent->message;
const Message* message = self->parent->message;
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
const Reflection* reflection = message->GetReflection();
@ -304,8 +304,8 @@ static PyObject* Subscript(PyObject* pself, PyObject* slice) {
}
PyObject* Append(RepeatedScalarContainer* self, PyObject* item) {
cmessage::AssureWritable(self->parent);
Message* message = self->parent->message;
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return nullptr;
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
const Reflection* reflection = message->GetReflection();
@ -398,10 +398,14 @@ static int AssSubscript(PyObject* pself, PyObject* slice, PyObject* value) {
Py_ssize_t slicelength;
bool create_list = false;
cmessage::AssureWritable(self->parent);
Message* message = self->parent->message;
Message* message = cmessage::AssureWritable(self->parent);
if (message == nullptr) return -1;
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
if (value == nullptr) {
return cmessage::DeleteRepeatedField(self->parent, field_descriptor, slice);
}
if (PyLong_Check(slice)) {
from = to = PyLong_AsLong(slice);
} else if (PySlice_Check(slice)) {
@ -417,10 +421,6 @@ static int AssSubscript(PyObject* pself, PyObject* slice, PyObject* value) {
return -1;
}
if (value == nullptr) {
return cmessage::DeleteRepeatedField(self->parent, field_descriptor, slice);
}
if (!create_list) {
return AssignItem(pself, from, value);
}
@ -441,7 +441,7 @@ static int AssSubscript(PyObject* pself, PyObject* slice, PyObject* value) {
}
PyObject* Extend(RepeatedScalarContainer* self, PyObject* value) {
cmessage::AssureWritable(self->parent);
if (cmessage::AssureWritable(self->parent) == nullptr) return nullptr;
ScopedPyObjectPtr iter(PyObject_GetIter(value));
if (iter == nullptr) {
@ -482,6 +482,16 @@ static PyObject* Insert(PyObject* pself, PyObject* args) {
}
static PyObject* Remove(PyObject* pself, PyObject* value) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
// Even if the value doesn't exist in the container, raise immutability error
// prior to value error if applicable.
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
Py_ssize_t match_index = -1;
for (Py_ssize_t i = 0; i < Len(pself); ++i) {
ScopedPyObjectPtr elem(Item(pself, i));
@ -613,7 +623,7 @@ std::string GetDefaultDTypeStr(FieldDescriptor::CppType cpp_type) {
PyObject* CreateArrayFromView(PyObject* pself, PyObject* np_module) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
Message* message = self->parent->message;
const Message* message = self->parent->message;
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
const Reflection* reflection = message->GetReflection();
std::string out_dtype = GetDefaultDTypeStr(field_descriptor->cpp_type());
@ -724,6 +734,14 @@ PyObject* Reduce(PyObject* unused_self, PyObject* unused_other) {
}
static PyObject* Sort(PyObject* pself, PyObject* args, PyObject* kwds) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
// Support the old sort_function argument for backwards
// compatibility.
if (kwds != nullptr) {
@ -764,6 +782,19 @@ static PyObject* Sort(PyObject* pself, PyObject* args, PyObject* kwds) {
}
static PyObject* Reverse(PyObject* pself) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
// TODO: b/517235198 - Reify even for empty sequences.
if (Len(pself) == 0) {
Py_RETURN_NONE;
}
ScopedPyObjectPtr full_slice(PySlice_New(nullptr, nullptr, nullptr));
if (full_slice == nullptr) {
return nullptr;
@ -787,15 +818,31 @@ static PyObject* Reverse(PyObject* pself) {
static PyObject* Clear(PyObject* pself) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
// TODO: b/517235198 - Reify even for empty sequences.
if (Len(pself) == 0) {
Py_RETURN_NONE;
}
CMessage* cmessage = self->parent;
cmessage::AssureWritable(cmessage);
Message* message = cmessage->message;
Message* message = cmessage::AssureWritable(cmessage);
if (message == nullptr) return nullptr;
const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
message->GetReflection()->ClearField(message, field_descriptor);
Py_RETURN_NONE;
}
static PyObject* Pop(PyObject* pself, PyObject* args) {
RepeatedScalarContainer* self =
reinterpret_cast<RepeatedScalarContainer*>(pself);
// Even if the value doesn't exist in the container, raise immutability error
// prior to value error.
if (self->parent->state == python::MESSAGE_FROZEN) {
PyErr_SetString(PyExc_TypeError, "Message is immutable.");
return nullptr;
}
Py_ssize_t index = -1;
if (!PyArg_ParseTuple(args, "|n", &index)) {
return nullptr;

View file

@ -85,7 +85,7 @@ PyObject* New(PyTypeObject* type, PyObject* args, PyObject* kwargs) {
self->parent = nullptr;
// Copy c_message's UnknownFieldSet.
Message* message = reinterpret_cast<CMessage*>(c_message)->message;
const Message* message = reinterpret_cast<CMessage*>(c_message)->message;
const Reflection* reflection = message->GetReflection();
self->fields = new google::protobuf::UnknownFieldSet;
self->fields->MergeFrom(reflection->GetUnknownFields(*message));

View file

@ -234,6 +234,7 @@ static int PyUpb_MapContainer_Contains(PyObject* _self, PyObject* key) {
static PyObject* PyUpb_MapContainer_Clear(PyObject* _self, PyObject* key) {
upb_Map* map = PyUpb_MapContainer_AssureWritable(_self);
if (!map) return NULL;
// TODO: b/517235198 - Reify even for empty sequences.
if (upb_Map_Size(map) > 0) {
upb_Map_Clear(map);
}

View file

@ -531,6 +531,8 @@ static PyObject* PyUpb_RepeatedContainer_Sort(PyObject* pself, PyObject* args,
PyErr_SetString(PyExc_TypeError, "Container is read-only");
return NULL;
}
// TODO:b/517235198 - Reify even for empty sequences.
if (PyUpb_RepeatedContainer_Length(pself) == 0) Py_RETURN_NONE;
upb_Array* arr = PyUpb_RepeatedContainer_AssureWritable(pself);
@ -575,6 +577,7 @@ static PyObject* PyUpb_RepeatedContainer_Reverse(PyObject* _self) {
static PyObject* PyUpb_RepeatedContainer_Clear(PyObject* _self) {
Py_ssize_t size = PyUpb_RepeatedContainer_Length(_self);
// TODO: b/517235198 - Reify even for empty sequences.
if (size == 0) Py_RETURN_NONE;
PyUpb_RepeatedContainer* self = (PyUpb_RepeatedContainer*)_self;