Repository: cython/cython

Issue:
[BUG] removal of old-style buffer special methods breaks pyarrow on py313
### Describe the bug

When trying to cythonize pyarrow with cython and cpython default branches it fails with:

```

      Error compiling Cython file:
      ------------------------------------------------------------
      ...
              buffer.obj = self
              buffer.shape = self.shape
              buffer.strides = self.strides
              buffer.suboffsets = NULL

          def __getsegcount__(self, Py_ssize_t *len_out):
                                    ^
      ------------------------------------------------------------

      pyarrow/io.pxi:1449:30: Cannot convert Python object argument to type 'Py_ssize_t *'

      Error compiling Cython file:
      ------------------------------------------------------------
      ...
          def __getsegcount__(self, Py_ssize_t *len_out):
              if len_out != NULL:
                  len_out[0] = <Py_ssize_t>self.size
              return 1

          def __getreadbuffer__(self, Py_ssize_t idx, void **p):
                                                      ^
      ------------------------------------------------------------

      pyarrow/io.pxi:1454:48: Cannot convert Python object argument to type 'void **'

      Error compiling Cython file:
      ------------------------------------------------------------
      ...
                  raise SystemError("accessing nonexistent buffer segment")
              if p != NULL:
                  p[0] = <void*> self.buffer.get().data()
              return self.size

          def __getwritebuffer__(self, Py_ssize_t idx, void **p):
                                                       ^
      ------------------------------------------------------------

      pyarrow/io.pxi:1461:49: Cannot convert Python object argument to type 'void **'
```

I was able to bisect it to 9a22b49cb8573c5085994d8c9c5da2de6b2b577d being the first bad commit which looks like it removes these methods from  `self.PyBufferProcs`.

Oddly, it does not fail with py312 and I do not have a good theory of why.

### Code to reproduce the behaviour:

https://github.com/apache/arrow/blob/22f88fa4a8f5ac7250f1845aace5a78d20006ef2/python/pyarrow/io.pxi#L1449-L1468

```cython
# example code
    def __getsegcount__(self, Py_ssize_t *len_out):
        if len_out != NULL:
            len_out[0] = <Py_ssize_t>self.size
        return 1


    def __getreadbuffer__(self, Py_ssize_t idx, void **p):
        if idx != 0:
            raise SystemError("accessing nonexistent buffer segment")
        if p != NULL:
            p[0] = <void*> self.buffer.get().data()
        return self.size


    def __getwritebuffer__(self, Py_ssize_t idx, void **p):
        if not self.buffer.get().is_mutable():
            raise SystemError("trying to write an immutable buffer")
        if idx != 0:
            raise SystemError("accessing nonexistent buffer segment")
        if p != NULL:
            p[0] = <void*> self.buffer.get().data()
        return self.size
```


### Expected behaviour

Compilation should not fail.

If these are going to be removed, then I think it needs to be better advertised and removed from https://cython.readthedocs.io/en/latest/src/userguide/special_methods.html#buffer-interface-legacy-no-python-equivalents-see-note-1

### OS

linux

### Python version

main branch

### Cython version

development branches

### Additional context

From searching code I have checked out on my machine I think this may also hit mpi4py.


Discussion:
I can definitely update the docs not to mention them.

I can try to make it a warning so that code doesn't immediately break - I doubt that's too hard.

They are useless though unless you're supporting Python 2 (which I don't think pyarrow is?). So whatever Cython does, it might be a good opportunity to remove them.

Resolving patch:
diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py
--- a/Cython/Compiler/Code.py
+++ b/Cython/Compiler/Code.py
@@ -237,8 +237,7 @@
 special_py_methods = cython.declare(frozenset, frozenset((
     '__cinit__', '__dealloc__', '__richcmp__', '__next__',
     '__await__', '__aiter__', '__anext__',
-    '__getreadbuffer__', '__getwritebuffer__', '__getsegcount__',
-    '__getcharbuffer__', '__getbuffer__', '__releasebuffer__',
+    '__getbuffer__', '__releasebuffer__',
 )))
 
 modifier_output_mapper = {
diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py
--- a/Cython/Compiler/ParseTreeTransforms.py
+++ b/Cython/Compiler/ParseTreeTransforms.py
@@ -407,6 +407,14 @@ def visit_JoinedStrNode(self, node):
         self.visitchildren(node)
         return node
 
+    def visit_DefNode(self, node):
+        if (self.scope_type == "cclass" and
+                node.name in ["__getreadbuffer__", "__getwritebuffer__", "__getsegcount__", "__getcharbuffer__"]):
+            warning(node.pos, f"'{node.name}' relates to the old Python 2 buffer protocol "
+                    "and is no longer used.", 2)
+            return None  # drop the node - the arguments are invalid for a def node
+        return self.visit_FuncDefNode(node)
+
 
 class _AssignmentExpressionTargetNameFinder(TreeVisitor):
     def __init__(self):


Test patch: