[pypy-commit] pypy py3.5: Implement __text_signature__ on PyCFunctions
rlamy
pypy.commits at gmail.com
Sun Nov 12 17:16:24 EST 2017
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.5
Changeset: r92998:a626dd21b1fa
Date: 2017-11-12 20:11 +0000
http://bitbucket.org/pypy/pypy/changeset/a626dd21b1fa/
Log: Implement __text_signature__ on PyCFunctions
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -43,6 +43,39 @@
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
+def undotted_name(name):
+ """Return the last component of a dotted name"""
+ dotpos = name.rfind('.')
+ if dotpos < 0:
+ return name
+ else:
+ return name[dotpos + 1:]
+
+SIGNATURE_MARKER = ')\n--\n\n'
+
+def extract_doc(raw_doc, name):
+ doc = raw_doc
+ name = undotted_name(name)
+ if raw_doc.startswith(name + '('):
+ end_sig = raw_doc.find(SIGNATURE_MARKER)
+ if end_sig > 0:
+ doc = raw_doc[end_sig + len(SIGNATURE_MARKER):]
+ if not doc:
+ return None
+ return doc
+
+def extract_txtsig(raw_doc, name):
+ name = undotted_name(name)
+ if raw_doc.startswith(name + '('):
+ end_sig = raw_doc.find(SIGNATURE_MARKER)
+ if end_sig > 0:
+ # Notes:
+ # * Parentheses are included
+ # * SIGNATURE_MARKER cannot appear inside name,
+ # so end_sig > len(name)
+ return raw_doc[len(name): end_sig + 1]
+ return None
+
class W_PyCFunctionObject(W_Root):
# TODO create a slightly different class depending on the c_ml_flags
def __init__(self, space, ml, w_self, w_module=None):
@@ -84,11 +117,22 @@
raise oefmt(space.w_RuntimeError, "unknown calling convention")
def get_doc(self, space):
- doc = self.ml.c_ml_doc
- if doc:
- return space.newtext(rffi.charp2str(rffi.cast(rffi.CCHARP,doc)))
- else:
- return space.w_None
+ c_doc = self.ml.c_ml_doc
+ if c_doc:
+ rawdoc = rffi.charp2str(rffi.cast(rffi.CCHARP, c_doc))
+ doc = extract_doc(rawdoc, self.name)
+ if doc is not None:
+ return space.newtext(doc)
+ return space.w_None
+
+ def get_txtsig(self, space):
+ c_doc = self.ml.c_ml_doc
+ if c_doc:
+ rawdoc = rffi.charp2str(rffi.cast(rffi.CCHARP, c_doc))
+ txtsig = extract_txtsig(rawdoc, self.name)
+ if txtsig is not None:
+ return space.newtext(txtsig)
+ return space.w_None
class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject):
def call(self, space, w_self, w_args, w_kw):
@@ -289,6 +333,7 @@
'builtin_function_or_method',
__call__ = interp2app(cfunction_descr_call),
__doc__ = GetSetProperty(W_PyCFunctionObject.get_doc),
+ __text_signature__ = GetSetProperty(W_PyCFunctionObject.get_txtsig),
__module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObject),
__name__ = interp_attrproperty('name', cls=W_PyCFunctionObject,
wrapfn="newtext_or_none"),
@@ -299,6 +344,7 @@
'builtin_function_or_method', W_PyCFunctionObject.typedef,
__call__ = interp2app(cfunction_descr_call_noargs),
__doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc),
+ __text_signature__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_txtsig),
__module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs),
__name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs,
wrapfn="newtext_or_none"),
@@ -309,6 +355,7 @@
'builtin_function_or_method', W_PyCFunctionObject.typedef,
__call__ = interp2app(cfunction_descr_call_single_object),
__doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc),
+ __text_signature__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_txtsig),
__module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject),
__name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject,
wrapfn="newtext_or_none"),
diff --git a/pypy/module/cpyext/test/docstrings.c b/pypy/module/cpyext/test/docstrings.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/docstrings.c
@@ -0,0 +1,149 @@
+#include "Python.h"
+
+static PyObject *
+test_with_docstring(PyObject *self)
+{
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(empty_doc,
+""
+);
+
+PyDoc_STRVAR(no_sig,
+"This docstring has no signature."
+);
+
+PyDoc_STRVAR(invalid_sig,
+"invalid_sig($module, /, boo)\n"
+"\n"
+"This docstring has an invalid signature."
+);
+
+PyDoc_STRVAR(invalid_sig2,
+"invalid_sig2($module, /, boo)\n"
+"\n"
+"--\n"
+"\n"
+"This docstring also has an invalid signature."
+);
+
+PyDoc_STRVAR(with_sig,
+"with_sig($module, /, sig)\n"
+"--\n"
+"\n"
+"This docstring has a valid signature."
+);
+
+PyDoc_STRVAR(with_sig_but_no_doc,
+"with_sig_but_no_doc($module, /, sig)\n"
+"--\n"
+"\n"
+);
+
+PyDoc_STRVAR(with_signature_and_extra_newlines,
+"with_signature_and_extra_newlines($module, /, parameter)\n"
+"--\n"
+"\n"
+"\n"
+"This docstring has a valid signature and some extra newlines."
+);
+
+
+static PyMethodDef methods[] = {
+ {"no_doc",
+ (PyCFunction)test_with_docstring, METH_NOARGS},
+ {"empty_doc",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ empty_doc},
+ {"no_sig",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ no_sig},
+ {"invalid_sig",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ invalid_sig},
+ {"invalid_sig2",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ invalid_sig2},
+ {"with_sig",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ with_sig},
+ {"with_sig_but_no_doc",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ with_sig_but_no_doc},
+ {"with_signature_and_extra_newlines",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ with_signature_and_extra_newlines},
+ {NULL, NULL} /* sentinel */
+};
+
+
+static PyType_Slot HeapType_slots[] = {
+ {Py_tp_doc, "HeapType()\n--\n\nA type with a signature"},
+ {0, 0},
+};
+
+static PyType_Spec HeapType_spec = {
+ "docstrings.HeapType",
+ sizeof(PyObject),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ HeapType_slots
+};
+
+static PyTypeObject SomeType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "docstrings.SomeType", /* tp_name */
+ sizeof(PyObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "SomeType()\n--\n\nA type with a signature", /* tp_doc */
+};
+
+
+static struct PyModuleDef def = {
+ PyModuleDef_HEAD_INIT,
+ "docstrings",
+ NULL,
+ -1,
+ methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+PyMODINIT_FUNC
+PyInit_docstrings(void)
+{
+ PyObject *m, *tmp;
+ m = PyModule_Create(&def);
+ if (m == NULL)
+ return NULL;
+ tmp = PyType_FromSpec(&HeapType_spec);
+ if (tmp == NULL)
+ return NULL;
+ if (PyModule_AddObject(m, "HeapType", tmp) != 0)
+ return NULL;
+ if (PyType_Ready(&SomeType) < 0)
+ return NULL;
+ if (PyModule_AddObject(m, "SomeType", (PyObject*)&SomeType) != 0)
+ return NULL;
+ return m;
+}
diff --git a/pypy/module/cpyext/test/test_methodobject.py b/pypy/module/cpyext/test/test_methodobject.py
--- a/pypy/module/cpyext/test/test_methodobject.py
+++ b/pypy/module/cpyext/test/test_methodobject.py
@@ -100,3 +100,23 @@
assert mod.check(A) == 0
assert mod.check(A.meth) == 0
assert mod.check(A.stat) == 0
+
+ def test_text_signature(self):
+ mod = self.import_module('docstrings')
+ assert mod.no_doc.__doc__ is None
+ assert mod.no_doc.__text_signature__ is None
+ assert mod.empty_doc.__doc__ is None
+ assert mod.empty_doc.__text_signature__ is None
+ assert mod.no_sig.__doc__
+ assert mod.no_sig.__text_signature__ is None
+ assert mod.invalid_sig.__doc__
+ assert mod.invalid_sig.__text_signature__ is None
+ assert mod.invalid_sig2.__doc__
+ assert mod.invalid_sig2.__text_signature__ is None
+ assert mod.with_sig.__doc__
+ assert mod.with_sig.__text_signature__ == '($module, /, sig)'
+ assert mod.with_sig_but_no_doc.__doc__ is None
+ assert mod.with_sig_but_no_doc.__text_signature__ == '($module, /, sig)'
+ assert mod.with_signature_and_extra_newlines.__doc__
+ assert (mod.with_signature_and_extra_newlines.__text_signature__ ==
+ '($module, /, parameter)')
More information about the pypy-commit
mailing list