In CPython, this works:
import ctypes
ctypes.pythonapi.PyString_AsString.argtypes = (ctypes.c_void_p,)
ctypes.pythonapi.PyString_AsString.restype = ctypes.POINTER(ctypes.c_char)
s = "abc"
cs = ctypes.pythonapi.PyString_AsString(id(s))
cs[0] = "x"
print s # will print 'xbc'
In PyPy, it does not because I cannot access the C-API this way.
Is there any way to do the same in PyPy?
You're not supposed to do that. The main reason is that PyPy has a movable garbage collector, so the pointer to it's contents might suddenly start pointing to garbage. The other reason is that we have few options that have a structure of say StringAdd(a, b), where there is really no underlying char* to refer to. Those optimizations are not on by default, but they'll probably be enabled soon.