Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- '''
- PyPyPipeProxy v0.5
- Brett Hartshorn 2010 - License BSD
- goatman.py@gmail.com
- Use CPython as a host for external libs, tested with PyGTK, PyODE, Pygame and Blender2.5
- Testing:
- blender25/blender -P pypypipe.py
- (you should see the cube and camera moving in a circle, along with planes being created and the camera scaled)
- Blender2.5:
- In Progress:
- bpy.ops
- bpy.data
- Differences:
- modules are not nested, instead of bpy.data.objects, use bpy_data_objects
- returned objects do not have dynamic attribute support, the work around is to use the GET_<attrname> methods,
- and to set call the attribute as a function, example:
- scale = ob.GET_scale()
- scale[0] = 10.0
- ob.scale( scale )
- objects that act like dicts will not work, but bpy already provides functions for getting from dict like objects (collections)
- # this will not work
- ob = bpy_data_objects['Cube']
- # this will work
- ob = bpy_data_objects.get('Cube')
- see the pypy_entry_point_test_bpy function below for what is working
- Notes:
- 1. no keyword arguments can be used from RPython, unless you define a custom wrapper.
- 2. only simple lambda callbacks are working (these operate in CPython space)
- 3. you do not need to use pypy translation, instead just call your entry point function,
- but you still need the pypy source code and set the path to it because rpoll is used as the pipe selector.
- Getting Started:
- make sure this file is named pypypipe.py
- copy this file to your pypy trunk folder, or add the path to pypy below PATH2PYPY
- optional: make sure you have pygame, pygtk, and pyode (currently broken, only blender25 is working)
- Hacking:
- Dynamic attribute access is not possible, but function calls are ok.
- This is wrapper incomplete, any class with unique init args, or functions with unique
- args must be hand wrapped. Even the return type may need to be defined.
- For Blender proper arg and return types are generated by inspecting RNA, so its safe to use keyword args.
- pypy hacking notes:
- s = s[1:len(s)-1] # pypy.rpython.error.TyperError: slice stop must be proved non-negative
- s = s.replace('<','').replace('>','') # pypy.rpython.error.TyperError: replace only works for char args
- long(string, 16) not allowed
- globals() not allowed
- function return types can not be mixed (required generated return functions per incompatible types)
- '''
- def pypy_entry_point_test_bpy():
- start = time.time()
- ## init wrapper ##
- bpy_ops = bpy_ops_wrapped()
- bpy_ops_mesh = bpy_ops.mesh()
- bpy_data = bpy_data_wrapped()
- bpy_data_objects = bpy_data.objects()
- ######################
- objs = bpy_data_objects.values()
- debug('----------------')
- for ob in objs:
- debug(str(ob))
- debug( str( ob.location()) )
- names = bpy_data_objects.keys()
- camera = None
- for n in names:
- debug( n )
- ob = bpy_data_objects.get( n )
- debug( str(ob) )
- if n == 'Camera':
- camera = ob
- debug('found the camera')
- #camera.select( True ) # broken, why only 1 arg taken?
- scale = camera.GET_scale()
- debug( 'camera scale: %s' %scale )
- scale[0] = 10.0
- camera.scale( scale )
- break
- i = 0
- while True:
- x = math.sin( radians(i) ) * 1.0
- y = math.cos( radians(i) ) * 1.0
- bpy_ops_mesh.primitive_plane_add( location=(x,y,.0) )
- for ob in objs: ob.location( [x,y,.0] )
- i += 1
- if i == 360: break
- debug( 'run time: %s' %(time.time()-start) )
- debug('bpy ops test complete')
- ## Set your PyPy root ##
- PATH2PYPY = 'pypy'
- import os, sys, time, inspect, types, select, subprocess, math, pickle
- if '.' not in sys.path: sys.path.append( '.' ) # blender253 pickle bug
- sys.path.append(PATH2PYPY) # assumes you have pypy dist in a subfolder, you may need to rename this pypy-trunk
- PYTHON_VERSION = sys.version_info[0]
- DEBUG = True
- if '--pypy' in sys.argv:
- from pypy.rlib import streamio
- from pypy.rlib import rpoll
- from pypy.translator.interactive import Translation
- stdin = streamio.fdopen_as_stream(0, 'r', 0) # fd, mode, buffering
- stdout = streamio.fdopen_as_stream(1, 'w', 0)
- stderr = streamio.fdopen_as_stream(2, 'w', 0)
- bpy = glib = gtk = ode = pygame = None
- try:
- import glib, gtk, pygame, ode
- except:
- try:
- import bpy
- ops = bpy.ops
- data = bpy.data
- types = bpy.types
- except: pass
- DynamicWrappers = {}
- DynamicShadowClasses = {}
- RNA_PROP_MAPPING = {
- 'POINTER': object,
- 'FLOAT': float,
- 'BOOLEAN': bool,
- 'INT': int,
- 'ENUM': str,
- 'STRING':str,
- '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
- }
- def reflect_RNA_prop( prop ):
- #print( dir(prop))
- #try: print( prop.default )
- #except: pass
- try:rtype = RNA_PROP_MAPPING[ prop.type ]
- except: print( dir(prop) ); print(prop.fixed_type); raise
- a = {
- 'name':prop.identifier, #name,
- 'type': rtype,
- #'default':prop.default,
- }
- if rtype is object:
- a['module'] = prop.fixed_type.__class__.__module__
- a['class'] = prop.fixed_type.__class__.__name__
- if hasattr( prop, 'default' ):
- #if not prop.use_output:
- a['default'] = prop.default
- #if rtype is bool: a['default'] = 'bool'
- #print( dir(prop) ) #array_length', 'bl_rna', 'default', 'default_array
- if hasattr( prop, 'array_length' ):
- a['array-length'] = prop.array_length
- if prop.array_length:
- a['default'] = [ prop.default ] * prop.array_length
- if rtype is bool: a['default'][0] = True # mini hack
- #for x in prop.default_array: print(x)
- #print( a)
- return a
- def reflect_RNA_instance( ob ):
- class genRNA(object): type = ob.type
- shadow_name = 'RBlender_%s' %ob.type
- genRNA.__name__ = shadow_name
- globals()[ shadow_name ] = genRNA
- for prop in ob.bl_rna.properties:
- if not prop.is_readonly:
- print(prop)
- dummy = lambda:()
- dummy._argument_info = ai = [ reflect_RNA_prop( prop ) ]
- dummy._return_info = ri = reflect_RNA_prop( prop )
- setattr( genRNA, prop.identifier, dummy )
- #if prop.identifier == 'location':
- # print(ai)
- # print( ri )
- # raise
- dummy = lambda:()
- dummy._return_info = ri = reflect_RNA_prop( prop )
- setattr( genRNA, 'GET_%s' %prop.identifier, dummy )
- for bfunc in ob.bl_rna.functions:
- args = []
- returns = []
- for prop in bfunc.parameters:
- if prop.use_output: returns.append( reflect_RNA_prop( prop ) )
- else: args.append( reflect_RNA_prop( prop ) )
- if len(returns) == 1: returns = returns[0]
- elif not returns: returns = None
- dummy = lambda:()
- dummy._argument_info = args
- dummy._return_info = returns
- setattr( genRNA, bfunc.identifier, dummy )
- return genRNA
- class BlenderObjectWrapper(object):
- def __init__(self, ob ):
- self._wrapped = ob
- self._rpython_shadow_class = 'RBlender_%s' %ob.type
- DynamicShadowClasses[ self._rpython_shadow_class ] = BlenderObjectWrapper
- self._dynamic_attributes = []
- for prop in ob.bl_rna.properties:
- if not prop.is_readonly:
- self._dynamic_attributes.append( prop.identifier )
- def _helper( self, name, attr ):
- setattr(self._wrapped, name, attr)
- return getattr( self._wrapped, name )
- def __getattr__( self, name ):
- if name.startswith('GET_'):
- n = name.split('GET_')[-1]
- return lambda *args: getattr( self._wrapped, n )
- elif name in self._dynamic_attributes:
- return lambda **kw: self._helper(name,kw[name])
- else: return getattr( self._wrapped, name )
- if bpy: DynamicWrappers[ 'bpy_types.Object' ] = BlenderObjectWrapper
- def pypy_entry_point():
- ## init module level proxies ##
- gtk = gtk_wrapped()
- #pygame = pygame_wrapped()
- #pygame.display = pygame_display_wrapped()
- #pygame.draw = pygame_draw_wrapped()
- #ode = ode_wrapped()
- #world = ode.World()
- #world.setERP( 0.8 )
- #world.setCFM( 0.1 )
- #world.setGravity( ( 0, -0.6*10000, 0 ) )
- #body = ode.Body( world )
- #body.addForce( (1,0,0) ) # not having dynamic attribute access is a good thing!?
- w = gtk.Window()
- w.set_size_request( 320,200 )
- w.set_title('PyPyPipe GTK test')
- root = gtk.VBox(); w.add( root )
- root.set_border_width(10)
- lab = gtk.Label()
- lab.set_text( 'hello world' )
- root.pack_start( lab )
- b = gtk.Button( 'click me' )
- b.connect('clicked', "lambda b,l: l.set_text('clicked test')", lab ) # note lambdas are passed as strings
- root.pack_start( b )
- root2 = b.get_parent()
- root2.pack_start( gtk.Label('return instance from CPython works') )
- b = gtk.Button('exit demo')
- b.connect('clicked', 'lambda b: gtk.main_quit()' )
- root2.pack_start( b )
- frame = gtk.Frame('PyODE body position')
- root2.pack_start( frame )
- bodylab = gtk.Label('body pos')
- frame.add( bodylab )
- w.show_all()
- w2 = gtk.Window( gtk.WINDOW_POPUP )
- w2.move( 20,240 )
- w2.set_size_request( 320,100 )
- r2 = gtk.VBox(); w2.add( r2 )
- lab2 = gtk.Label('enum values work (this is a popup window)' )
- r2.pack_start( lab2 )
- b2 = gtk.Button('close')
- b2.connect('clicked', "lambda b,w: w.destroy()", w2)
- r2.pack_start( b2 )
- w2.show_all()
- #pygame.init()
- #surf = pygame.display.set_mode( (320,240) )
- i = 0
- while True: # does not block gtk because we have a glib timeout in the other process
- #x = math.sin( radians(i) ) * 100
- #y = math.cos( radians(i) ) * 100
- #pygame.draw.aaline( surf, (255,255,255,255), (160,120), (int(160+x),int(120+y)) )
- #pygame.display.flip()
- #world.quickStep( 0.001 )
- #pos = body.getPosition()
- #bodylab.set_text( str(pos) )
- i += 1
- if i == 360: raise SystemExit
- def pypy_entry_point_test_simple():
- os = os_wrapper()
- print( os.system )
- ## Reserved Characters, can not be passed from rpython to Cpython by function call in a string
- ## you can change them if you need ##
- INSTANCE_SEP = '|'
- FUNCTION_SEP = '!'
- FUNC_ARG_SEP = '^'
- ARGUMENT_SEP = '~'
- DO_NOT_CREATE = 'DO_NOT_CREATE'
- def debug( string ):
- stderr.write( '\t[debug][%s]\n' %string )
- stderr.flush()
- degToRad = math.pi / 180.0
- def radians(x):
- """radians(x) -> converts angle x from degrees to radians
- """
- return x * degToRad
- _dyn_instances = {}
- def format_instance( res ):
- mod = res.__class__.__module__
- cname = res.__class__.__name__
- classpath = '%s.%s' %(mod,cname)
- if classpath in DynamicWrappers:
- if id(res) not in _dyn_instances:
- dawrap = DynamicWrappers[ classpath ]( res )
- _dyn_instances[ id(res) ] = dawrap
- else: dawrap = _dyn_attr_instances[ id(res) ]
- INSTANCES[ id(dawrap) ] = dawrap
- classpath = dawrap._rpython_shadow_class # rpython-side class name in GeneratedClasses
- return '<%s%s%s>' %(classpath,INSTANCE_SEP, id(dawrap))
- else:
- INSTANCES[ id(res) ] = res
- return '<%s%s%s>' %(classpath,INSTANCE_SEP, id(res))
- def get_class( path ):
- if path in DynamicShadowClasses: return DynamicShadowClasses[ path ]
- g = globals()
- for n in path.split('.'):
- if type(g) is dict: g = g[ n ]
- else: g = getattr( g, n )
- return g
- def string2args( string ):
- nargs = []
- kw = {}
- if DEBUG: print( 'string2args->', string )
- for arg in string.split(ARGUMENT_SEP):
- if arg.strip():
- key = None
- if '=' in arg:
- idx = arg.index('=')
- key = arg[ : idx ]
- arg = arg[ idx+1 : ]
- if arg.startswith('@'): val = INSTANCES[int(arg[1:])]
- else: val = eval(arg)
- if type(val) is list:
- hack = [] # pypy bug
- for i in val:
- if type(i) is int and (i==0 or i==1): hack.append( bool(i) )
- if len(hack) == len(val): val = hack
- nargs.append( val )
- if key: kw[key] = val
- if kw: return kw
- else: return nargs
- VECTOR_TYPES = [ list, tuple ] # where is bpy Vector?
- if bpy:
- for ob in bpy.data.objects:
- ## where are these bpy types?
- vtype = ob.location.__class__
- if vtype not in VECTOR_TYPES: VECTOR_TYPES.append( vtype )
- break
- def loop():
- #time.sleep(0.01)
- #rlist,wlist,xlist = select.select( [read], [write], [], 0.01 )
- #if rlist and wlist: # must wait for both read and write, because read can be selected by itself, but we must also respond
- rlist,wlist,xlist = select.select( [read], [], [], 0.01 ) # this is faster
- if not rlist:
- pass #if DEBUG: print('waiting for read')
- else:
- a = read.readline().decode().strip()
- if DEBUG and a: print( a )
- if a and a.startswith('>'):
- a = a.split('>')[-1].strip()
- if FUNC_ARG_SEP in a:
- classpath, args = a.split( FUNC_ARG_SEP )
- cls = get_class( classpath )
- nargs = string2args( args ) # should return args,kw
- # workaround for functions that return objects
- if DO_NOT_CREATE in nargs:
- if DEBUG: print( 'not creating instance' )
- ID = nargs[-1]; o = INSTANCES[ int(ID) ]
- else: o = cls( *nargs )
- else:
- cls = get_class( a )
- try: o = cls()
- except: o = cls # module/class-like-module
- ID = id( o ) # cpython object
- INSTANCES[ ID ] = o
- if DEBUG:
- print( 'cpython instance: %s string: %s' %(o,a) )
- print( 'instance ID', ID )
- rlist,wlist,xlist = select.select( [], [write], [], 10 ) # only wait for write when func has finished
- if PYTHON_VERSION == 3: write.write( bytes( str(ID), 'utf-8' ) + b'\n' )
- else: write.write( '%s\n' %ID )
- write.flush()
- elif a and a[0] in '$@': #(a.startswith('@') or a.startswith('$')):
- ID, b = a.split( FUNCTION_SEP )
- if ID[0] == '$': ID = ID[2:]
- else: ID = ID[1:] # strip the @
- ID = int(ID)
- o = INSTANCES[ID]
- if DEBUG: print( 'got instance', o)
- fname,args = b.split(FUNC_ARG_SEP)
- func = getattr( o, fname )
- nargs = string2args( args )
- if DEBUG: print( 'calling', func)
- if type(nargs) is list:
- res = func( *nargs ) # call and check the output of the function ##
- else:
- res = func( **nargs )
- if a[0] == '@':
- if type(res) in (str,bool, float, int):
- r = str( res )
- elif type(res) in VECTOR_TYPES:
- r = ''
- for item in res:
- if type(item) in (str,bool,float,int):
- r += str(item) + ARGUMENT_SEP
- elif isinstance(item,object):
- r += format_instance( item ) + ARGUMENT_SEP
- elif isinstance(res,object): # seems true for almost any object
- r = format_instance( res )
- if DEBUG: print( 'piping back ->', r )
- rlist,wlist,xlist = select.select( [], [write], [], 10 )
- if PYTHON_VERSION == 3: write.write( bytes( r, 'utf-8' ) + b'\n' )
- else: write.write( '%s\n' %r )
- write.flush()
- else:
- if DEBUG: print('returns nothing', fname)
- write.write(b'\n')
- write.flush()
- return True
- GeneratedResponses = {}
- GeneratedClasses = {}
- GeneratedRPC = {}
- class RCache(object):
- def __init__(self):
- self.instances = {}
- Cache = RCache()
- class Proxy(object): ## pypy only allows __init__ and __del__
- pass
- def geninit(): # generating init for each class is too slow
- def __init__(self, *args):
- header = self._classpath_
- if args:
- a = ''
- for arg in list(args):
- if isinstance(arg,Proxy): a += '@%s%s' %(arg._ID, ARGUMENT_SEP)
- elif isinstance( arg, str ): # type(arg) is str # not allowed in pypy
- a += '"%s"%s' %(arg, ARGUMENT_SEP)
- else: a += '%s%s' %(arg, ARGUMENT_SEP)
- pipe( '>create>%s%s%s' %(header, FUNC_ARG_SEP, a) )
- else: pipe( '>create>%s' %header )
- self._ID = '0'
- rl,wl,xl = rpoll.select( [0], [], [], 10.0 ) # wait 10 seconds, subprocess only needs to wait for reading
- if rl:
- ID = stdin.readline().strip('\n').strip(' ')
- if DEBUG: debug( 'got ID from cpython %s' %ID )
- self._ID = str(ID) #int(ID)
- Cache.instances[ self._ID ] = self
- if DEBUG: debug('ok')
- else:
- pipe( 'PIPE BLOCK - can not init' )
- raise SystemExit
- return __init__
- Proxy.__init__ = geninit()
- INSTANCES = {}
- CALL_GROUPS = { 'tag lambda *args': [], 'pygame.draw.line': [] }
- if gtk:
- CALL_GROUPS[ 'tag lambda *args' ].append( gtk.Object.connect )
- if pygame:
- CALL_GROUPS[ 'pygame.draw.line' ].append( pygame.draw.line )
- CALL_GROUPS[ 'pygame.draw.line' ].append( pygame.draw.aaline )
- RETURN_GROUPS = { tuple : [] }
- if ode:
- RETURN_GROUPS[ tuple ].append(ode.Body.getPosition)
- _parse_string = lambda s: str(s)
- _parse_string.func_name = '_parse_string'
- _parse_float = lambda s: float(s)
- _parse_float.func_name = '_parse_float'
- _parse_int = lambda s: int(s)
- _parse_int.func_name = '_parse_int'
- _parse_bool = lambda x: True if (x=='True') else False
- _parse_bool.func_name = '_parse_bool'
- #_parse_tuple_float = lambda s: [ float(x) for x in s.replace('(',' ').replace(')',' ').strip(' ').split(',') ]
- _parse_tuple_float = lambda s: [ float(x) for x in s ]
- _parse_tuple_float.func_name = '_parse_tuple_float'
- _parse_tuple_int = lambda s: [ int(x) for x in s ]
- _parse_tuple_int.func_name = '_parse_tuple_int'
- _parse_tuple_string = lambda s: [ str(x) for x in s ]
- _parse_tuple_string.func_name = '_parse_tuple_string'
- _parse_tuple_bool = lambda s: [ _parse_bool(x) for x in s ]
- _parse_tuple_bool.func_name = '_parse_tuple_bool'
- _parse_tuple_object = lambda s: [ _parse_object(x) for x in s ]
- _parse_tuple_object.func_name = '_parse_tuple_object'
- def _parse_object( s ):
- s = s.replace('<',' ').replace('>',' ').strip(' ')
- a,ID = s.split( INSTANCE_SEP ) # pypy only splits on a single char
- if ID in Cache.instances:
- if DEBUG: debug( 'proxy already exists' )
- return Cache.instances[ID]
- else:
- if DEBUG:
- debug( 'creating new rproxy' )
- debug( a )
- cls = GeneratedClasses[ a ]
- return cls( DO_NOT_CREATE, ID )
- #long = int #py3 hack
- #unicode = str
- RParseMapping = {
- object : _parse_object,
- str : _parse_string,
- float : _parse_float,
- int : _parse_int,
- bool : _parse_bool,
- #list : _parse_tuple_float, # check create_dupli_list
- (float,): _parse_tuple_float,
- (int,): _parse_tuple_int,
- (str,): _parse_tuple_string,
- (object,): _parse_tuple_object,
- (bool,): _parse_tuple_bool,
- }
- if PYTHON_VERSION == 2:
- RParseMapping[ basestring ] = _parse_string
- RParseMapping[ (basestring,) ] = _parse_tuple_string
- RParseMapping[long] = _parse_int
- RParseMapping[(long,)] = _parse_tuple_int
- def pipe( string ): # the types of *args can not change in pypy, convert to strings first
- if DEBUG: debug('pipe start: %s' %string)
- rl,wl,xl = rpoll.select( [], [1], [], 0 )
- if wl:
- stdout.write( '%s\n' %string )
- stdout.flush()
- if DEBUG: debug('pipe end: %s' %string)
- def _closure_tuple( res ):
- pipe( res )
- if DEBUG: debug( 'waiting for function response (tuple)' )
- rl,wl,xl = rpoll.select( [0], [], [], 10 )
- if rl:
- if DEBUG: debug( 'readline ready' )
- s = string = stdin.readline().strip('\n').strip(' ')
- if DEBUG: debug( 'got function response: %s' %string )
- r = []
- for a in s.split(ARGUMENT_SEP):
- if a: r.append( a ) # was the problem here?
- return r
- else:
- debug( 'PIPE BLOCK - closure tuple failed' )
- raise SystemExit
- def _closure( res ):
- pipe( res )
- if DEBUG: debug( 'waiting for function response' )
- rl,wl,xl = rpoll.select( [0], [], [], 10 ) # wait 10 seconds, subprocess only needs to wait for reading
- if rl:
- if DEBUG: debug( 'readline ready' )
- s = string = stdin.readline().strip('\n').strip(' ')
- if DEBUG: debug( 'got function response: %s' %string )
- return s
- else:
- debug( 'PIPE BLOCK - closure failed' )
- raise SystemExit
- # how to optimize this away? parent process mainloop expects to read only a single line per loop,
- # but if we dont block we can send multiple commands to parent iteration, who may wait for reading,
- # but then the server is waiting for reading, so we have a dead lock.
- # ( not a big optimize since most useful functions return something )
- def _closure_none(res):
- pipe( '$'+res )
- rl,wl,xl = rpoll.select( [0], [], [], 30 )
- r = stdin.readline()
- #if DEBUG: debug(r)
- def gen_func_response( rtype=object ):
- if rtype is None: # NO_RETURN
- #get_function_response = lambda res: pipe( '$'+res ) # this would be faster, but only for functions returning nothing - not so useful?
- get_function_response = _closure_none
- elif type(rtype) is list:
- head = 'lambda '
- body = ': ['
- for i,T in enumerate(rtype):
- head += 'arg%s,' %i
- body += '%s(arg%s),' %(RParseMapping[T].func_name, i)
- gen = head + body + ']'
- get_function_response = eval('lambda res: (%s)(*_closure(res))' %gen)
- else:
- closure = RParseMapping[rtype]
- if type(rtype) is tuple:
- get_function_response = eval('lambda res: (%s)(_closure_tuple(res))' %closure.func_name)
- else:
- get_function_response = eval('lambda res: (%s)(_closure(res))' %closure.func_name)
- return get_function_response
- class WrapFunction(object):
- def __init__(self, fname, func, parent ):
- self._func_name = fname
- self._argument_info = None
- self._return_info = object
- ## manual wrapping ##
- self._call_group = None
- self._return_group = None
- for group in CALL_GROUPS:
- if func in CALL_GROUPS[group]:
- self._call_group = group
- break
- for group in RETURN_GROUPS:
- if func in RETURN_GROUPS[group]:
- self._return_group = group
- break
- ## reflection/inspect RNA wrapping ##
- if isinstance(func,object) and func.__class__.__name__ == 'bpy_ops_submodule_op':
- print( func )
- #classpath = '%s_%s.%s' %(cls.__class__.__module__, cls.module, cls.func)
- rna = func.get_rna()
- args = []
- for prop in rna.bl_rna.properties:
- if prop.identifier == 'rna_type': continue
- args.append( reflect_RNA_prop( prop ) )
- print( args )
- self._argument_info = args
- self._return_info = None
- if hasattr( func, '_argument_info' ):
- self._argument_info = func._argument_info
- if hasattr( func, '_return_info' ):
- self._return_info = func._return_info
- if parent.__class__.__name__ == 'bpy_prop_collection':
- if fname == 'keys': self._return_info = (str,)
- elif fname == 'get': self._return_info = object
- elif fname == 'values': self._return_info = (object,)
- def unpack( self ): ## interesting pypy needs generated functions so that *args can work properly
- fname = self._func_name
- index = len( GeneratedRPC )
- gupdate = {}
- genres_name = '_generated_RPC_closure%s'%index
- if self._return_group:
- gupdate[ genres_name ] = genres_func = gen_func_response( self._return_group )
- else:
- rinfo = self._return_info
- debug( 'unpacking function %s' %fname )
- debug( str(rinfo) )
- if type(rinfo) is dict:
- if 'array-length' in rinfo and rinfo['array-length']:
- if rinfo['type'] is long: rinfo = (int,)
- else: rinfo = ( rinfo['type'], ) #* rinfo['array-length']
- else:
- if rinfo['type'] is long: rinfo = int
- else: rinfo = rinfo['type']
- elif type(rinfo) is list:
- r = []
- for a in rinfo:
- if 'array-length' in a and a['array-length']:
- if a['type'] is long: a = (int,)
- else: a = ( a['type'], ) #* a['array-length']
- else:
- if a['type'] is long: a = int
- else: a = a['type']
- r.append( a )
- rinfo = r
- if rinfo is long: rinfo = int # temp py3/2 problem
- elif type(rinfo) is tuple and rinfo[0] is long: rinfo = (int,)
- gupdate[ genres_name ] = genres_func = gen_func_response( rinfo ) # defaults to object
- GeneratedResponses[ genres_name ] = genres_func
- genres_func.func_name = genres_name
- rpc_name = '_generated_RPC%s'%index
- if self._argument_info is not None:
- e = 'lambda self,'
- for arg in self._argument_info:
- debug( arg)
- if 'default' in arg:
- if arg['type'] is basestring: e += '%s="%s",' %(arg['name'],arg['default'])
- else: e += '%s=%s,' %(arg['name'],arg['default'])
- else:
- if arg['type'] is basestring: e += '%s="",' %arg['name']
- if arg['type'] in (int,float,bool,dict,list): e += '%s=%s,' %(arg['name'], arg['type']() )
- else: e += '%s=None,' %arg['name']
- e += ': %s( "@" + str(self._ID) + "%s%s%s" ' %(genres_name,FUNCTION_SEP,fname,FUNC_ARG_SEP)
- for arg in self._argument_info:
- if arg['type'] is basestring:
- #e += '''+'"%s"%s' %''' + '(%s, ARGUMENT_SEP) ' %arg['name']
- e += ''' + '%s="'+%s+'"%s' ''' %(arg['name'], arg['name'], ARGUMENT_SEP)
- elif arg['type'] is object:
- e += '+ "%s=@" + str(%s._ID) + "%s" ' %(arg['name'],arg['name'], ARGUMENT_SEP)
- else:
- e += '+ "%s=" + str(%s) + "%s" ' %(arg['name'],arg['name'],ARGUMENT_SEP)
- e += ' ) '
- debug( e)
- wrap = rpc = eval( e )
- #if fname == 'primitive_plane_add': raise
- elif self._call_group == 'tag lambda *args':
- def rpc( self, name, tag, callback, *args ):
- if DEBUG: debug( '-rpc %s %s' %(self, name) )
- a = '"%s"%s' %(tag, ARGUMENT_SEP)
- a += '%s%s' %(callback, ARGUMENT_SEP)
- for arg in list(args):
- if isinstance(arg,Proxy): a += '@%s%s' %(arg._ID, ARGUMENT_SEP)
- elif isinstance( arg, str ): # type(arg) is str # not allowed in pypy
- a += '"%s"%s' %(arg, ARGUMENT_SEP)
- else: a += '%s%s' %(arg, ARGUMENT_SEP)
- return '@%s%s%s%s%s' %(self._ID, FUNCTION_SEP, name, FUNC_ARG_SEP, a)
- wrap = eval( 'lambda self,tag,cb,*args: %s(%s(self,"%s",tag,cb,*args))' %(genres_name, rpc_name, fname) )
- elif self._call_group == 'pygame.draw.line':
- def rpc( self, name, surf, color, start, end, width=1 ):
- if DEBUG: debug( '-rpc %s %s' %(self, name) )
- a = '@%s%s' %(surf._ID,ARGUMENT_SEP)
- a += '%s%s' %(color,ARGUMENT_SEP)
- a += '%s%s' %(start,ARGUMENT_SEP)
- a += '%s%s' %(end,ARGUMENT_SEP)
- a += '%s%s' %(width,ARGUMENT_SEP)
- return '@%s%s%s%s%s' %(self._ID, FUNCTION_SEP, name, FUNC_ARG_SEP, a)
- wrap = eval( 'lambda self,surf,color,start,end,width=1: %s(%s(self,"%s",surf,color,start,end,width))' %(genres_name, rpc_name, fname) )
- else:
- def rpc( self, name, *args ):
- if DEBUG: debug( '-rpc %s %s' %(self, name) )
- a = ''
- for arg in list(args):
- if isinstance(arg,Proxy): a += '@%s%s' %(arg._ID, ARGUMENT_SEP)
- elif isinstance( arg, str ): # type(arg) is str # not allowed in pypy
- a += '"%s"%s' %(arg, ARGUMENT_SEP)
- else: a += '%s%s' %(arg, ARGUMENT_SEP)
- return '@%s%s%s%s%s' %(self._ID, FUNCTION_SEP, name, FUNC_ARG_SEP, a)
- wrap = eval( 'lambda self,*args: %s(%s(self,"%s",*args))' %(genres_name, rpc_name, fname) )
- rpc.func_name = rpc_name
- GeneratedRPC[ rpc_name ] = rpc
- gupdate[ rpc_name ] = rpc
- globals().update( gupdate )
- return wrap
- ## these are considered constants ##
- WrappableAttributesTypes = (
- bool,
- int,
- #long,
- float,
- str,
- #unicode,
- )
- INIT_GROUPS = { int:[], object:[] }
- if gtk:
- INIT_GROUPS[ int ].append( gtk.Window )
- if ode:
- INIT_GROUPS[ object ].append( ode.Body )
- INIT_GROUPS_gen = {}
- for tag in INIT_GROUPS: INIT_GROUPS_gen[ tag ] = geninit()
- del tag
- class WrapClass(object):
- def __init__( self, cls, classpath, items={} ):
- self._classpath = classpath
- self._items = items
- self._init_type = None
- for group in INIT_GROUPS:
- if cls in INIT_GROUPS[group]:
- self._init_type = group
- def unpack( self ):
- classitems = {}
- for key in self._items:
- #debug( key )
- v = self._items[key]
- if isinstance( v, (WrapClass,WrapFunction) ): v = v.unpack()
- classitems[ key ] = v
- if self._init_type in INIT_GROUPS_gen:
- classitems['__init__'] = INIT_GROUPS_gen[ self._init_type ] # classes that have the same init vars
- classitems['_classpath_'] = str( self._classpath )
- debug( 'generating -> %s' %self._classpath )
- classname = self._classpath.split('.')[-1]
- metaclass = types.ClassType(str(classname), bases=(Proxy,), dict=classitems)
- GeneratedClasses[ str( self._classpath ) ] = metaclass
- return metaclass
- CallableClasses = 'bpy_ops_submodule_op'.split()
- def wrap_class( cls, name, classes=False ):
- print( 'wrapping', cls )
- bpy_prop_collection = False
- if bpy and isinstance( cls, bpy.types.Main ):
- mname = 'bpy'
- #if cls.__class__.__name__ == 'Main':
- # mname = 'bpy'
- #else:
- # mname = 'bpy.data'
- #elif cls.__class__.__name__ == 'RNA_Types':
- # mname = None
- elif cls.__class__.__name__ == 'bpy_prop_collection': mname = 'bpy.data'; bpy_prop_collection = True
- # mname = 'bpy'
- # mname = 'data'
- #elif cls.__class__.__name__ == 'bpy_ops':
- # mname = None
- #elif cls.__class__.__name__ == 'bpy_ops_submodule':
- # #name = cls.module # name of submod "mesh" etc..
- # mname = 'ops'
- #else:
- # #name = cls.__name__
- elif inspect.ismodule( cls ): mname = ''
- else: mname = cls.__module__
- if mname in ('__main__','pypypipe'): mname = ''
- if mname: classpath = '%s.%s' %(mname,name)
- else: classpath = name
- d = {}
- for n in dir(cls):
- if n.startswith('_'): continue
- a = getattr( cls, n )
- if inspect.isroutine( a ) or (isinstance(a,object) and a.__class__.__name__ in CallableClasses):
- d[n] = WrapFunction(n,a, cls)
- elif inspect.isclass( a ) and classes:
- #if classes: print '\tCLASS: ', n
- if a.__module__ == 'gtk._gtk': continue
- else: d[ n ] = wrap_class( a,n )
- elif isinstance(a,object) and a.__class__.__name__ in 'bpy_ops_submodule bpy_prop_collection'.split():
- d[ n ] = wrap_class( a, n )
- elif type(a) in WrappableAttributesTypes:
- d[ n ] = a
- elif type(a) in (list,tuple):
- safe = True
- for item in a:
- if type(item) not in WrappableAttributesTypes:
- safe = False; break
- if safe:
- d[ n ] = a
- else:
- ## check for gtk enum
- items = dir(a)
- enum = True
- for test in 'conjugate denominator imag numerator real'.split():
- if test not in items: enum = False; break
- if enum:
- d[ n ] = a.real
- wrapped = WrapClass( cls, classpath, items=d )
- return wrapped
- def setup_blender_loop():
- bpy.ops.screen.animation_play('EXEC_DEFAULT')
- def draw_callback( area, region ): loop()
- for area in bpy.context.window.screen.areas:
- print(area.type)
- if area.type == 'VIEW_3D':
- for reg in area.regions:
- print( '\tregion: ', reg.type )
- if reg.type == 'WINDOW':
- print( 'adding callback' )
- reg.callback_add(draw_callback, (area,reg), 'POST_PIXEL') # sneaky! unlisted function!!!
- def dump():
- ## save wrap ##
- D = {}
- if gtk:
- w = wrap_class( gtk, 'gtk', classes=True )
- D['gtk_wrapped'] = w
- #D['pygame_wrapped'] = wrap_class( pygame, classes=True )
- #D['pygame_display_wrapped'] = wrap_class( pygame.display, classes=True )
- #D['pygame_draw_wrapped'] = wrap_class( pygame.draw, classes=True )
- D['ode_wrapped'] = wrap_class( ode, 'ode', classes=True )
- elif bpy:
- bobject = bpy.data.objects.new("_unlinked_", bpy.data.meshes.new("_dummy_") )
- genclass = reflect_RNA_instance( bobject )
- D[ genclass.__name__ ] = wrap_class( genclass, genclass.__name__ )
- bobject = bpy.data.objects.new("_unlinked_", bpy.data.cameras.new("_dummy_") )
- genclass = reflect_RNA_instance( bobject )
- D[ genclass.__name__ ] = wrap_class( genclass, genclass.__name__ )
- bobject = bpy.data.objects.new("_unlinked_", bpy.data.lamps.new("_dummy_") )
- genclass = reflect_RNA_instance( bobject )
- D[ genclass.__name__ ] = wrap_class( genclass, genclass.__name__ )
- D['bpy_ops_wrapped'] = wrap_class( bpy.ops, 'ops', classes=True )
- D['bpy_data_wrapped'] = wrap_class( bpy.data, 'data', classes=True )
- #D['bpy_types_wrapped'] = wrap_class( bpy.types, 'types', classes=True )
- else:
- D['os_wrapped'] = wrap_class( os, 'os', classes=True )
- f = open('/tmp/genwrapper.pickle','wb')
- pickle.dump( D, f, protocol=2) #,fix_imports=False )
- f.close()
- print( 'genwrapper.pickle dumped' )
- if __name__ == '__main__' and '--pypy' in sys.argv:
- dump = pickle.load( open('/tmp/genwrapper.pickle','rb') )
- D = globals()
- for key in dump:
- debug( key )
- metaclass = dump[key].unpack()
- debug( metaclass )
- D[ key ] = metaclass
- #why does this fail?#GeneratedClasses[ key ] = metaclass
- for k in globals().keys():
- if not k.startswith('_'):
- debug('global %s' %k )
- ## uncomment this if you do not want pypy translation to C
- #pypy_entry_point_test_bpy()
- #raise
- if '--bpy' in sys.argv: t = Translation( pypy_entry_point_test_bpy )
- elif '--gtk' in sys.argv: t = Translation( pypy_entry_point )
- else: t = Translation( pypy_entry_point_simple )
- t.annotate(); t.rtype()
- f = t.compile_c()
- f()
- print( 'subprocess exit' )
- elif __name__ == '__main__':
- import pypypipe # blender253 pickle bug
- pypypipe.dump()
- test = ''
- if bpy: test = '--bpy'
- elif gtk: test = '--gtk'
- process = subprocess.Popen( 'python pypypipe.py --pypy %s' %test, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0, shell=True )
- write = process.stdin
- read = process.stdout
- print( 'read pipe', read )
- print( 'read write', write )
- if glib:
- glib.timeout_add( 33, loop )
- gtk.main()
- elif bpy: setup_blender_loop()
- print( 'toplevel exit' )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement