Example #1
0
File: pyext.py Project: sr-gi/clif
 def WrapClass(self, c, unused_ln, cpp_namespace, unused_class_ns=''):
     """Process AST.ClassDecl c."""
     cname = Ident(c.name.cpp_name)
     pyname = c.name.native
     self.Add(Context(cname, pyname, c.name.cpp_name))
     ns = self.class_namespace
     yield ''
     yield 'namespace %s {' % ns
     virtual, has_iterator = _IsSpecialClass(c, pyname)
     if virtual:
         for s in gen.VirtualOverriderClass(
                 VIRTUAL_OVERRIDER_CLASS,
                 pyname,
                 c.name.cpp_name,
                 c.name.cpp_name + '::' + cname,
                 c.cpp_abstract,
                 Ident,
                 vfuncs=virtual,
                 pcfunc=lambda t, tm=self.typemap: postconv.Initializer(
                     t, tm)):
             yield s
         c.bases.add().cpp_name = c.name.cpp_name
     # Flag that we're now in the nested __iter__ class.
     iter_class = (pyname == _ITER_KW)
     for s in gen.WrapperClassDef(
             WRAPPER_CLASS_NAME,
             is_iter=iter_class,
             has_iter=has_iterator,
             iter_ns=_ClassNamespace(_ITER_KW),
             cname=c.name.cpp_name,
             ctype=VIRTUAL_OVERRIDER_CLASS if virtual else c.name.cpp_name,
             enable_instance_dict=c.enable_instance_dict):
         yield s
     tracked_slot_groups = {}
     cpp_has_ext_def_ctor = False
     if iter_class:
         # Special-case nested __iter__ class.
         for s in _WrapIterSubclass(c.members, self.typemap):
             yield s
         tp_slots = {
             'tp_flags': ['Py_TPFLAGS_DEFAULT'],
             'tp_iter': 'PyObject_SelfIter',
             'tp_iternext': gen.IterNext.name
         }
         ctor = None
     else:
         # Normal class generator.
         if c.final:
             self.final = True
         else:
             yield ''
             yield 'static %s* ThisPtr(PyObject*);' % c.name.cpp_name
         ctor = ('DEF' if c.cpp_has_def_ctor and
                 (not c.cpp_abstract or virtual) else None)
         for d in c.members:
             if d.decltype == d.FUNC:
                 f = d.func
                 if f.name.native == '__init__':
                     if virtual:
                         # Constructors are not virtual, but we mark the constructor as
                         # virtual to indicate that there exist virtual functions in the
                         # Clif class declaration.
                         f.virtual = True
                     if not f.params:
                         if f.is_extend_method:
                             cpp_has_ext_def_ctor = True
                         else:
                             # Skip generating wrapper function for unextended default
                             # ctor. But wrapper function for extended default ctor is still
                             # necessary.
                             continue
                     ctor = 'wrap%s_as___init__' % types.Mangle(cname)
                 elif c.cpp_abstract and f.virtual:
                     continue  # Skip calling virtual func from the abstract base class.
             for s in self.WrapDecl(d, parent_ns=cpp_namespace,
                                    class_ns=ns):
                 yield s
         if virtual and not ctor:
             raise ValueError(
                 'A constructor should be declared in the Clif wrapper for %s as it '
                 'has virtual method declarations.' % c.name.cpp_name)
         # For Py2 slots.py relies on Py_TPFLAGS_DEFAULT being set.
         tp_slots = {'tp_flags': ['Py_TPFLAGS_DEFAULT']}
         if c.cpp_abstract:
             tp_slots['tp_flags'].append('Py_TPFLAGS_IS_ABSTRACT')
         if not self.py3output:
             tp_slots['tp_flags'].append('Py_TPFLAGS_CHECKTYPES')
         if not c.final:
             tp_slots['tp_flags'].append('Py_TPFLAGS_BASETYPE')
         if has_iterator:
             n = _ClassNamespace(_ITER_KW) + '::'
             w = n + WRAPPER_CLASS_NAME
             # Python convention to have a type struct named FOO_Type.
             for s in gen.NewIter(_GetCppObj(), n, w, w + '_Type'):
                 yield s
             tp_slots['tp_iter'] = gen.NewIter.name
         if self.properties or c.enable_instance_dict:
             yield ''
             for s in gen.GetSetDef(self.properties,
                                    c.enable_instance_dict):
                 yield s
             tp_slots['tp_getset'] = gen.GetSetDef.name
         for b in c.bases:
             if b.cpp_name and not b.native:
                 p = b.cpp_name
                 w = 'as_' + types.Mangle(p)  # pyname == cname == w
                 for s in gen.CastAsCapsule(_GetCppObj(), p, w):
                     yield s
                 self.methods.append((w, w, NOARGS, 'Upcast to %s*' % p))
         _AppendReduceExIfNeeded(self.methods)
         if self.methods:
             for s in slots.GenSlots(self.methods,
                                     tp_slots,
                                     py3=self.py3output,
                                     tracked_groups=tracked_slot_groups):
                 yield s
             if self.methods:  # If all methods are slots, it's empty.
                 for s in gen.MethodDef(self.methods):
                     yield s
                 tp_slots['tp_methods'] = gen.MethodDef.name
     qualname = '.'.join(f.pyname for f in self.nested)
     tp_slots['tp_name'] = '"%s.%s"' % (self.path, qualname)
     if c.docstring:
         docstring = c.docstring.strip()
         # Escape characters for inclusion in the raw C string.
         if str is bytes:  # PY2
             docstring = docstring.encode('unicode-escape')
         docstring = docstring.replace('\n', r'\n')
         docstring = docstring.replace('"', r'\"')
         tp_slots['tp_doc'] = '"%s"' % docstring
     if c.async_dtor and c.cpp_has_trivial_dtor:
         raise ValueError(
             'Class %s has a trivial dtor yet @async__del__ decorator' %
             pyname)
     # Python convention to have a type struct named FOO_Type.
     # Generate wrapper Type object in wname+'_Type' static var.
     for s in gen.TypeObject(
             qualname,
             tracked_slot_groups,
             tp_slots,
             pyname,
             ctor,
             wname=WRAPPER_CLASS_NAME,
             fqclassname=c.name.cpp_name,
             abstract=c.cpp_abstract,
             iterator=_GetCppObj('iter') if iter_class else None,
             trivial_dtor=c.cpp_has_trivial_dtor,
             subst_cpp_ptr=VIRTUAL_OVERRIDER_CLASS if virtual else '',
             enable_instance_dict=c.enable_instance_dict,
             cpp_has_ext_def_ctor=cpp_has_ext_def_ctor):
         yield s
     if not iter_class:
         for s in types.GenThisPointerFunc(c.name.cpp_name,
                                           WRAPPER_CLASS_NAME, c.final):
             yield s
     yield ''
     yield '}  // namespace ' + ns
     type_dict = self.dict
     wrapns = '::'.join(f.class_namespace for f in self.nested) + '::'
     wclass = wrapns + WRAPPER_CLASS_NAME
     vclass = wrapns + VIRTUAL_OVERRIDER_CLASS
     # Python convention to have a type struct named FOO_Type.
     wtype = wclass + '_Type'
     self.DropContext()
     base, wrapped_base = _ProcessInheritance(
         c.bases, '::%s_Type' % WRAPPER_CLASS_NAME, self.namemap)
     self.types_init.append((wtype, base, wrapped_base, type_dict))
     if iter_class:
         if base:
             raise TypeError(
                 "__iter__ class can't be derived, base '%s' found" % base)
     else:
         self.dict.append((pyname, types.AsPyObj(wtype)))
         self.types.append(
             types.ClassType(c.name.cpp_name,
                             qualname,
                             wclass,
                             wtype,
                             wrapns,
                             can_copy=c.cpp_copyable and not c.cpp_abstract,
                             can_move=c.cpp_movable and not c.cpp_abstract,
                             can_destruct=c.cpp_has_public_dtor,
                             virtual=vclass if virtual else '',
                             ns=cpp_namespace))
Example #2
0
 def WrapClass(self, c, unused_ln, cpp_namespace):
     """Process AST.ClassDecl c."""
     cname = Ident(c.name.cpp_name)
     pyname = c.name.native
     self.Add(Context(cname, pyname, c.name.cpp_name))
     ns = self.class_namespace
     yield ''
     yield 'namespace %s {' % ns
     virtual = False
     # If c has @virtual members, generate a derived redirector class.
     for d in c.members:
         if d.decltype == d.FUNC and d.func.virtual:
             if not virtual:
                 yield ''
                 yield 'struct %s : PyObj, %s {' % (VIRTUAL_OVERRIDER_CLASS,
                                                    c.name.cpp_name)
                 yield I + 'using %s::%s;' % (c.name.cpp_name, cname)
                 virtual = True
             for s in gen.VirtualFunctionCall(
                     Ident(d.func.name.cpp_name), d.func, pyname,
                     c.cpp_abstract, lambda atype: postconv.Initializer(
                         atype, self.typemap)):
                 yield s
     if virtual:
         if c.final:
             raise ValueError("Final class %s can't have virtual methods." %
                              pyname)
         yield '};'
         b = c.bases.add()
         b.cpp_name = c.name.cpp_name
     yield ''
     yield 'struct %s {' % self.wrapper_class_name
     yield I + 'PyObject_HEAD'
     yield I + '::clif::SharedPtr<%s> cpp;' % (VIRTUAL_OVERRIDER_CLASS if
                                               virtual else c.name.cpp_name)
     yield '};'
     if c.final:
         self.final = True
     else:
         yield 'static %s* ThisPtr(PyObject*);' % c.name.cpp_name
     ctor = ('DEF' if c.cpp_has_def_ctor and
             (not c.cpp_abstract or virtual) else None)
     for d in c.members:
         if d.decltype == d.FUNC:
             f = d.func
             if f.name.native == '__init__':
                 if virtual:
                     # Constructors are not virtual, but we mark the constructor as
                     # virtual to indicate that there exist virtual functions in the
                     # Clif class declaration.
                     f.virtual = True
                 if not f.params:
                     continue  # Skip generating wrapper function for default ctor.
                 ctor = 'wrap%s_as___init__' % types.Mangle(cname)
             elif c.cpp_abstract and f.virtual:
                 continue  # Skip calling virtual func from the abstract base class.
         for s in (self.WrapDecl(d, cpp_namespace)):
             yield s
     if virtual and not ctor:
         raise ValueError(
             'A constructor should be declared in the Clif wrapper for %s as it '
             'has virtual method declarations.' % c.name.cpp_name)
     # For Py2 slots.py relies on Py_TPFLAGS_DEFAULT being set.
     tp_slots = {
         'tp_flags': ['Py_TPFLAGS_DEFAULT', 'Py_TPFLAGS_TYPE_SUBCLASS']
     }
     if c.cpp_abstract:
         tp_slots['tp_flags'].append('Py_TPFLAGS_IS_ABSTRACT')
     if not self.py3output:
         tp_slots['tp_flags'].append('Py_TPFLAGS_CHECKTYPES')
     if not c.final: tp_slots['tp_flags'].append('Py_TPFLAGS_BASETYPE')
     if self.properties:
         for s in (gen.GetSetDef(self.properties)):
             yield s
         tp_slots['tp_getset'] = gen.GetSetDef.name
     for b in c.bases:
         if b.cpp_name and not b.native:
             p = b.cpp_name
             w = 'as_' + types.Mangle(p)  # pyname == cname == w
             yield ''
             yield '// Implicit cast this as %s*' % p
             yield 'static PyObject* %s(PyObject* self) {' % w
             yield I + ('%s* p = ::clif::python::Get(reinterpret_cast<%s*>('
                        'self)->cpp);' % (p, self.wrapper_class_name))
             yield I + 'if (p == nullptr) return nullptr;'
             yield I + ('return PyCapsule_New(p, C("%s"), nullptr);') % p
             yield '}'
             self.methods.append((w, w, NOARGS, 'Upcast to %s*' % p))
     if self.methods:
         for s in (slots.GenSlots(self.methods,
                                  tp_slots,
                                  py3=self.py3output)):
             yield s
         if self.methods:  # If all methods are slots, it's empty.
             for s in (gen.MethodDef(self.methods)):
                 yield s
             tp_slots['tp_methods'] = gen.MethodDef.name
     pypath = '.'.join(f.pyname for f in self.nested)
     tp_slots['tp_name'] = '"%s.%s"' % (self.path, pypath)
     for s in (
             # Generate wrapper Type object in wname+'_Type' static var.
             gen.TypeObject(
                 tp_slots,
                 slots.GenTypeSlots,
                 pyname,
                 ctor=ctor,
                 wname=self.wrapper_class_name,
                 fqclassname=c.name.cpp_name,
                 abstract=c.cpp_abstract,
                 subst_cpp_ptr=VIRTUAL_OVERRIDER_CLASS if virtual else '',
                 async_dtor=c.async_dtor,
             )):
         yield s
     for s in types.GenThisPointerFunc(c.name.cpp_name,
                                       self.wrapper_class_name, c.final):
         yield s
     yield '}  // namespace ' + ns
     type_dict = self.dict
     wrapns = '::'.join(f.class_namespace for f in self.nested) + '::'
     wclass = wrapns + self.wrapper_class_name
     vclass = wrapns + VIRTUAL_OVERRIDER_CLASS
     wrap = '::' + self.wrapper_class_name
     wtype = wclass + '_Type'
     wrapt = wrap + '_Type'
     self.DropContext()
     base = cpp_replacement = None
     if c.bases:
         # Pytd fills bases with native name (cpp_name='') for Python inheritance.
         # Matcher extends it with cpp_name (native=='') for C++ parent classes.
         base = [b.native for b in c.bases if b.native and not b.cpp_name]
         if base:
             if len(base) > 1:
                 raise ValueError('Multiple inheritance not supported')
             base = base[0]
             if '.' not in base:
                 # base defined in the same .clif wrapper
                 base = _ClassNamespace(base) + wrapt
         elif c.bases[0].native == 'replacement':
             assert c.bases[0].cpp_name
             cpp_replacement = c.bases[0].cpp_name
     self.types_init.append((wtype, base, type_dict))
     self.dict.append(
         (pyname, types.AsPyObj(self.wrap_namespace + '::' + wtype)))
     if cpp_replacement:
         # Find replacement class namespace.
         for b in c.cpp_bases:
             if b.name == cpp_replacement:
                 cpp_namespace = b.namespace
                 break
         else:
             cpp_namespace = ''
     self.types.add(
         types.ClassType(c.name.cpp_name,
                         pypath,
                         wclass,
                         wtype,
                         wrapns,
                         can_copy=c.cpp_copyable and not c.cpp_abstract,
                         can_destruct=c.cpp_has_public_dtor,
                         down_cast=cpp_replacement,
                         virtual=vclass if virtual else '',
                         ns=cpp_namespace))