Advertisement
Guest User

PyPyPipe for Blender2.5

a guest
Jul 29th, 2010
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 32.06 KB | None | 0 0
  1. '''
  2. PyPyPipeProxy v0.5
  3. Brett Hartshorn 2010 - License BSD
  4. goatman.py@gmail.com
  5.  
  6. Use CPython as a host for external libs, tested with PyGTK, PyODE, Pygame and Blender2.5
  7.  
  8. Testing:
  9.     blender25/blender -P pypypipe.py
  10.     (you should see the cube and camera moving in a circle, along with planes being created and the camera scaled)
  11.  
  12. Blender2.5:
  13.     In Progress:
  14.         bpy.ops
  15.         bpy.data
  16.     Differences:
  17.         modules are not nested, instead of bpy.data.objects, use bpy_data_objects
  18.         returned objects do not have dynamic attribute support, the work around is to use the GET_<attrname> methods,
  19.         and to set call the attribute as a function, example:
  20.             scale = ob.GET_scale()
  21.             scale[0] = 10.0
  22.             ob.scale( scale )
  23.         objects that act like dicts will not work, but bpy already provides functions for getting from dict like objects (collections)
  24.             # this will not work
  25.             ob = bpy_data_objects['Cube']
  26.             # this will work
  27.             ob = bpy_data_objects.get('Cube')
  28.  
  29.         see the pypy_entry_point_test_bpy function below for what is working
  30.  
  31. Notes:
  32.     1. no keyword arguments can be used from RPython, unless you define a custom wrapper.
  33.     2. only simple lambda callbacks are working (these operate in CPython space)
  34.     3. you do not need to use pypy translation, instead just call your entry point function,
  35.     but you still need the pypy source code and set the path to it because rpoll is used as the pipe selector.
  36.  
  37. Getting Started:
  38.     make sure this file is named pypypipe.py
  39.     copy this file to your pypy trunk folder, or add the path to pypy below PATH2PYPY
  40.     optional: make sure you have pygame, pygtk, and pyode  (currently broken, only blender25 is working)
  41.  
  42. Hacking:
  43.     Dynamic attribute access is not possible, but function calls are ok.
  44.     This is wrapper incomplete, any class with unique init args, or functions with unique
  45.     args must be hand wrapped.  Even the return type may need to be defined.
  46.     For Blender proper arg and return types are generated by inspecting RNA, so its safe to use keyword args.
  47.  
  48.  
  49. pypy hacking notes:
  50.     s = s[1:len(s)-1]       # pypy.rpython.error.TyperError: slice stop must be proved non-negative
  51.     s = s.replace('<','').replace('>','')   # pypy.rpython.error.TyperError: replace only works for char args
  52.     long(string, 16) not allowed
  53.     globals() not allowed
  54.     function return types can not be mixed (required generated return functions per incompatible types)
  55.  
  56. '''
  57.  
  58. def pypy_entry_point_test_bpy():
  59.     start = time.time()
  60.     ## init wrapper ##
  61.     bpy_ops = bpy_ops_wrapped()
  62.     bpy_ops_mesh = bpy_ops.mesh()
  63.     bpy_data = bpy_data_wrapped()
  64.     bpy_data_objects = bpy_data.objects()
  65.     ######################
  66.  
  67.     objs = bpy_data_objects.values()
  68.     debug('----------------')
  69.     for ob in objs:
  70.         debug(str(ob))
  71.         debug( str( ob.location()) )
  72.  
  73.     names = bpy_data_objects.keys()
  74.     camera = None
  75.     for n in names:
  76.         debug( n )
  77.         ob = bpy_data_objects.get( n )
  78.         debug( str(ob) )
  79.         if n == 'Camera':
  80.             camera = ob
  81.             debug('found the camera')
  82.             #camera.select( True )  # broken, why only 1 arg taken?
  83.             scale = camera.GET_scale()
  84.             debug( 'camera scale: %s' %scale )
  85.             scale[0] = 10.0
  86.             camera.scale( scale )
  87.             break
  88.  
  89.     i = 0
  90.     while True:
  91.         x = math.sin( radians(i) ) * 1.0
  92.         y = math.cos( radians(i) ) * 1.0
  93.         bpy_ops_mesh.primitive_plane_add( location=(x,y,.0) )
  94.         for ob in objs: ob.location( [x,y,.0] )
  95.         i += 1
  96.         if i == 360: break
  97.  
  98.  
  99.     debug( 'run time: %s' %(time.time()-start) )
  100.     debug('bpy ops test complete')
  101.  
  102.  
  103. ## Set your PyPy root ##
  104. PATH2PYPY = 'pypy'
  105.  
  106. import os, sys, time, inspect, types, select, subprocess, math, pickle
  107. if '.' not in sys.path: sys.path.append( '.' )      # blender253 pickle bug
  108. sys.path.append(PATH2PYPY)      # assumes you have pypy dist in a subfolder, you may need to rename this pypy-trunk
  109. PYTHON_VERSION = sys.version_info[0]
  110. DEBUG = True
  111.  
  112. if '--pypy' in sys.argv:
  113.     from pypy.rlib import streamio
  114.     from pypy.rlib import rpoll
  115.     from pypy.translator.interactive import Translation
  116.     stdin = streamio.fdopen_as_stream(0, 'r', 0)        # fd, mode, buffering
  117.     stdout = streamio.fdopen_as_stream(1, 'w', 0)
  118.     stderr = streamio.fdopen_as_stream(2, 'w', 0)
  119.  
  120. bpy = glib = gtk = ode = pygame = None
  121. try:
  122.     import glib, gtk, pygame, ode
  123. except:
  124.     try:
  125.         import bpy
  126.         ops = bpy.ops
  127.         data = bpy.data
  128.         types = bpy.types
  129.     except: pass
  130.  
  131. DynamicWrappers = {}
  132. DynamicShadowClasses = {}
  133.  
  134.  
  135. RNA_PROP_MAPPING = {
  136.     'POINTER': object,
  137.     'FLOAT': float,
  138.     'BOOLEAN': bool,
  139.     'INT': int,
  140.     'ENUM': str,
  141.     'STRING':str,
  142.     'COLLECTION': (object,),        # this can be a list of dicts (link-append file=[{}]) or a return list of objects, ob.modifiers - TODO decide what to do
  143. }
  144. def reflect_RNA_prop( prop ):
  145.     #print( dir(prop))
  146.     #try: print( prop.default )
  147.     #except: pass
  148.     try:rtype = RNA_PROP_MAPPING[ prop.type ]
  149.     except: print( dir(prop) ); print(prop.fixed_type); raise
  150.     a = {
  151.         'name':prop.identifier,     #name,
  152.         'type': rtype,
  153.         #'default':prop.default,
  154.     }
  155.     if rtype is object:
  156.         a['module'] = prop.fixed_type.__class__.__module__
  157.         a['class'] = prop.fixed_type.__class__.__name__
  158.     if hasattr( prop, 'default' ):
  159.         #if not prop.use_output:
  160.         a['default'] = prop.default
  161.         #if rtype is bool: a['default'] = 'bool'
  162.         #print( dir(prop) ) #array_length', 'bl_rna', 'default', 'default_array
  163.     if hasattr( prop, 'array_length' ):
  164.         a['array-length'] = prop.array_length
  165.         if prop.array_length:
  166.             a['default'] = [ prop.default ] * prop.array_length
  167.             if rtype is bool: a['default'][0] = True        # mini hack
  168.         #for x in prop.default_array: print(x)
  169.  
  170.         #print( a)
  171.     return a
  172.  
  173.  
  174. def reflect_RNA_instance( ob ):
  175.     class genRNA(object): type = ob.type
  176.     shadow_name = 'RBlender_%s' %ob.type
  177.     genRNA.__name__ = shadow_name
  178.     globals()[ shadow_name ] = genRNA
  179.     for prop in ob.bl_rna.properties:
  180.         if not prop.is_readonly:
  181.             print(prop)
  182.             dummy = lambda:()
  183.             dummy._argument_info = ai = [ reflect_RNA_prop( prop ) ]
  184.             dummy._return_info = ri = reflect_RNA_prop( prop )
  185.             setattr( genRNA, prop.identifier, dummy )
  186.             #if prop.identifier == 'location':
  187.             #   print(ai)
  188.             #   print( ri )
  189.             #   raise
  190.         dummy = lambda:()
  191.         dummy._return_info = ri = reflect_RNA_prop( prop )
  192.         setattr( genRNA, 'GET_%s' %prop.identifier, dummy )
  193.  
  194.     for bfunc in ob.bl_rna.functions:
  195.         args = []
  196.         returns = []
  197.         for prop in bfunc.parameters:
  198.             if prop.use_output: returns.append( reflect_RNA_prop( prop ) )
  199.             else: args.append( reflect_RNA_prop( prop ) )
  200.         if len(returns) == 1: returns = returns[0]
  201.         elif not returns:  returns = None
  202.         dummy = lambda:()
  203.         dummy._argument_info = args
  204.         dummy._return_info = returns
  205.         setattr( genRNA, bfunc.identifier, dummy )
  206.     return genRNA
  207.  
  208.  
  209. class BlenderObjectWrapper(object):
  210.     def __init__(self, ob ):
  211.         self._wrapped = ob
  212.         self._rpython_shadow_class = 'RBlender_%s' %ob.type
  213.         DynamicShadowClasses[ self._rpython_shadow_class ] = BlenderObjectWrapper
  214.         self._dynamic_attributes = []
  215.         for prop in ob.bl_rna.properties:
  216.             if not prop.is_readonly:
  217.                 self._dynamic_attributes.append( prop.identifier )
  218.  
  219.     def _helper( self, name, attr ):
  220.         setattr(self._wrapped, name, attr)
  221.         return getattr( self._wrapped, name )
  222.     def __getattr__( self, name ):
  223.         if name.startswith('GET_'):
  224.             n = name.split('GET_')[-1]
  225.             return lambda *args: getattr( self._wrapped, n )
  226.         elif name in self._dynamic_attributes:
  227.             return lambda **kw: self._helper(name,kw[name])
  228.         else: return getattr( self._wrapped, name )
  229.  
  230. if bpy: DynamicWrappers[ 'bpy_types.Object' ] = BlenderObjectWrapper
  231.  
  232.  
  233.  
  234.  
  235. def pypy_entry_point():
  236.     ## init module level proxies ##
  237.     gtk = gtk_wrapped()
  238.     #pygame = pygame_wrapped()
  239.     #pygame.display = pygame_display_wrapped()
  240.     #pygame.draw = pygame_draw_wrapped()
  241.     #ode = ode_wrapped()
  242.  
  243.     #world = ode.World()
  244.     #world.setERP( 0.8 )
  245.     #world.setCFM( 0.1 )
  246.     #world.setGravity( ( 0, -0.6*10000, 0 ) )
  247.     #body = ode.Body( world )
  248.     #body.addForce( (1,0,0) )       # not having dynamic attribute access is a good thing!?
  249.  
  250.     w = gtk.Window()
  251.     w.set_size_request( 320,200 )
  252.     w.set_title('PyPyPipe GTK test')
  253.     root = gtk.VBox(); w.add( root )
  254.     root.set_border_width(10)
  255.     lab = gtk.Label()
  256.     lab.set_text( 'hello world' )
  257.     root.pack_start( lab )
  258.     b = gtk.Button( 'click me' )
  259.     b.connect('clicked', "lambda b,l: l.set_text('clicked test')", lab )        # note lambdas are passed as strings
  260.     root.pack_start( b )
  261.     root2 = b.get_parent()
  262.     root2.pack_start( gtk.Label('return instance from CPython works') )
  263.     b = gtk.Button('exit demo')
  264.     b.connect('clicked', 'lambda b: gtk.main_quit()' )
  265.     root2.pack_start( b )
  266.     frame = gtk.Frame('PyODE body position')
  267.     root2.pack_start( frame )
  268.     bodylab = gtk.Label('body pos')
  269.     frame.add( bodylab )
  270.     w.show_all()
  271.  
  272.     w2 = gtk.Window( gtk.WINDOW_POPUP )
  273.     w2.move( 20,240 )
  274.     w2.set_size_request( 320,100 )
  275.     r2 = gtk.VBox(); w2.add( r2 )
  276.     lab2 = gtk.Label('enum values work (this is a popup window)' )
  277.     r2.pack_start( lab2 )
  278.     b2 = gtk.Button('close')
  279.     b2.connect('clicked', "lambda b,w: w.destroy()", w2)
  280.     r2.pack_start( b2 )
  281.     w2.show_all()
  282.  
  283.     #pygame.init()
  284.     #surf = pygame.display.set_mode( (320,240) )
  285.     i = 0
  286.     while True:     # does not block gtk because we have a glib timeout in the other process
  287.         #x = math.sin( radians(i) ) * 100
  288.         #y = math.cos( radians(i) ) * 100
  289.         #pygame.draw.aaline( surf, (255,255,255,255), (160,120), (int(160+x),int(120+y)) )
  290.         #pygame.display.flip()
  291.  
  292.         #world.quickStep( 0.001 )
  293.         #pos = body.getPosition()
  294.         #bodylab.set_text( str(pos) )
  295.  
  296.         i += 1
  297.         if i == 360: raise SystemExit
  298.  
  299.  
  300. def pypy_entry_point_test_simple():
  301.     os = os_wrapper()
  302.     print( os.system )
  303.  
  304.  
  305. ## Reserved Characters, can not be passed from rpython to Cpython by function call in a string
  306. ## you can change them if you need ##
  307. INSTANCE_SEP = '|'
  308. FUNCTION_SEP = '!'
  309. FUNC_ARG_SEP = '^'
  310. ARGUMENT_SEP = '~'
  311. DO_NOT_CREATE = 'DO_NOT_CREATE'
  312.  
  313.  
  314. def debug( string ):
  315.     stderr.write( '\t[debug][%s]\n' %string )
  316.     stderr.flush()
  317.  
  318. degToRad = math.pi / 180.0
  319. def radians(x):
  320.     """radians(x) -> converts angle x from degrees to radians
  321.     """
  322.     return x * degToRad
  323.  
  324. _dyn_instances = {}
  325. def format_instance( res ):
  326.     mod = res.__class__.__module__
  327.     cname = res.__class__.__name__
  328.     classpath = '%s.%s' %(mod,cname)
  329.     if classpath in DynamicWrappers:
  330.         if id(res) not in _dyn_instances:
  331.             dawrap = DynamicWrappers[ classpath ]( res )
  332.             _dyn_instances[ id(res) ] = dawrap
  333.         else: dawrap = _dyn_attr_instances[ id(res) ]
  334.         INSTANCES[ id(dawrap) ] = dawrap
  335.         classpath = dawrap._rpython_shadow_class    # rpython-side class name in GeneratedClasses
  336.         return '<%s%s%s>' %(classpath,INSTANCE_SEP, id(dawrap))
  337.  
  338.     else:
  339.         INSTANCES[ id(res) ] = res
  340.         return '<%s%s%s>' %(classpath,INSTANCE_SEP, id(res))
  341.  
  342. def get_class( path ):
  343.     if path in DynamicShadowClasses: return DynamicShadowClasses[ path ]
  344.     g = globals()
  345.     for n in path.split('.'):
  346.         if type(g) is dict: g = g[ n ]
  347.         else: g = getattr( g, n )
  348.     return g
  349.  
  350. def string2args( string ):
  351.     nargs = []
  352.     kw = {}
  353.     if DEBUG: print( 'string2args->', string )
  354.     for arg in string.split(ARGUMENT_SEP):
  355.         if arg.strip():
  356.             key = None
  357.             if '=' in arg:
  358.                 idx = arg.index('=')
  359.                 key = arg[ : idx ]
  360.                 arg = arg[ idx+1 : ]
  361.             if arg.startswith('@'): val = INSTANCES[int(arg[1:])]
  362.             else: val = eval(arg)
  363.             if type(val) is list:
  364.                 hack = []       # pypy bug
  365.                 for i in val:
  366.                     if type(i) is int and (i==0 or i==1): hack.append( bool(i) )
  367.                 if len(hack) == len(val): val = hack
  368.             nargs.append( val )
  369.             if key: kw[key] = val
  370.     if kw: return kw
  371.     else: return nargs
  372.  
  373.  
  374. VECTOR_TYPES = [ list, tuple ]  # where is bpy Vector?
  375. if bpy:
  376.     for ob in bpy.data.objects:
  377.         ## where are these bpy types?
  378.         vtype = ob.location.__class__
  379.         if vtype not in VECTOR_TYPES: VECTOR_TYPES.append( vtype )
  380.         break
  381.  
  382. def loop():
  383.     #time.sleep(0.01)
  384.     #rlist,wlist,xlist = select.select( [read], [write], [], 0.01 )    
  385.     #if rlist and wlist:    # must wait for both read and write, because read can be selected by itself, but we must also respond
  386.     rlist,wlist,xlist = select.select( [read], [], [], 0.01 )       # this is faster
  387.     if not rlist:
  388.         pass #if DEBUG: print('waiting for read')
  389.     else:
  390.         a = read.readline().decode().strip()
  391.         if DEBUG and a: print( a )
  392.         if a and a.startswith('>'):
  393.             a = a.split('>')[-1].strip()
  394.             if FUNC_ARG_SEP in a:
  395.                 classpath, args = a.split( FUNC_ARG_SEP )
  396.                 cls = get_class( classpath )
  397.                 nargs = string2args( args )     # should return args,kw
  398.                 # workaround for functions that return objects
  399.                 if DO_NOT_CREATE in nargs:
  400.                     if DEBUG: print( 'not creating instance' )
  401.                     ID = nargs[-1]; o = INSTANCES[ int(ID) ]
  402.                 else: o = cls( *nargs )
  403.             else:
  404.                 cls = get_class( a )
  405.                 try: o = cls()
  406.                 except: o = cls     # module/class-like-module
  407.  
  408.             ID = id( o )        # cpython object
  409.             INSTANCES[ ID ] = o
  410.             if DEBUG:
  411.                 print( 'cpython instance: %s    string: %s' %(o,a) )
  412.                 print( 'instance ID', ID )
  413.  
  414.             rlist,wlist,xlist = select.select( [], [write], [], 10 )    # only wait for write when func has finished
  415.             if PYTHON_VERSION == 3: write.write( bytes( str(ID), 'utf-8' ) + b'\n' )
  416.             else: write.write( '%s\n' %ID )
  417.             write.flush()
  418.  
  419.         elif a and a[0] in '$@':        #(a.startswith('@') or a.startswith('$')):
  420.             ID, b = a.split( FUNCTION_SEP )
  421.             if ID[0] == '$': ID = ID[2:]
  422.             else: ID = ID[1:]   # strip the @
  423.             ID = int(ID)
  424.             o = INSTANCES[ID]
  425.             if DEBUG: print( 'got instance', o)
  426.             fname,args = b.split(FUNC_ARG_SEP)
  427.             func = getattr( o, fname )
  428.             nargs = string2args( args )
  429.             if DEBUG: print( 'calling', func)
  430.             if type(nargs) is list:
  431.                 res = func( *nargs )        # call and check the output of the function ##
  432.             else:
  433.                 res = func( **nargs )
  434.  
  435.             if a[0] == '@':
  436.                 if type(res) in (str,bool, float, int):
  437.                     r = str( res )
  438.                 elif type(res) in VECTOR_TYPES:
  439.                     r = ''
  440.                     for item in res:
  441.                         if type(item) in (str,bool,float,int):
  442.                             r += str(item) + ARGUMENT_SEP
  443.                         elif isinstance(item,object):
  444.                             r += format_instance( item ) + ARGUMENT_SEP
  445.                 elif isinstance(res,object):        # seems true for almost any object
  446.                     r = format_instance( res )
  447.  
  448.  
  449.                 if DEBUG: print( 'piping back ->', r )
  450.                 rlist,wlist,xlist = select.select( [], [write], [], 10 )
  451.                 if PYTHON_VERSION == 3: write.write( bytes( r, 'utf-8' ) + b'\n' )
  452.                 else: write.write( '%s\n' %r )
  453.                 write.flush()
  454.             else:
  455.                 if DEBUG: print('returns nothing', fname)
  456.                 write.write(b'\n')
  457.                 write.flush()
  458.     return True
  459.  
  460.  
  461.  
  462.  
  463.  
  464. GeneratedResponses = {}
  465. GeneratedClasses = {}
  466. GeneratedRPC = {}
  467.  
  468. class RCache(object):
  469.     def __init__(self):
  470.         self.instances = {}
  471. Cache = RCache()
  472.  
  473.  
  474. class Proxy(object):        ## pypy only allows __init__ and __del__
  475.     pass
  476. def geninit():      # generating init for each class is too slow
  477.     def __init__(self, *args):
  478.         header = self._classpath_
  479.         if args:
  480.             a = ''
  481.             for arg in list(args):
  482.                 if isinstance(arg,Proxy): a += '@%s%s' %(arg._ID, ARGUMENT_SEP)
  483.                 elif isinstance( arg, str ):        # type(arg) is str # not allowed in pypy
  484.                     a += '"%s"%s' %(arg, ARGUMENT_SEP)
  485.                 else: a += '%s%s' %(arg, ARGUMENT_SEP)
  486.             pipe( '>create>%s%s%s' %(header, FUNC_ARG_SEP, a) )
  487.         else: pipe( '>create>%s' %header )
  488.  
  489.         self._ID = '0'
  490.         rl,wl,xl = rpoll.select( [0], [], [], 10.0 )    # wait 10 seconds, subprocess only needs to wait for reading
  491.         if rl:
  492.             ID = stdin.readline().strip('\n').strip(' ')
  493.             if DEBUG: debug( 'got ID from cpython %s' %ID )
  494.             self._ID = str(ID)      #int(ID)
  495.             Cache.instances[ self._ID ] = self
  496.             if DEBUG: debug('ok')
  497.         else:
  498.             pipe( 'PIPE BLOCK - can not init' )
  499.             raise SystemExit
  500.     return __init__
  501. Proxy.__init__ = geninit()
  502.  
  503.  
  504. INSTANCES = {}
  505.  
  506. CALL_GROUPS = { 'tag lambda *args': [], 'pygame.draw.line': [] }
  507. if gtk:
  508.     CALL_GROUPS[ 'tag lambda *args' ].append( gtk.Object.connect )
  509. if pygame:
  510.     CALL_GROUPS[ 'pygame.draw.line' ].append( pygame.draw.line )
  511.     CALL_GROUPS[ 'pygame.draw.line' ].append( pygame.draw.aaline )
  512.  
  513. RETURN_GROUPS = { tuple : [] }
  514. if ode:
  515.     RETURN_GROUPS[ tuple ].append(ode.Body.getPosition)
  516.  
  517. _parse_string = lambda s: str(s)
  518. _parse_string.func_name = '_parse_string'
  519. _parse_float = lambda s: float(s)
  520. _parse_float.func_name = '_parse_float'
  521. _parse_int = lambda s: int(s)
  522. _parse_int.func_name = '_parse_int'
  523. _parse_bool = lambda x: True if (x=='True') else False
  524. _parse_bool.func_name = '_parse_bool'
  525. #_parse_tuple_float = lambda s: [ float(x) for x in s.replace('(',' ').replace(')',' ').strip(' ').split(',') ]
  526. _parse_tuple_float = lambda s: [ float(x) for x in s ]
  527. _parse_tuple_float.func_name = '_parse_tuple_float'
  528. _parse_tuple_int = lambda s: [ int(x) for x in s ]
  529. _parse_tuple_int.func_name = '_parse_tuple_int'
  530. _parse_tuple_string = lambda s: [ str(x) for x in s ]
  531. _parse_tuple_string.func_name = '_parse_tuple_string'
  532. _parse_tuple_bool = lambda s: [ _parse_bool(x) for x in s ]
  533. _parse_tuple_bool.func_name = '_parse_tuple_bool'
  534. _parse_tuple_object = lambda s: [ _parse_object(x) for x in s ]
  535. _parse_tuple_object.func_name = '_parse_tuple_object'
  536.  
  537.  
  538. def _parse_object( s ):
  539.     s = s.replace('<',' ').replace('>',' ').strip(' ')
  540.     a,ID = s.split( INSTANCE_SEP )      # pypy only splits on a single char
  541.     if ID in Cache.instances:
  542.         if DEBUG: debug( 'proxy already exists' )
  543.         return Cache.instances[ID]
  544.     else:
  545.         if DEBUG:
  546.             debug( 'creating new rproxy' )
  547.             debug( a )
  548.         cls = GeneratedClasses[ a ]
  549.         return cls( DO_NOT_CREATE, ID )
  550.  
  551. #long = int     #py3 hack
  552. #unicode = str
  553. RParseMapping = {
  554.     object : _parse_object,
  555.     str : _parse_string,
  556.     float : _parse_float,
  557.     int : _parse_int,
  558.     bool : _parse_bool,
  559.     #list : _parse_tuple_float,     # check create_dupli_list
  560.     (float,): _parse_tuple_float,
  561.     (int,): _parse_tuple_int,
  562.     (str,): _parse_tuple_string,
  563.     (object,): _parse_tuple_object,
  564.     (bool,): _parse_tuple_bool,
  565. }
  566. if PYTHON_VERSION == 2:
  567.     RParseMapping[ basestring ] = _parse_string
  568.     RParseMapping[ (basestring,) ] = _parse_tuple_string
  569.     RParseMapping[long] = _parse_int
  570.     RParseMapping[(long,)] = _parse_tuple_int
  571.  
  572. def pipe( string ): # the types of *args can not change in pypy, convert to strings first
  573.     if DEBUG: debug('pipe start: %s'  %string)
  574.     rl,wl,xl = rpoll.select( [], [1], [], 0 )
  575.     if wl:
  576.         stdout.write( '%s\n' %string )
  577.         stdout.flush()
  578.         if DEBUG: debug('pipe end: %s' %string)
  579.  
  580. def _closure_tuple( res ):
  581.     pipe( res )
  582.     if DEBUG: debug( 'waiting for function response (tuple)' )
  583.     rl,wl,xl = rpoll.select( [0], [], [], 10 )
  584.     if rl:
  585.         if DEBUG: debug( 'readline ready' )
  586.         s = string = stdin.readline().strip('\n').strip(' ')
  587.         if DEBUG: debug( 'got function response: %s' %string )
  588.         r = []
  589.         for a in s.split(ARGUMENT_SEP):
  590.             if a: r.append( a )     # was the problem here?
  591.         return r
  592.     else:
  593.         debug( 'PIPE BLOCK - closure tuple failed' )
  594.         raise SystemExit
  595.  
  596. def _closure( res ):
  597.     pipe( res )
  598.     if DEBUG: debug( 'waiting for function response' )
  599.     rl,wl,xl = rpoll.select( [0], [], [], 10 )  # wait 10 seconds, subprocess only needs to wait for reading
  600.     if rl:
  601.         if DEBUG: debug( 'readline ready' )
  602.         s = string = stdin.readline().strip('\n').strip(' ')
  603.         if DEBUG: debug( 'got function response: %s' %string )
  604.         return s
  605.     else:
  606.         debug( 'PIPE BLOCK - closure failed' )
  607.         raise SystemExit
  608.  
  609. # how to optimize this away? parent process mainloop expects to read only a single line per loop,
  610. # but if we dont block we can send multiple commands to parent iteration, who may wait for reading,
  611. # but then the server is waiting for reading, so we have a dead lock.
  612. # ( not a big optimize since most useful functions return something )
  613. def _closure_none(res):
  614.     pipe( '$'+res )
  615.     rl,wl,xl = rpoll.select( [0], [], [], 30 )
  616.     r = stdin.readline()
  617.     #if DEBUG: debug(r)
  618.  
  619. def gen_func_response( rtype=object ):
  620.     if rtype is None:   # NO_RETURN
  621.         #get_function_response = lambda res: pipe( '$'+res )        # this would be faster, but only for functions returning nothing - not so useful?
  622.         get_function_response = _closure_none
  623.     elif type(rtype) is list:
  624.         head = 'lambda '
  625.         body = ': ['
  626.         for i,T in enumerate(rtype):
  627.             head += 'arg%s,' %i
  628.             body += '%s(arg%s),' %(RParseMapping[T].func_name, i)
  629.         gen = head + body + ']'
  630.         get_function_response = eval('lambda res: (%s)(*_closure(res))' %gen)
  631.  
  632.     else:
  633.         closure = RParseMapping[rtype]
  634.         if type(rtype) is tuple:
  635.             get_function_response = eval('lambda res: (%s)(_closure_tuple(res))' %closure.func_name)
  636.         else:
  637.             get_function_response = eval('lambda res: (%s)(_closure(res))' %closure.func_name)
  638.  
  639.     return get_function_response
  640.  
  641. class WrapFunction(object):
  642.     def __init__(self, fname, func, parent ):
  643.         self._func_name = fname
  644.         self._argument_info = None
  645.         self._return_info = object
  646.  
  647.         ## manual wrapping ##
  648.         self._call_group = None
  649.         self._return_group = None
  650.         for group in CALL_GROUPS:
  651.             if func in CALL_GROUPS[group]:
  652.                 self._call_group = group
  653.                 break
  654.         for group in RETURN_GROUPS:
  655.             if func in RETURN_GROUPS[group]:
  656.                 self._return_group = group
  657.                 break
  658.  
  659.         ## reflection/inspect RNA wrapping ##
  660.         if isinstance(func,object) and func.__class__.__name__ == 'bpy_ops_submodule_op':
  661.             print( func )
  662.             #classpath = '%s_%s.%s' %(cls.__class__.__module__, cls.module, cls.func)
  663.             rna = func.get_rna()
  664.             args = []
  665.             for prop in rna.bl_rna.properties:
  666.                 if prop.identifier == 'rna_type': continue
  667.                 args.append( reflect_RNA_prop( prop ) )
  668.             print( args )
  669.             self._argument_info = args
  670.             self._return_info = None
  671.  
  672.         if hasattr( func, '_argument_info' ):
  673.             self._argument_info = func._argument_info
  674.  
  675.         if hasattr( func, '_return_info' ):
  676.             self._return_info = func._return_info
  677.  
  678.         if parent.__class__.__name__ == 'bpy_prop_collection':
  679.             if fname == 'keys': self._return_info = (str,)
  680.             elif fname == 'get': self._return_info = object
  681.             elif fname == 'values': self._return_info = (object,)
  682.  
  683.     def unpack( self ):     ## interesting pypy needs generated functions so that *args can work properly
  684.         fname = self._func_name
  685.         index = len( GeneratedRPC )
  686.         gupdate = {}
  687.  
  688.         genres_name = '_generated_RPC_closure%s'%index
  689.         if self._return_group:
  690.             gupdate[ genres_name ] = genres_func = gen_func_response( self._return_group )
  691.         else:
  692.             rinfo = self._return_info
  693.             debug( 'unpacking function %s' %fname )
  694.             debug( str(rinfo) )
  695.             if type(rinfo) is dict:
  696.                 if 'array-length' in rinfo and rinfo['array-length']:
  697.                     if rinfo['type'] is long: rinfo = (int,)
  698.                     else: rinfo = ( rinfo['type'], ) #* rinfo['array-length']
  699.                 else:
  700.                     if rinfo['type'] is long: rinfo = int
  701.                     else: rinfo = rinfo['type']
  702.             elif type(rinfo) is list:
  703.                 r = []
  704.                 for a in rinfo:
  705.                     if 'array-length' in a and a['array-length']:
  706.                         if a['type'] is long: a = (int,)
  707.                         else: a = ( a['type'], ) #* a['array-length']
  708.                     else:
  709.                         if a['type'] is long: a = int
  710.                         else: a = a['type']
  711.                     r.append( a )
  712.                 rinfo = r
  713.  
  714.             if rinfo is long: rinfo = int       # temp py3/2 problem
  715.             elif type(rinfo) is tuple and rinfo[0] is long: rinfo = (int,)
  716.             gupdate[ genres_name ] = genres_func = gen_func_response( rinfo )       # defaults to object
  717.  
  718.         GeneratedResponses[ genres_name ] = genres_func
  719.         genres_func.func_name = genres_name
  720.  
  721.         rpc_name = '_generated_RPC%s'%index
  722.         if self._argument_info is not None:
  723.             e = 'lambda self,'
  724.             for arg in self._argument_info:
  725.                 debug( arg)
  726.                 if 'default' in arg:
  727.                     if arg['type'] is basestring: e += '%s="%s",' %(arg['name'],arg['default'])
  728.                     else: e += '%s=%s,' %(arg['name'],arg['default'])
  729.                 else:
  730.                     if arg['type'] is basestring: e += '%s="",' %arg['name']
  731.                     if arg['type'] in (int,float,bool,dict,list):  e += '%s=%s,' %(arg['name'], arg['type']() )
  732.                     else: e += '%s=None,' %arg['name']
  733.             e += ':  %s( "@" + str(self._ID) +  "%s%s%s"  ' %(genres_name,FUNCTION_SEP,fname,FUNC_ARG_SEP)
  734.             for arg in self._argument_info:
  735.                 if arg['type'] is basestring:
  736.                     #e += '''+'"%s"%s' %''' + '(%s, ARGUMENT_SEP) ' %arg['name']
  737.                     e += ''' + '%s="'+%s+'"%s' ''' %(arg['name'], arg['name'], ARGUMENT_SEP)
  738.                 elif arg['type'] is object:
  739.                     e += '+ "%s=@" + str(%s._ID) + "%s" ' %(arg['name'],arg['name'], ARGUMENT_SEP)
  740.                 else:
  741.                     e += '+ "%s=" + str(%s) + "%s" ' %(arg['name'],arg['name'],ARGUMENT_SEP)
  742.             e += ' ) '
  743.             debug( e)
  744.             wrap = rpc = eval( e )
  745.             #if fname == 'primitive_plane_add': raise
  746.         elif self._call_group == 'tag lambda *args':
  747.             def rpc( self, name, tag, callback, *args ):
  748.                 if DEBUG: debug( '-rpc %s %s' %(self, name) )
  749.                 a = '"%s"%s' %(tag, ARGUMENT_SEP)
  750.                 a += '%s%s' %(callback, ARGUMENT_SEP)
  751.                 for arg in list(args):
  752.                     if isinstance(arg,Proxy): a += '@%s%s' %(arg._ID, ARGUMENT_SEP)
  753.                     elif isinstance( arg, str ):        # type(arg) is str # not allowed in pypy
  754.                         a += '"%s"%s' %(arg, ARGUMENT_SEP)
  755.                     else: a += '%s%s' %(arg, ARGUMENT_SEP)
  756.                 return '@%s%s%s%s%s' %(self._ID, FUNCTION_SEP, name, FUNC_ARG_SEP, a)
  757.             wrap = eval( 'lambda self,tag,cb,*args: %s(%s(self,"%s",tag,cb,*args))' %(genres_name, rpc_name, fname) )
  758.  
  759.         elif self._call_group == 'pygame.draw.line':
  760.             def rpc( self, name, surf, color, start, end, width=1 ):
  761.                 if DEBUG: debug( '-rpc %s %s' %(self, name) )
  762.                 a = '@%s%s' %(surf._ID,ARGUMENT_SEP)
  763.                 a += '%s%s' %(color,ARGUMENT_SEP)
  764.                 a += '%s%s' %(start,ARGUMENT_SEP)
  765.                 a += '%s%s' %(end,ARGUMENT_SEP)
  766.                 a += '%s%s' %(width,ARGUMENT_SEP)
  767.                 return '@%s%s%s%s%s' %(self._ID, FUNCTION_SEP, name, FUNC_ARG_SEP, a)
  768.             wrap = eval( 'lambda self,surf,color,start,end,width=1: %s(%s(self,"%s",surf,color,start,end,width))' %(genres_name, rpc_name, fname) )
  769.  
  770.         else:
  771.             def rpc( self, name, *args ):
  772.                 if DEBUG: debug( '-rpc %s %s' %(self, name) )
  773.                 a = ''
  774.                 for arg in list(args):
  775.                     if isinstance(arg,Proxy): a += '@%s%s' %(arg._ID, ARGUMENT_SEP)
  776.                     elif isinstance( arg, str ):        # type(arg) is str # not allowed in pypy
  777.                         a += '"%s"%s' %(arg, ARGUMENT_SEP)
  778.                     else: a += '%s%s' %(arg, ARGUMENT_SEP)
  779.                 return '@%s%s%s%s%s' %(self._ID, FUNCTION_SEP, name, FUNC_ARG_SEP, a)
  780.             wrap = eval( 'lambda self,*args: %s(%s(self,"%s",*args))' %(genres_name, rpc_name, fname) )
  781.  
  782.         rpc.func_name = rpc_name
  783.         GeneratedRPC[ rpc_name ] = rpc
  784.         gupdate[ rpc_name ] = rpc
  785.         globals().update( gupdate )
  786.         return wrap
  787.  
  788.  
  789. ## these are considered constants ##
  790. WrappableAttributesTypes = (
  791.     bool,
  792.     int,
  793.     #long,
  794.     float,
  795.     str,
  796.     #unicode,
  797. )
  798. INIT_GROUPS = { int:[], object:[] }
  799. if gtk:
  800.     INIT_GROUPS[ int ].append( gtk.Window )
  801. if ode:
  802.     INIT_GROUPS[ object ].append( ode.Body )
  803.  
  804. INIT_GROUPS_gen = {}
  805. for tag in INIT_GROUPS: INIT_GROUPS_gen[ tag ] = geninit()
  806. del tag
  807.  
  808. class WrapClass(object):
  809.     def __init__( self, cls, classpath, items={} ):
  810.         self._classpath = classpath
  811.         self._items = items
  812.         self._init_type = None
  813.         for group in INIT_GROUPS:
  814.             if cls in INIT_GROUPS[group]:
  815.                 self._init_type = group
  816.  
  817.     def unpack( self ):
  818.         classitems = {}
  819.         for key in self._items:
  820.             #debug( key )
  821.             v = self._items[key]
  822.             if isinstance( v, (WrapClass,WrapFunction) ): v = v.unpack()
  823.             classitems[ key ] = v
  824.  
  825.         if self._init_type in INIT_GROUPS_gen:
  826.             classitems['__init__'] = INIT_GROUPS_gen[ self._init_type ] # classes that have the same init vars
  827.  
  828.         classitems['_classpath_'] = str( self._classpath )
  829.         debug( 'generating -> %s' %self._classpath )
  830.         classname = self._classpath.split('.')[-1]
  831.         metaclass = types.ClassType(str(classname), bases=(Proxy,), dict=classitems)
  832.         GeneratedClasses[ str( self._classpath ) ] = metaclass
  833.         return metaclass
  834.  
  835. CallableClasses = 'bpy_ops_submodule_op'.split()
  836.  
  837. def wrap_class( cls, name, classes=False ):
  838.     print( 'wrapping', cls )
  839.     bpy_prop_collection = False
  840.     if bpy and isinstance( cls, bpy.types.Main ):
  841.         mname = 'bpy'
  842.         #if cls.__class__.__name__ == 'Main':
  843.         #   mname = 'bpy'
  844.         #else:
  845.         #   mname = 'bpy.data'
  846.     #elif cls.__class__.__name__ == 'RNA_Types':
  847.     #   mname = None
  848.     elif cls.__class__.__name__ == 'bpy_prop_collection': mname = 'bpy.data'; bpy_prop_collection = True
  849.     #   mname = 'bpy'
  850.     #   mname = 'data'
  851.     #elif cls.__class__.__name__ == 'bpy_ops':
  852.     #   mname = None
  853.     #elif cls.__class__.__name__ == 'bpy_ops_submodule':
  854.     #   #name = cls.module      # name of submod "mesh" etc..
  855.     #   mname = 'ops'
  856.     #else:
  857.     #   #name = cls.__name__
  858.     elif inspect.ismodule( cls ): mname = ''
  859.     else: mname = cls.__module__
  860.     if mname in ('__main__','pypypipe'): mname = ''
  861.     if mname: classpath = '%s.%s' %(mname,name)
  862.     else: classpath = name
  863.     d = {}
  864.  
  865.     for n in dir(cls):
  866.         if n.startswith('_'): continue
  867.         a = getattr( cls, n )
  868.         if inspect.isroutine( a ) or (isinstance(a,object) and a.__class__.__name__ in CallableClasses):
  869.             d[n] = WrapFunction(n,a, cls)
  870.         elif inspect.isclass( a ) and classes:
  871.             #if classes: print '\tCLASS: ', n
  872.             if a.__module__ == 'gtk._gtk': continue
  873.             else: d[ n ] = wrap_class( a,n )
  874.         elif isinstance(a,object) and a.__class__.__name__ in 'bpy_ops_submodule bpy_prop_collection'.split():
  875.             d[ n ] = wrap_class( a, n )
  876.         elif type(a) in WrappableAttributesTypes:
  877.             d[ n ] = a
  878.         elif type(a) in (list,tuple):
  879.             safe = True
  880.             for item in a:
  881.                 if type(item) not in WrappableAttributesTypes:
  882.                     safe = False; break
  883.             if safe:
  884.                 d[ n ] = a
  885.         else:
  886.             ## check for gtk enum
  887.             items = dir(a)
  888.             enum = True
  889.             for test in 'conjugate denominator imag numerator real'.split():
  890.                 if test not in items: enum = False; break
  891.             if enum:
  892.                 d[ n ] = a.real
  893.  
  894.     wrapped = WrapClass( cls, classpath, items=d )
  895.     return wrapped
  896.  
  897.  
  898. def setup_blender_loop():
  899.     bpy.ops.screen.animation_play('EXEC_DEFAULT')
  900.     def draw_callback( area, region ): loop()
  901.     for area in bpy.context.window.screen.areas:
  902.         print(area.type)
  903.         if area.type == 'VIEW_3D':
  904.             for reg in area.regions:
  905.                 print( '\tregion: ', reg.type )
  906.                 if reg.type == 'WINDOW':
  907.                     print( 'adding callback' )
  908.                     reg.callback_add(draw_callback, (area,reg), 'POST_PIXEL')   # sneaky! unlisted function!!!
  909.  
  910. def dump():
  911.     ## save wrap ##
  912.     D = {}
  913.     if gtk:
  914.         w = wrap_class( gtk, 'gtk', classes=True )
  915.         D['gtk_wrapped'] = w
  916.         #D['pygame_wrapped'] = wrap_class( pygame, classes=True )
  917.         #D['pygame_display_wrapped'] = wrap_class( pygame.display, classes=True )
  918.         #D['pygame_draw_wrapped'] = wrap_class( pygame.draw, classes=True )
  919.         D['ode_wrapped'] = wrap_class( ode, 'ode', classes=True )
  920.     elif bpy:
  921.         bobject = bpy.data.objects.new("_unlinked_", bpy.data.meshes.new("_dummy_") )
  922.         genclass = reflect_RNA_instance( bobject )
  923.         D[ genclass.__name__ ] = wrap_class( genclass, genclass.__name__ )
  924.  
  925.         bobject = bpy.data.objects.new("_unlinked_", bpy.data.cameras.new("_dummy_") )
  926.         genclass = reflect_RNA_instance( bobject )
  927.         D[ genclass.__name__ ] = wrap_class( genclass, genclass.__name__ )
  928.  
  929.         bobject = bpy.data.objects.new("_unlinked_", bpy.data.lamps.new("_dummy_") )
  930.         genclass = reflect_RNA_instance( bobject )
  931.         D[ genclass.__name__ ] = wrap_class( genclass, genclass.__name__ )
  932.  
  933.  
  934.         D['bpy_ops_wrapped'] = wrap_class( bpy.ops, 'ops', classes=True )
  935.         D['bpy_data_wrapped'] = wrap_class( bpy.data, 'data', classes=True )
  936.         #D['bpy_types_wrapped'] = wrap_class( bpy.types, 'types', classes=True )
  937.  
  938.     else:
  939.         D['os_wrapped'] = wrap_class( os, 'os', classes=True )
  940.  
  941.     f = open('/tmp/genwrapper.pickle','wb')
  942.     pickle.dump( D, f, protocol=2)      #,fix_imports=False )
  943.     f.close()
  944.     print( 'genwrapper.pickle dumped' )
  945.  
  946. if __name__ == '__main__' and '--pypy' in sys.argv:
  947.     dump = pickle.load( open('/tmp/genwrapper.pickle','rb') )
  948.     D = globals()
  949.     for key in dump:
  950.         debug( key )
  951.         metaclass = dump[key].unpack()
  952.         debug( metaclass )
  953.         D[ key ] = metaclass
  954.         #why does this fail?#GeneratedClasses[ key ] = metaclass
  955.     for k in globals().keys():
  956.         if not k.startswith('_'):
  957.             debug('global %s' %k )
  958.  
  959.     ## uncomment this if you do not want pypy translation to C
  960.     #pypy_entry_point_test_bpy()
  961.     #raise
  962.  
  963.     if '--bpy' in sys.argv: t = Translation( pypy_entry_point_test_bpy )
  964.     elif '--gtk' in sys.argv: t = Translation( pypy_entry_point )
  965.     else: t = Translation( pypy_entry_point_simple )
  966.     t.annotate(); t.rtype()
  967.     f = t.compile_c()
  968.     f()
  969.     print( 'subprocess exit' )
  970.  
  971. elif __name__ == '__main__':
  972.     import pypypipe     # blender253 pickle bug
  973.     pypypipe.dump()
  974.     test = ''
  975.     if bpy: test = '--bpy'
  976.     elif gtk: test = '--gtk'
  977.     process = subprocess.Popen( 'python pypypipe.py --pypy %s' %test, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0, shell=True )
  978.     write = process.stdin
  979.     read = process.stdout
  980.     print( 'read pipe', read )
  981.     print( 'read write', write )
  982.     if glib:
  983.         glib.timeout_add( 33, loop )
  984.         gtk.main()
  985.     elif bpy: setup_blender_loop()
  986.     print( 'toplevel exit' )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement