Commits

Virgil Dupras committed cb6414d

Allow o2p wrapper to inherit each other.

Comments (0)

Files changed (7)

demos/inherit/main.m

+#import <Cocoa/Cocoa.h>
+#import <Python.h>
+#import "Foo.h"
+#import "Bar.h"
+
+int main(int argc, char *argv[])
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    Py_Initialize();
+    FILE* fp = fopen("main.py", "r");
+    PyRun_SimpleFile(fp, "main.py");
+    fclose(fp);
+    Foo *foo = [[Foo alloc] init];
+    [foo helloFromFoo];
+    [foo release];
+    Bar *bar = [[Bar alloc] init];
+    [bar helloFromBar];
+    [bar helloFromFoo];
+    [bar release];
+    Py_Finalize();
+    [pool release];
+    return 0;   
+}

demos/inherit/main.py

+class Foo:
+    def helloFromFoo(self):
+        print("Hello from Foo!")
+
+class Bar(Foo):
+    def helloFromBar(self):
+        print("Hello from Bar!")

demos/inherit/waf

Binary file added.

demos/inherit/wscript

+#! /usr/bin/env python
+# encoding: utf-8
+# Chris Pickel, 2011
+
+VERSION='0.0.1'
+APPNAME='hello_world'
+
+top = '.'
+out = 'build'
+
+def options(opt):
+    opt.load('python')
+    opt.load('compiler_c')
+
+def configure(conf):
+    conf.load('compiler_c')
+    conf.load('python')
+    conf.check_python_version((3,2))
+    conf.check_python_headers()
+    conf.env.append_value('FRAMEWORK_COCOA', 'Cocoa')
+    conf.env.ARCH = ['i386', 'x86_64']
+
+def build(bld):
+    import objp.o2p
+    import main
+    objp.o2p.generate_objc_code(main.Foo, 'autogen')
+    objp.o2p.generate_objc_code(main.Bar, 'autogen', inherit=True)
+    bld.program(
+        features      = 'c cprogram pyembed',
+        target        = 'inherit',
+        source        = 'main.m autogen/Foo.m autogen/Bar.m autogen/ObjP.m',
+        use           = 'COCOA',
+        includes      = '. autogen',
+    )
+    bld(
+        rule = 'cp ${SRC} ${TGT}',
+        source = bld.path.make_node('main.py'),
+        target = bld.path.get_bld().make_node('main.py'),
+    )
+
+from waflib import TaskGen
+@TaskGen.extension('.m')
+def m_hook(self, node):
+    """Alias .m files to be compiled the same as .c files, gcc will do the right thing."""
+    return self.create_compiled_task('c', node)
+
+@TaskGen.feature('cshlib')
+@TaskGen.after('apply_obj_vars', 'apply_link')
+def kill_flag(self):
+    fl = self.link_task.env.LINKFLAGS
+    if '-bundle' in fl and '-dynamiclib' in fl:
+         fl.remove('-bundle')
+         self.link_task.env.LINKFLAGS = fl
+
+# -*- indent-tabs-mode: t -*-
 
 TypeSpec = namedtuple('TypeSpec', 'pytype objctype o2p_code p2o_code')
 ArgSpec = namedtuple('ArgSpec', 'argname typespec')
-MethodSpec = namedtuple('MethodSpec', 'pyname objcname argspecs returntype')
-ClassSpec = namedtuple('ClassSpec', 'clsname methodspecs is_protocol')
+MethodSpec = namedtuple('MethodSpec', 'pyname objcname argspecs returntype is_inherited')
+ClassSpec = namedtuple('ClassSpec', 'clsname superclass methodspecs is_protocol follow_protocols')
 
 TYPE_SPECS = [
     TypeSpec(str, 'NSString *', 'ObjP_str_o2p(%s)', 'ObjP_str_p2o(%s)'),
 @end
 """
 
+TEMPLATE_HEADER_INHERITED = """
+#import <Cocoa/Cocoa.h>
+#import <Python.h>
+%%imports%%
+
+@interface %%classname%%:%%superclass%% %%protocols%% {}
+%%methods%%
+@end
+"""
+
 TEMPLATE_UNIT = """
 #import "%%classname%%.h"
 #import "ObjP.h"
 @end
 """
 
+TEMPLATE_UNIT_INHERITED = """
+#import "%%classname%%.h"
+#import "ObjP.h"
+
+@implementation %%classname%%
+%%methods%%
+@end
+"""
+
 TEMPLATE_INIT_METHOD = """
 - %%signature%%
 {
     elems = [elems[0]] + [e.title() for e in elems[1:]]
     return ''.join(elems)
 
-def internalize_argspec(name, argspec):
+def internalize_argspec(name, argspec, is_inherited):
     # take argspec from the inspect module and returns MethodSpec
     args = argspec.args[1:] # remove self
     ann = argspec.annotations
         else:
             returntype = None
         objcname = name.replace('_', ':')
-    return MethodSpec(name, objcname, argspecs, returntype)
+    return MethodSpec(name, objcname, argspecs, returntype, is_inherited)
 
 def get_arg_c_code(argspecs):
     result = []
         if getattr(meth, 'dontwrap', False):
             continue
         argspec = inspect.getfullargspec(meth)
+        is_inherited = name not in class_.__dict__
         try:
             if hasattr(meth, 'objcname'):
                 name = meth.objcname
-            methodspec = internalize_argspec(name, argspec)
+            methodspec = internalize_argspec(name, argspec, is_inherited)
             methodspecs.append(methodspec)
         except AssertionError:
             print("Warning: Couldn't generate spec for %s" % name)
             continue
     if not any(ms.pyname == '__init__' for ms in methodspecs):
         # Always create a default init method.
-        methodspecs.insert(0, MethodSpec('__init__', 'init', [], PYTYPE2SPEC[object]))
-    return ClassSpec(class_.__name__, methodspecs, True)
+        methodspecs.insert(0, MethodSpec('__init__', 'init', [], PYTYPE2SPEC[object], True))
+    follow_protocols = getattr(class_, 'FOLLOW_PROTOCOLS', [])
+    superclass = class_.__bases__[0].__name__
+    return ClassSpec(class_.__name__, superclass, methodspecs, True, follow_protocols)
 
-def generate_objc_code(class_, destfolder, extra_imports=None, follow_protocols=None):
+def generate_objc_code(class_, destfolder, inherit=False):
     clsspec = spec_from_python_class(class_)
     clsname = clsspec.clsname
     method_code = []
     method_sigs = []
     for methodspec in clsspec.methodspecs:
+        if inherit and methodspec.is_inherited and methodspec.pyname != '__init__':
+            continue
         try:
             code, sig = get_objc_method_code(clsspec, methodspec)
         except AssertionError:
             continue
         method_code.append(code)
         method_sigs.append(sig)
-    if extra_imports:
-        tmpl_imports = '\n'.join('#import "%s"' % imp for imp in extra_imports)
-    else:
-        tmpl_imports = ''
-    if follow_protocols:
-        tmpl_protocols = '<%s>' % ','.join(follow_protocols)
+    if clsspec.follow_protocols:
+        tmpl_imports = '\n'.join('#import "%s.h"' % imp for imp in clsspec.follow_protocols)
+        tmpl_protocols = '<%s>' % ','.join(clsspec.follow_protocols)
     else:
         tmpl_protocols = ''
-    header = tmpl_replace(TEMPLATE_HEADER, classname=clsname, methods='\n'.join(method_sigs),
-        imports=tmpl_imports, protocols=tmpl_protocols)
-    implementation = tmpl_replace(TEMPLATE_UNIT, classname=clsname, methods=''.join(method_code))
+        tmpl_imports = ''
+    if inherit:
+        tmpl_superclass = clsspec.superclass
+        tmpl_imports += '\n#import "%s.h"' % clsspec.superclass
+        header = tmpl_replace(TEMPLATE_HEADER_INHERITED, classname=clsname, methods='\n'.join(method_sigs),
+            imports=tmpl_imports, protocols=tmpl_protocols, superclass=tmpl_superclass)
+        implementation = tmpl_replace(TEMPLATE_UNIT_INHERITED, classname=clsname, methods=''.join(method_code))
+    else:
+        header = tmpl_replace(TEMPLATE_HEADER, classname=clsname, methods='\n'.join(method_sigs),
+                imports=tmpl_imports, protocols=tmpl_protocols)
+        implementation = tmpl_replace(TEMPLATE_UNIT, classname=clsname, methods=''.join(method_code))
     copy_objp_unit(destfolder)
     with open(op.join(destfolder, '%s.h' % clsname), 'wt') as fp:
         fp.write(header)
                 argtype = OBJCTYPE2SPEC[elem[1]]
                 args.append(ArgSpec(argname, argtype))
         pyname = name.replace(':', '_')
-        method_specs.append(MethodSpec(pyname, name, args, resulttype))
-    return ClassSpec(clsname, method_specs, is_protocol)
+        method_specs.append(MethodSpec(pyname, name, args, resulttype, False))
+    return ClassSpec(clsname, '', method_specs, is_protocol, [])
 
 def generate_target_protocol(clsspec):
     clsname = clsspec.clsname