Guest User

PyPyPipe for Blender2.5

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