Spaces:
Build error
Build error
from __future__ import absolute_import, print_function | |
from .Visitor import CythonTransform | |
from .StringEncoding import EncodedString | |
from . import Options | |
from . import PyrexTypes | |
from ..CodeWriter import ExpressionWriter | |
from .Errors import warning | |
class AnnotationWriter(ExpressionWriter): | |
""" | |
A Cython code writer for Python expressions in argument/variable annotations. | |
""" | |
def __init__(self, description=None): | |
"""description is optional. If specified it is used in | |
warning messages for the nodes that don't convert to string properly. | |
If not specified then no messages are generated. | |
""" | |
ExpressionWriter.__init__(self) | |
self.description = description | |
self.incomplete = False | |
def visit_Node(self, node): | |
self.put(u"<???>") | |
self.incomplete = True | |
if self.description: | |
warning(node.pos, | |
"Failed to convert code to string representation in {0}".format( | |
self.description), level=1) | |
def visit_LambdaNode(self, node): | |
# XXX Should we do better? | |
self.put("<lambda>") | |
self.incomplete = True | |
if self.description: | |
warning(node.pos, | |
"Failed to convert lambda to string representation in {0}".format( | |
self.description), level=1) | |
def visit_UnicodeNode(self, node): | |
# Discard Unicode prefix in annotations. Any tool looking at them | |
# would probably expect Py3 string semantics. | |
self.emit_string(node, "") | |
def visit_AnnotationNode(self, node): | |
self.put(node.string.unicode_value) | |
class EmbedSignature(CythonTransform): | |
def __init__(self, context): | |
super(EmbedSignature, self).__init__(context) | |
self.class_name = None | |
self.class_node = None | |
def _fmt_expr(self, node): | |
writer = ExpressionWriter() | |
result = writer.write(node) | |
# print(type(node).__name__, '-->', result) | |
return result | |
def _fmt_annotation(self, node): | |
writer = AnnotationWriter() | |
result = writer.write(node) | |
# print(type(node).__name__, '-->', result) | |
return result | |
def _setup_format(self): | |
signature_format = self.current_directives['embedsignature.format'] | |
self.is_format_c = signature_format == 'c' | |
self.is_format_python = signature_format == 'python' | |
self.is_format_clinic = signature_format == 'clinic' | |
def _fmt_arg(self, arg): | |
arg_doc = arg.name | |
annotation = None | |
defaultval = None | |
if arg.is_self_arg: | |
if self.is_format_clinic: | |
arg_doc = '$self' | |
elif arg.is_type_arg: | |
if self.is_format_clinic: | |
arg_doc = '$type' | |
elif self.is_format_c: | |
if arg.type is not PyrexTypes.py_object_type: | |
arg_doc = arg.type.declaration_code(arg.name, for_display=1) | |
elif self.is_format_python: | |
if not arg.annotation: | |
annotation = self._fmt_type(arg.type) | |
if arg.annotation: | |
if not self.is_format_clinic: | |
annotation = self._fmt_annotation(arg.annotation) | |
if arg.default: | |
defaultval = self._fmt_expr(arg.default) | |
if annotation: | |
arg_doc = arg_doc + (': %s' % annotation) | |
if defaultval: | |
arg_doc = arg_doc + (' = %s' % defaultval) | |
elif defaultval: | |
arg_doc = arg_doc + ('=%s' % defaultval) | |
return arg_doc | |
def _fmt_star_arg(self, arg): | |
arg_doc = arg.name | |
if arg.annotation: | |
if not self.is_format_clinic: | |
annotation = self._fmt_annotation(arg.annotation) | |
arg_doc = arg_doc + (': %s' % annotation) | |
return arg_doc | |
def _fmt_arglist(self, args, | |
npoargs=0, npargs=0, pargs=None, | |
nkargs=0, kargs=None, | |
hide_self=False): | |
arglist = [] | |
for arg in args: | |
if not hide_self or not arg.entry.is_self_arg: | |
arg_doc = self._fmt_arg(arg) | |
arglist.append(arg_doc) | |
if pargs: | |
arg_doc = self._fmt_star_arg(pargs) | |
arglist.insert(npargs + npoargs, '*%s' % arg_doc) | |
elif nkargs: | |
arglist.insert(npargs + npoargs, '*') | |
if npoargs: | |
arglist.insert(npoargs, '/') | |
if kargs: | |
arg_doc = self._fmt_star_arg(kargs) | |
arglist.append('**%s' % arg_doc) | |
return arglist | |
def _fmt_type(self, type): | |
if type is PyrexTypes.py_object_type: | |
return None | |
elif self.is_format_c: | |
code = type.declaration_code("", for_display=1) | |
return code | |
elif self.is_format_python: | |
annotation = None | |
if type.is_string: | |
annotation = self.current_directives['c_string_type'] | |
elif type.is_numeric: | |
annotation = type.py_type_name() | |
if annotation is None: | |
code = type.declaration_code('', for_display=1) | |
annotation = code.replace(' ', '_').replace('*', 'p') | |
return annotation | |
return None | |
def _fmt_signature(self, cls_name, func_name, args, | |
npoargs=0, npargs=0, pargs=None, | |
nkargs=0, kargs=None, | |
return_expr=None, return_type=None, | |
hide_self=False): | |
arglist = self._fmt_arglist( | |
args, npoargs, npargs, pargs, nkargs, kargs, | |
hide_self=hide_self, | |
) | |
arglist_doc = ', '.join(arglist) | |
func_doc = '%s(%s)' % (func_name, arglist_doc) | |
if self.is_format_c and cls_name: | |
func_doc = '%s.%s' % (cls_name, func_doc) | |
if not self.is_format_clinic: | |
ret_doc = None | |
if return_expr: | |
ret_doc = self._fmt_annotation(return_expr) | |
elif return_type: | |
ret_doc = self._fmt_type(return_type) | |
if ret_doc: | |
func_doc = '%s -> %s' % (func_doc, ret_doc) | |
return func_doc | |
def _embed_signature(self, signature, node_doc): | |
if self.is_format_clinic and self.current_directives['binding']: | |
return node_doc | |
if node_doc: | |
if self.is_format_clinic: | |
docfmt = "%s\n--\n\n%s" | |
else: | |
docfmt = "%s\n%s" | |
return docfmt % (signature, node_doc) | |
else: | |
if self.is_format_clinic: | |
docfmt = "%s\n--\n\n" | |
else: | |
docfmt = "%s" | |
return docfmt % signature | |
def __call__(self, node): | |
if not Options.docstrings: | |
return node | |
else: | |
return super(EmbedSignature, self).__call__(node) | |
def visit_ClassDefNode(self, node): | |
oldname = self.class_name | |
oldclass = self.class_node | |
self.class_node = node | |
try: | |
# PyClassDefNode | |
self.class_name = node.name | |
except AttributeError: | |
# CClassDefNode | |
self.class_name = node.class_name | |
self.visitchildren(node) | |
self.class_name = oldname | |
self.class_node = oldclass | |
return node | |
def visit_LambdaNode(self, node): | |
# lambda expressions so not have signature or inner functions | |
return node | |
def visit_DefNode(self, node): | |
if not self.current_directives['embedsignature']: | |
return node | |
self._setup_format() | |
is_constructor = False | |
hide_self = False | |
if node.entry.is_special: | |
is_constructor = self.class_node and node.name == '__init__' | |
if not is_constructor: | |
return node | |
class_name = None | |
func_name = node.name | |
if self.is_format_c: | |
func_name = self.class_name | |
hide_self = True | |
else: | |
class_name, func_name = self.class_name, node.name | |
npoargs = getattr(node, 'num_posonly_args', 0) | |
nkargs = getattr(node, 'num_kwonly_args', 0) | |
npargs = len(node.args) - nkargs - npoargs | |
signature = self._fmt_signature( | |
class_name, func_name, node.args, | |
npoargs, npargs, node.star_arg, | |
nkargs, node.starstar_arg, | |
return_expr=node.return_type_annotation, | |
return_type=None, hide_self=hide_self) | |
if signature: | |
if is_constructor and self.is_format_c: | |
doc_holder = self.class_node.entry.type.scope | |
else: | |
doc_holder = node.entry | |
if doc_holder.doc is not None: | |
old_doc = doc_holder.doc | |
elif not is_constructor and getattr(node, 'py_func', None) is not None: | |
old_doc = node.py_func.entry.doc | |
else: | |
old_doc = None | |
new_doc = self._embed_signature(signature, old_doc) | |
doc_holder.doc = EncodedString(new_doc) | |
if not is_constructor and getattr(node, 'py_func', None) is not None: | |
node.py_func.entry.doc = EncodedString(new_doc) | |
return node | |
def visit_CFuncDefNode(self, node): | |
if not node.overridable: # not cpdef FOO(...): | |
return node | |
if not self.current_directives['embedsignature']: | |
return node | |
self._setup_format() | |
signature = self._fmt_signature( | |
self.class_name, node.declarator.base.name, | |
node.declarator.args, | |
return_type=node.return_type) | |
if signature: | |
if node.entry.doc is not None: | |
old_doc = node.entry.doc | |
elif getattr(node, 'py_func', None) is not None: | |
old_doc = node.py_func.entry.doc | |
else: | |
old_doc = None | |
new_doc = self._embed_signature(signature, old_doc) | |
node.entry.doc = EncodedString(new_doc) | |
py_func = getattr(node, 'py_func', None) | |
if py_func is not None: | |
py_func.entry.doc = EncodedString(new_doc) | |
return node | |
def visit_PropertyNode(self, node): | |
if not self.current_directives['embedsignature']: | |
return node | |
self._setup_format() | |
entry = node.entry | |
body = node.body | |
prop_name = entry.name | |
type_name = None | |
if entry.visibility == 'public': | |
if self.is_format_c: | |
# property synthesised from a cdef public attribute | |
type_name = entry.type.declaration_code("", for_display=1) | |
if not entry.type.is_pyobject: | |
type_name = "'%s'" % type_name | |
elif entry.type.is_extension_type: | |
type_name = entry.type.module_name + '.' + type_name | |
elif self.is_format_python: | |
type_name = self._fmt_type(entry.type) | |
if type_name is None: | |
for stat in body.stats: | |
if stat.name != '__get__': | |
continue | |
if self.is_format_c: | |
prop_name = '%s.%s' % (self.class_name, prop_name) | |
ret_annotation = stat.return_type_annotation | |
if ret_annotation: | |
type_name = self._fmt_annotation(ret_annotation) | |
if type_name is not None : | |
signature = '%s: %s' % (prop_name, type_name) | |
new_doc = self._embed_signature(signature, entry.doc) | |
if not self.is_format_clinic: | |
entry.doc = EncodedString(new_doc) | |
return node | |