| 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):
|
|
|
| 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):
|
|
|
|
|
| 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)
|
|
|
| return result
|
|
|
| def _fmt_annotation(self, node):
|
| writer = AnnotationWriter()
|
| result = writer.write(node)
|
|
|
| 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:
|
|
|
| self.class_name = node.name
|
| except AttributeError:
|
|
|
| self.class_name = node.class_name
|
| self.visitchildren(node)
|
| self.class_name = oldname
|
| self.class_node = oldclass
|
| return node
|
|
|
| def visit_LambdaNode(self, node):
|
|
|
| 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:
|
| 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:
|
|
|
| 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
|
|
|