Home | History | Annotate | Download | only in Compiler
      1 from Cython.Compiler.Visitor import CythonTransform
      2 from Cython.Compiler.StringEncoding import EncodedString
      3 from Cython.Compiler import Options
      4 from Cython.Compiler import PyrexTypes, ExprNodes
      5 
      6 class EmbedSignature(CythonTransform):
      7 
      8     def __init__(self, context):
      9         super(EmbedSignature, self).__init__(context)
     10         self.denv = None # XXX
     11         self.class_name = None
     12         self.class_node = None
     13 
     14     unop_precedence = 11
     15     binop_precedence = {
     16         'or': 1,
     17         'and': 2,
     18         'not': 3,
     19         'in': 4, 'not in': 4, 'is': 4, 'is not': 4, '<': 4, '<=': 4, '>': 4, '>=': 4, '!=': 4, '==': 4,
     20         '|': 5,
     21         '^': 6,
     22         '&': 7,
     23         '<<': 8, '>>': 8,
     24         '+': 9, '-': 9,
     25         '*': 10, '/': 10, '//': 10, '%': 10,
     26         # unary: '+': 11, '-': 11, '~': 11
     27         '**': 12}
     28 
     29     def _fmt_expr_node(self, node, precedence=0):
     30         if isinstance(node, ExprNodes.BinopNode) and not node.inplace:
     31             new_prec = self.binop_precedence.get(node.operator, 0)
     32             result = '%s %s %s' % (self._fmt_expr_node(node.operand1, new_prec),
     33                                    node.operator,
     34                                    self._fmt_expr_node(node.operand2, new_prec))
     35             if precedence > new_prec:
     36                 result = '(%s)' % result
     37         elif isinstance(node, ExprNodes.UnopNode):
     38             result = '%s%s' % (node.operator,
     39                                self._fmt_expr_node(node.operand, self.unop_precedence))
     40             if precedence > self.unop_precedence:
     41                 result = '(%s)' % result
     42         elif isinstance(node, ExprNodes.AttributeNode):
     43             result = '%s.%s' % (self._fmt_expr_node(node.obj), node.attribute)
     44         else:
     45             result = node.name
     46         return result
     47 
     48     def _fmt_arg_defv(self, arg):
     49         default_val = arg.default
     50         if not default_val:
     51             return None
     52         try:
     53             denv = self.denv  # XXX
     54             ctval = default_val.compile_time_value(self.denv)
     55             repr_val = repr(ctval)
     56             if isinstance(default_val, ExprNodes.UnicodeNode):
     57                 if repr_val[:1] != 'u':
     58                     return u'u%s' % repr_val
     59             elif isinstance(default_val, ExprNodes.BytesNode):
     60                 if repr_val[:1] != 'b':
     61                     return u'b%s' % repr_val
     62             elif isinstance(default_val, ExprNodes.StringNode):
     63                 if repr_val[:1] in 'ub':
     64                     return repr_val[1:]
     65             return repr_val
     66         except Exception:
     67             try:
     68                 return self._fmt_expr_node(default_val)
     69             except AttributeError, e:
     70                 return '<???>'
     71 
     72     def _fmt_arg(self, arg):
     73         if arg.type is PyrexTypes.py_object_type or arg.is_self_arg:
     74             doc = arg.name
     75         else:
     76             doc = arg.type.declaration_code(arg.name, for_display=1)
     77         if arg.default:
     78             arg_defv = self._fmt_arg_defv(arg)
     79             if arg_defv:
     80                 doc = doc + ('=%s' % arg_defv)
     81         return doc
     82 
     83     def _fmt_arglist(self, args,
     84                      npargs=0, pargs=None,
     85                      nkargs=0, kargs=None,
     86                      hide_self=False):
     87         arglist = []
     88         for arg in args:
     89             if not hide_self or not arg.entry.is_self_arg:
     90                 arg_doc = self._fmt_arg(arg)
     91                 arglist.append(arg_doc)
     92         if pargs:
     93             arglist.insert(npargs, '*%s' % pargs.name)
     94         elif nkargs:
     95             arglist.insert(npargs, '*')
     96         if kargs:
     97             arglist.append('**%s' % kargs.name)
     98         return arglist
     99 
    100     def _fmt_ret_type(self, ret):
    101         if ret is PyrexTypes.py_object_type:
    102             return None
    103         else:
    104             return ret.declaration_code("", for_display=1)
    105 
    106     def _fmt_signature(self, cls_name, func_name, args,
    107                        npargs=0, pargs=None,
    108                        nkargs=0, kargs=None,
    109                        return_type=None, hide_self=False):
    110         arglist = self._fmt_arglist(args,
    111                                     npargs, pargs,
    112                                     nkargs, kargs,
    113                                     hide_self=hide_self)
    114         arglist_doc = ', '.join(arglist)
    115         func_doc = '%s(%s)' % (func_name, arglist_doc)
    116         if cls_name:
    117             func_doc = '%s.%s' % (cls_name, func_doc)
    118         if return_type:
    119             ret_doc = self._fmt_ret_type(return_type)
    120             if ret_doc:
    121                 func_doc = '%s -> %s' % (func_doc, ret_doc)
    122         return func_doc
    123 
    124     def _embed_signature(self, signature, node_doc):
    125         if node_doc:
    126             return "%s\n%s" % (signature, node_doc)
    127         else:
    128             return signature
    129 
    130     def __call__(self, node):
    131         if not Options.docstrings:
    132             return node
    133         else:
    134             return super(EmbedSignature, self).__call__(node)
    135 
    136     def visit_ClassDefNode(self, node):
    137         oldname = self.class_name
    138         oldclass = self.class_node
    139         self.class_node = node
    140         try:
    141             # PyClassDefNode
    142             self.class_name = node.name
    143         except AttributeError:
    144             # CClassDefNode
    145             self.class_name = node.class_name
    146         self.visitchildren(node)
    147         self.class_name = oldname
    148         self.class_node = oldclass
    149         return node
    150 
    151     def visit_DefNode(self, node):
    152         if not self.current_directives['embedsignature']:
    153             return node
    154 
    155         is_constructor = False
    156         hide_self = False
    157         if node.entry.is_special:
    158             is_constructor = self.class_node and node.name == '__init__'
    159             if not is_constructor:
    160                 return node
    161             class_name, func_name = None, self.class_name
    162             hide_self = True
    163         else:
    164             class_name, func_name = self.class_name, node.name
    165 
    166         nkargs = getattr(node, 'num_kwonly_args', 0)
    167         npargs = len(node.args) - nkargs
    168         signature = self._fmt_signature(
    169             class_name, func_name, node.args,
    170             npargs, node.star_arg,
    171             nkargs, node.starstar_arg,
    172             return_type=None, hide_self=hide_self)
    173         if signature:
    174             if is_constructor:
    175                 doc_holder = self.class_node.entry.type.scope
    176             else:
    177                 doc_holder = node.entry
    178 
    179             if doc_holder.doc is not None:
    180                 old_doc = doc_holder.doc
    181             elif not is_constructor and getattr(node, 'py_func', None) is not None:
    182                 old_doc = node.py_func.entry.doc
    183             else:
    184                 old_doc = None
    185             new_doc  = self._embed_signature(signature, old_doc)
    186             doc_holder.doc = EncodedString(new_doc)
    187             if not is_constructor and getattr(node, 'py_func', None) is not None:
    188                 node.py_func.entry.doc = EncodedString(new_doc)
    189         return node
    190 
    191     def visit_CFuncDefNode(self, node):
    192         if not self.current_directives['embedsignature']:
    193             return node
    194         if not node.overridable: # not cpdef FOO(...):
    195             return node
    196 
    197         signature = self._fmt_signature(
    198             self.class_name, node.declarator.base.name,
    199             node.declarator.args,
    200             return_type=node.return_type)
    201         if signature:
    202             if node.entry.doc is not None:
    203                 old_doc = node.entry.doc
    204             elif getattr(node, 'py_func', None) is not None:
    205                 old_doc = node.py_func.entry.doc
    206             else:
    207                 old_doc = None
    208             new_doc = self._embed_signature(signature, old_doc)
    209             node.entry.doc = EncodedString(new_doc)
    210             if hasattr(node, 'py_func') and node.py_func is not None:
    211                 node.py_func.entry.doc = EncodedString(new_doc)
    212         return node
    213 
    214     def visit_PropertyNode(self, node):
    215         if not self.current_directives['embedsignature']:
    216             return node
    217 
    218         entry = node.entry
    219         if entry.visibility == 'public':
    220             # property synthesised from a cdef public attribute
    221             type_name = entry.type.declaration_code("", for_display=1)
    222             if not entry.type.is_pyobject:
    223                 type_name = "'%s'" % type_name
    224             elif entry.type.is_extension_type:
    225                 type_name = entry.type.module_name + '.' + type_name
    226             signature = '%s: %s' % (entry.name, type_name)
    227             new_doc = self._embed_signature(signature, entry.doc)
    228             entry.doc = EncodedString(new_doc)
    229         return node
    230