Mark sequence containers as Py_TPFLAGS_SEQUENCE, enabling pattern matching

PEP634 introduces structural pattern matching. This works out of the box for most parts of protobuf messages, but fails for sequence matching (defined in https://peps.python.org/pep-0634/#sequence-patterns). This is caused by the underlying containers missing the newly introduced Py_TPFLAGS_SEQUENCE flag (see 069e81ab3d).

This simply adds the flag, making the following fall into the first case:
```
  message = test_pb2.TestMessage(int_sequence=(1, 2, 3))
  match message:
    case test_pb2.TestMessage(int_sequence=(1, *rest)):
      print(f"message.int_sequence is a seq starting with 1, ending in {rest}")
    case _:
      print(f"No case on 'int_sequence' matched! Value: {message.int_sequence}")
```

PiperOrigin-RevId: 524326722
pull/12463/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent a1ba8d238a
commit a05c57d43c
  1. 34
      python/google/protobuf/pyext/repeated_composite_container.cc
  2. 44
      python/google/protobuf/pyext/repeated_scalar_container.cc

@ -549,22 +549,26 @@ PyTypeObject RepeatedCompositeContainer_Type = {
#if PY_VERSION_HEX >= 0x03080000
0, // tp_vectorcall_offset
#else
nullptr, // tp_print
nullptr, // tp_print
#endif
nullptr, // tp_getattr
nullptr, // tp_setattr
nullptr, // tp_compare
repeated_composite_container::ToStr, // tp_repr
nullptr, // tp_as_number
&repeated_composite_container::SqMethods, // tp_as_sequence
&repeated_composite_container::MpMethods, // tp_as_mapping
PyObject_HashNotImplemented, // tp_hash
nullptr, // tp_call
nullptr, // tp_str
nullptr, // tp_getattro
nullptr, // tp_setattro
nullptr, // tp_as_buffer
#if PY_VERSION_HEX >= 0x030A0000
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_SEQUENCE, // tp_flags
#else
Py_TPFLAGS_DEFAULT, // tp_flags
#endif
nullptr, // tp_getattr
nullptr, // tp_setattr
nullptr, // tp_compare
repeated_composite_container::ToStr, // tp_repr
nullptr, // tp_as_number
&repeated_composite_container::SqMethods, // tp_as_sequence
&repeated_composite_container::MpMethods, // tp_as_mapping
PyObject_HashNotImplemented, // tp_hash
nullptr, // tp_call
nullptr, // tp_str
nullptr, // tp_getattro
nullptr, // tp_setattro
nullptr, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
"A Repeated scalar container", // tp_doc
nullptr, // tp_traverse
nullptr, // tp_clear

@ -728,29 +728,33 @@ static PyMethodDef Methods[] = {
PyTypeObject RepeatedScalarContainer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
".RepeatedScalarContainer", // tp_name
sizeof(RepeatedScalarContainer), // tp_basicsize
0, // tp_itemsize
repeated_scalar_container::Dealloc, // tp_dealloc
".RepeatedScalarContainer", // tp_name
sizeof(RepeatedScalarContainer), // tp_basicsize
0, // tp_itemsize
repeated_scalar_container::Dealloc, // tp_dealloc
#if PY_VERSION_HEX >= 0x03080000
0, // tp_vectorcall_offset
0, // tp_vectorcall_offset
#else
nullptr, // tp_print
nullptr, // tp_print
#endif
nullptr, // tp_getattr
nullptr, // tp_setattr
nullptr, // tp_compare
repeated_scalar_container::ToStr, // tp_repr
nullptr, // tp_as_number
&repeated_scalar_container::SqMethods, // tp_as_sequence
&repeated_scalar_container::MpMethods, // tp_as_mapping
PyObject_HashNotImplemented, // tp_hash
nullptr, // tp_call
nullptr, // tp_str
nullptr, // tp_getattro
nullptr, // tp_setattro
nullptr, // tp_as_buffer
#if PY_VERSION_HEX >= 0x030A0000
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_SEQUENCE, // tp_flags
#else
Py_TPFLAGS_DEFAULT, // tp_flags
#endif
nullptr, // tp_getattr
nullptr, // tp_setattr
nullptr, // tp_compare
repeated_scalar_container::ToStr, // tp_repr
nullptr, // tp_as_number
&repeated_scalar_container::SqMethods, // tp_as_sequence
&repeated_scalar_container::MpMethods, // tp_as_mapping
PyObject_HashNotImplemented, // tp_hash
nullptr, // tp_call
nullptr, // tp_str
nullptr, // tp_getattro
nullptr, // tp_setattro
nullptr, // tp_as_buffer
Py_TPFLAGS_DEFAULT, // tp_flags
"A Repeated scalar container", // tp_doc
nullptr, // tp_traverse
nullptr, // tp_clear

Loading…
Cancel
Save