""" Gateway between app-level and interpreter-level: * BuiltinCode (call interp-level code from app-level) * app2interp (embed an app-level function into an interp-level callable) * interp2app (publish an interp-level object to be visible from app-level) """ import types, sys, os from pypy.tool.compat import md5 NoneNotWrapped = object() from pypy.tool.sourcetools import func_with_new_name from pypy.interpreter.error import OperationError from pypy.interpreter import eval from pypy.interpreter.function import Function, Method, ClassMethod from pypy.interpreter.baseobjspace import W_Root, ObjSpace, Wrappable from pypy.interpreter.baseobjspace import Wrappable, SpaceCache, DescrMismatch from pypy.interpreter.argument import Arguments, AbstractArguments from pypy.tool.sourcetools import NiceCompile, compile2 from pypy.rlib.jit import hint from pypy.rlib.rarithmetic import r_longlong, r_int, r_ulonglong, r_uint # internal non-translatable parts: import py class Signature: "NOT_RPYTHON" def __init__(self, func=None, argnames=None, varargname=None, kwargname=None, name = None): self.func = func if func is not None: self.name = func.__name__ else: self.name = name if argnames is None: argnames = [] self.argnames = argnames self.varargname = varargname self.kwargname = kwargname def append(self, argname): self.argnames.append(argname) def signature(self): return self.argnames, self.varargname, self.kwargname #________________________________________________________________ class UnwrapSpecRecipe: "NOT_RPYTHON" bases_order = [Wrappable, W_Root, ObjSpace, Arguments, object] def dispatch(self, el, *args): if isinstance(el, str): getattr(self, "visit_%s" % (el,))(el, *args) elif isinstance(el, tuple): if el[0] == 'self': self.visit_self(el[1], *args) else: self.visit_function(el, *args) else: for typ in self.bases_order: if issubclass(el, typ): visit = getattr(self, "visit__%s" % (typ.__name__,)) visit(el, *args) break else: raise Exception("%s: no match for unwrap_spec element %s" % ( self.__class__.__name__, el)) def apply_over(self, unwrap_spec, *extra): dispatch = self.dispatch for el in unwrap_spec: dispatch(el, *extra) class UnwrapSpecEmit(UnwrapSpecRecipe): def __init__(self): self.n = 0 self.miniglobals = {} def succ(self): n = self.n self.n += 1 return n def use(self, obj): name = obj.__name__ self.miniglobals[name] = obj return name #________________________________________________________________ class UnwrapSpec_Check(UnwrapSpecRecipe): # checks for checking interp2app func argument names wrt unwrap_spec # and synthetizing an app-level signature def __init__(self, original_sig): self.func = original_sig.func self.orig_arg = iter(original_sig.argnames).next def visit_function(self, (func, cls), app_sig): self.dispatch(cls, app_sig) def visit_self(self, cls, app_sig): self.visit__Wrappable(cls, app_sig) def checked_space_method(self, typname, app_sig): argname = self.orig_arg() assert not argname.startswith('w_'), ( "unwrapped %s argument %s of built-in function %r should " "not start with 'w_'" % (typname, argname, self.func)) app_sig.append(argname) def visit_index(self, index, app_sig): self.checked_space_method(index, app_sig) def visit_bufferstr(self, el, app_sig): self.checked_space_method(el, app_sig) def visit__Wrappable(self, el, app_sig): name = el.__name__ argname = self.orig_arg() assert not argname.startswith('w_'), ( "unwrapped %s argument %s of built-in function %r should " "not start with 'w_'" % (name, argname, self.func)) app_sig.append(argname) def visit__ObjSpace(self, el, app_sig): self.orig_arg() def visit__W_Root(self, el, app_sig): assert el is W_Root, "%s is not W_Root (forgotten to put .im_func in interp2app argument?)" % (el,) argname = self.orig_arg() assert argname.startswith('w_'), ( "argument %s of built-in function %r should " "start with 'w_'" % (argname, self.func)) app_sig.append(argname[2:]) def visit__Arguments(self, el, app_sig): argname = self.orig_arg() assert app_sig.varargname is None,( "built-in function %r has conflicting rest args specs" % self.func) app_sig.varargname = 'args' app_sig.kwargname = 'keywords' def visit_args_w(self, el, app_sig): argname = self.orig_arg() assert argname.endswith('_w'), ( "rest arguments arg %s of built-in function %r should end in '_w'" % (argname, self.func)) assert app_sig.varargname is None,( "built-in function %r has conflicting rest args specs" % self.func) app_sig.varargname = argname[:-2] def visit_w_args(self, el, app_sig): argname = self.orig_arg() assert argname.startswith('w_'), ( "rest arguments arg %s of built-in function %r should start 'w_'" % (argname, self.func)) assert app_sig.varargname is None,( "built-in function %r has conflicting rest args specs" % self.func) app_sig.varargname = argname[2:] def visit__object(self, typ, app_sig): name = int_unwrapping_space_method(typ) self.checked_space_method(name, app_sig) class UnwrapSpec_EmitRun(UnwrapSpecEmit): # collect code to emit for interp2app builtin frames based on unwrap_spec def __init__(self): UnwrapSpecEmit.__init__(self) self.run_args = [] def scopenext(self): return "scope_w[%d]" % self.succ() def visit_function(self, (func, cls)): self.run_args.append("%s(%s)" % (self.use(func), self.scopenext())) def visit_self(self, typ): self.run_args.append("space.descr_self_interp_w(%s, %s)" % (self.use(typ), self.scopenext())) def visit__Wrappable(self, typ): self.run_args.append("space.interp_w(%s, %s)" % (self.use(typ), self.scopenext())) def visit__ObjSpace(self, el): self.run_args.append('space') def visit__W_Root(self, el): self.run_args.append(self.scopenext()) def visit__Arguments(self, el): self.miniglobals['Arguments'] = Arguments self.run_args.append("Arguments.frompacked(space, %s, %s)" % (self.scopenext(), self.scopenext())) def visit_args_w(self, el): self.run_args.append("space.viewiterable(%s)" % self.scopenext()) def visit_w_args(self, el): self.run_args.append(self.scopenext()) def visit__object(self, typ): name = int_unwrapping_space_method(typ) self.run_args.append("space.%s(%s)" % (name, self.scopenext())) def visit_index(self, typ): self.run_args.append("space.getindex_w(%s, space.w_OverflowError)" % (self.scopenext(), )) def visit_bufferstr(self, typ): self.run_args.append("space.bufferstr_w(%s)" % (self.scopenext(),)) def _make_unwrap_activation_class(self, unwrap_spec, cache={}): try: key = tuple(unwrap_spec) activation_factory_cls, run_args = cache[key] assert run_args == self.run_args, ( "unexpected: same spec, different run_args") return activation_factory_cls except KeyError: parts = [] for el in unwrap_spec: if isinstance(el, tuple): parts.append(''.join([getattr(subel, '__name__', subel) for subel in el])) else: parts.append(getattr(el, '__name__', el)) label = '_'.join(parts) #print label d = {} source = """if 1: def _run_UWS_%s(self, space, scope_w): return self.behavior(%s) \n""" % (label, ', '.join(self.run_args)) exec compile2(source) in self.miniglobals, d d['_run'] = d['_run_UWS_%s' % label] del d['_run_UWS_%s' % label] activation_cls = type("BuiltinActivation_UwS_%s" % label, (BuiltinActivation,), d) cache[key] = activation_cls, self.run_args return activation_cls def make_activation(unwrap_spec, func): emit = UnwrapSpec_EmitRun() emit.apply_over(unwrap_spec) activation_uw_cls = emit._make_unwrap_activation_class(unwrap_spec) return activation_uw_cls(func) make_activation = staticmethod(make_activation) class BuiltinActivation(object): def __init__(self, behavior): """NOT_RPYTHON""" self.behavior = behavior def _run(self, space, scope_w): """Subclasses with behavior specific for an unwrap spec are generated""" raise TypeError, "abstract" #________________________________________________________________ class FastFuncNotSupported(Exception): pass class UnwrapSpec_FastFunc_Unwrap(UnwrapSpecEmit): def __init__(self): UnwrapSpecEmit.__init__(self) self.args = [] self.unwrap = [] self.finger = 0 def dispatch(self, el, *args): UnwrapSpecEmit.dispatch(self, el, *args) self.finger += 1 if self.n > 4: raise FastFuncNotSupported def nextarg(self): arg = "w%d" % self.succ() self.args.append(arg) return arg def visit_function(self, (func, cls)): raise FastFuncNotSupported def visit_self(self, typ): self.unwrap.append("space.descr_self_interp_w(%s, %s)" % (self.use(typ), self.nextarg())) def visit__Wrappable(self, typ): self.unwrap.append("space.interp_w(%s, %s)" % (self.use(typ), self.nextarg())) def visit__ObjSpace(self, el): if self.finger != 0: raise FastFuncNotSupported self.unwrap.append("space") def visit__W_Root(self, el): self.unwrap.append(self.nextarg()) def visit__Arguments(self, el): raise FastFuncNotSupported def visit_args_w(self, el): raise FastFuncNotSupported def visit_w_args(self, el): raise FastFuncNotSupported def visit__object(self, typ): name = int_unwrapping_space_method(typ) self.unwrap.append("space.%s(%s)" % (name, self.nextarg())) def visit_index(self, typ): self.unwrap.append("space.getindex_w(%s, space.w_OverflowError)" % (self.nextarg()), ) def visit_bufferstr(self, typ): self.unwrap.append("space.bufferstr_w(%s)" % (self.nextarg(),)) def make_fastfunc(unwrap_spec, func): unwrap_info = UnwrapSpec_FastFunc_Unwrap() unwrap_info.apply_over(unwrap_spec) narg = unwrap_info.n args = ['space'] + unwrap_info.args if args == unwrap_info.unwrap: fastfunc = func else: # try to avoid excessive bloat if func.__module__ == 'pypy.interpreter.astcompiler.ast': raise FastFuncNotSupported if (not func.__module__.startswith('pypy.module.__builtin__') and not func.__module__.startswith('pypy.module.sys') and not func.__module__.startswith('pypy.module.math')): if not func.__name__.startswith('descr'): raise FastFuncNotSupported d = {} unwrap_info.miniglobals['func'] = func source = """if 1: def fastfunc_%s_%d(%s): return func(%s) \n""" % (func.__name__, narg, ', '.join(args), ', '.join(unwrap_info.unwrap)) exec compile2(source) in unwrap_info.miniglobals, d fastfunc = d['fastfunc_%s_%d' % (func.__name__, narg)] return narg, fastfunc make_fastfunc = staticmethod(make_fastfunc) def int_unwrapping_space_method(typ): assert typ in (int, str, float, unicode, r_longlong, r_uint, r_ulonglong, bool) if typ is r_int is r_longlong: return 'r_longlong_w' elif typ is r_uint: return 'uint_w' elif typ is bool: return 'is_true' else: return typ.__name__ + '_w' class BuiltinCode(eval.Code): "The code object implementing a built-in (interpreter-level) hook." _immutable_ = True hidden_applevel = True descrmismatch_op = None descr_reqcls = None # When a BuiltinCode is stored in a Function object, # you get the functionality of CPython's built-in function type. NOT_RPYTHON_ATTRIBUTES = ['_bltin', '_unwrap_spec'] def __init__(self, func, unwrap_spec = None, self_type = None, descrmismatch=None): "NOT_RPYTHON" # 'implfunc' is the interpreter-level function. # Note that this uses a lot of (construction-time) introspection. eval.Code.__init__(self, func.__name__) self.docstring = func.__doc__ # unwrap_spec can be passed to interp2app or # attached as an attribute to the function. # It is a list of types or singleton objects: # baseobjspace.ObjSpace is used to specify the space argument # baseobjspace.W_Root is for wrapped arguments to keep wrapped # baseobjspace.Wrappable subclasses imply interp_w and a typecheck # argument.Arguments is for a final rest arguments Arguments object # 'args_w' for viewiterable applied to rest arguments # 'w_args' for rest arguments passed as wrapped tuple # str,int,float: unwrap argument as such type # (function, cls) use function to check/unwrap argument of type cls # First extract the signature from the (CPython-level) code object from pypy.interpreter import pycode argnames, varargname, kwargname = pycode.cpython_code_signature(func.func_code) if unwrap_spec is None: unwrap_spec = getattr(func,'unwrap_spec',None) if unwrap_spec is None: unwrap_spec = [ObjSpace]+ [W_Root] * (len(argnames)-1) if self_type: unwrap_spec = ['self'] + unwrap_spec[1:] if self_type: assert unwrap_spec[0] == 'self',"self_type without 'self' spec element" unwrap_spec = list(unwrap_spec) if descrmismatch is not None: assert issubclass(self_type, Wrappable) unwrap_spec[0] = ('self', self_type) self.descrmismatch_op = descrmismatch self.descr_reqcls = self_type else: unwrap_spec[0] = self_type else: assert descrmismatch is None, ( "descrmismatch without a self-type specified") orig_sig = Signature(func, argnames, varargname, kwargname) app_sig = Signature(func) UnwrapSpec_Check(orig_sig).apply_over(unwrap_spec, app_sig #to populate ) self.sig = argnames, varargname, kwargname = app_sig.signature() self.minargs = len(argnames) if varargname: self.maxargs = sys.maxint else: self.maxargs = self.minargs self.activation = UnwrapSpec_EmitRun.make_activation(unwrap_spec, func) self._bltin = func self._unwrap_spec = unwrap_spec # speed hack if 0 <= len(unwrap_spec) <= 5: try: arity, fastfunc = UnwrapSpec_FastFunc_Unwrap.make_fastfunc( unwrap_spec, func) except FastFuncNotSupported: if unwrap_spec == [ObjSpace, Arguments]: self.__class__ = BuiltinCodePassThroughArguments0 self.func__args__ = func elif unwrap_spec == [ObjSpace, W_Root, Arguments]: self.__class__ = BuiltinCodePassThroughArguments1 self.func__args__ = func else: self.__class__ = globals()['BuiltinCode%d' % arity] setattr(self, 'fastfunc_%d' % arity, fastfunc) def signature(self): return self.sig def getdocstring(self, space): return space.wrap(self.docstring) def funcrun(self, func, args): return BuiltinCode.funcrun_obj(self, func, None, args) def funcrun_obj(self, func, w_obj, args): space = func.space activation = self.activation scope_w = args.parse_obj(w_obj, func.name, self.sig, func.defs_w, self.minargs) try: w_result = activation._run(space, scope_w) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except NotImplementedError, e: raise except RuntimeError, e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) except DescrMismatch, e: if w_obj is not None: args = args.prepend(w_obj) return scope_w[0].descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, args) if w_result is None: w_result = space.w_None return w_result # (verbose) performance hack below class BuiltinCodePassThroughArguments0(BuiltinCode): def funcrun(self, func, args): space = func.space try: w_result = self.func__args__(space, args) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except NotImplementedError, e: raise except RuntimeError, e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) except DescrMismatch, e: return args.firstarg().descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, args) if w_result is None: w_result = space.w_None return w_result class BuiltinCodePassThroughArguments1(BuiltinCode): fast_natural_arity = -1 def funcrun_obj(self, func, w_obj, args): space = func.space try: w_result = self.func__args__(space, w_obj, args) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except NotImplementedError, e: raise except RuntimeError, e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) except DescrMismatch, e: return args.firstarg().descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, args.prepend(w_obj)) if w_result is None: w_result = space.w_None return w_result class BuiltinCode0(BuiltinCode): fast_natural_arity = 0 def fastcall_0(self, space, w_func): self = hint(self, deepfreeze=True) try: w_result = self.fastfunc_0(space) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except (RuntimeError, DescrMismatch), e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) if w_result is None: w_result = space.w_None return w_result class BuiltinCode1(BuiltinCode): fast_natural_arity = 1 def fastcall_1(self, space, w_func, w1): self = hint(self, deepfreeze=True) try: w_result = self.fastfunc_1(space, w1) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except NotImplementedError, e: raise except RuntimeError, e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) except DescrMismatch, e: return w1.descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, Arguments(space, [w1])) if w_result is None: w_result = space.w_None return w_result class BuiltinCode2(BuiltinCode): fast_natural_arity = 2 def fastcall_2(self, space, w_func, w1, w2): self = hint(self, deepfreeze=True) try: w_result = self.fastfunc_2(space, w1, w2) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except NotImplementedError, e: raise except RuntimeError, e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) except DescrMismatch, e: return w1.descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, Arguments(space, [w1, w2])) if w_result is None: w_result = space.w_None return w_result class BuiltinCode3(BuiltinCode): fast_natural_arity = 3 def fastcall_3(self, space, func, w1, w2, w3): self = hint(self, deepfreeze=True) try: w_result = self.fastfunc_3(space, w1, w2, w3) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except NotImplementedError, e: raise except RuntimeError, e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) except DescrMismatch, e: return w1.descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, Arguments(space, [w1, w2, w3])) if w_result is None: w_result = space.w_None return w_result class BuiltinCode4(BuiltinCode): fast_natural_arity = 4 def fastcall_4(self, space, func, w1, w2, w3, w4): self = hint(self, deepfreeze=True) try: w_result = self.fastfunc_4(space, w1, w2, w3, w4) except KeyboardInterrupt: raise OperationError(space.w_KeyboardInterrupt, space.w_None) except MemoryError: raise OperationError(space.w_MemoryError, space.w_None) except NotImplementedError, e: raise except RuntimeError, e: raise OperationError(space.w_RuntimeError, space.wrap("internal error: " + str(e))) except DescrMismatch, e: return w1.descr_call_mismatch(space, self.descrmismatch_op, self.descr_reqcls, Arguments(space, [w1, w2, w3, w4])) if w_result is None: w_result = space.w_None return w_result class interp2app(Wrappable): """Build a gateway that calls 'f' at interp-level.""" # NOTICE interp2app defaults are stored and passed as # wrapped values, this to avoid having scope_w be of mixed # wrapped and unwrapped types; # an exception is made for the NoneNotWrapped special value # which is passed around as default as an unwrapped None, # unwrapped None and wrapped types are compatible # # Takes optionally an unwrap_spec, see BuiltinCode NOT_RPYTHON_ATTRIBUTES = ['_staticdefs'] def __init__(self, f, app_name=None, unwrap_spec = None, descrmismatch=None, as_classmethod=False): "NOT_RPYTHON" Wrappable.__init__(self) # f must be a function whose name does NOT start with 'app_' self_type = None if hasattr(f, 'im_func'): self_type = f.im_class f = f.im_func if not isinstance(f, types.FunctionType): raise TypeError, "function expected, got %r instead" % f if app_name is None: if f.func_name.startswith('app_'): raise ValueError, ("function name %r suspiciously starts " "with 'app_'" % f.func_name) app_name = f.func_name self._code = BuiltinCode(f, unwrap_spec=unwrap_spec, self_type = self_type, descrmismatch=descrmismatch) self.__name__ = f.func_name self.name = app_name self.as_classmethod = as_classmethod self._staticdefs = list(f.func_defaults or ()) def _getdefaults(self, space): "NOT_RPYTHON" defs_w = [] for val in self._staticdefs: if val is NoneNotWrapped: defs_w.append(None) else: defs_w.append(space.wrap(val)) return defs_w # lazy binding to space def __spacebind__(self, space): # we first make a real Function object out of it # and the result is a wrapped version of this Function. return self.get_function(space) def get_function(self, space): return self.getcache(space).getorbuild(self) def getcache(self, space): return space.fromcache(GatewayCache) class GatewayCache(SpaceCache): def build(cache, gateway): "NOT_RPYTHON" space = cache.space defs = gateway._getdefaults(space) # needs to be implemented by subclass code = gateway._code fn = Function(space, code, None, defs, forcename = gateway.name) if gateway.as_classmethod: fn = ClassMethod(space.wrap(fn)) return fn # # the next gateways are to be used only for # temporary/initialization purposes class interp2app_temp(interp2app): "NOT_RPYTHON" def getcache(self, space): return self.__dict__.setdefault(space, GatewayCache(space)) # and now for something completely different ... # class ApplevelClass: """NOT_RPYTHON A container for app-level source code that should be executed as a module in the object space; interphook() builds a static interp-level function that invokes the callable with the given name at app-level.""" hidden_applevel = True def __init__(self, source, filename = None, modname = '__builtin__'): self.filename = filename if self.filename is None: self.code = py.code.Source(source).compile() else: self.code = NiceCompile(self.filename)(source) self.modname = modname # look at the first three lines for a NOT_RPYTHON tag first = "\n".join(source.split("\n", 3)[:3]) if "NOT_RPYTHON" in first: self.can_use_geninterp = False else: self.can_use_geninterp = True def __repr__(self): return "" % (self.filename, self.can_use_geninterp) def getwdict(self, space): return space.fromcache(ApplevelCache).getorbuild(self) def buildmodule(self, space, name='applevel'): from pypy.interpreter.module import Module return Module(space, space.wrap(name), self.getwdict(space)) def wget(self, space, name): if hasattr(space, '_applevelclass_hook'): # XXX for the CPyObjSpace return space._applevelclass_hook(self, name) w_globals = self.getwdict(space) return space.getitem(w_globals, space.wrap(name)) def interphook(self, name): "NOT_RPYTHON" def appcaller(space, *args_w): if not isinstance(space, ObjSpace): raise TypeError("first argument must be a space instance.") # redirect if the space handles this specially # XXX can this be factored a bit less flow space dependently? if hasattr(space, 'specialcases'): sc = space.specialcases if ApplevelClass in sc: ret_w = sc[ApplevelClass](space, self, name, args_w) if ret_w is not None: # it was RPython return ret_w # the last argument can be an Arguments if not args_w: args = Arguments(space, []) else: args = args_w[-1] assert args is not None if not isinstance(args, AbstractArguments): args = Arguments(space, list(args_w)) else: # ...which is merged with the previous arguments, if any if len(args_w) > 1: more_args_w, more_kwds_w = args.unpack() args = Arguments(space, list(args_w[:-1]) + more_args_w, more_kwds_w) w_func = self.wget(space, name) return space.call_args(w_func, args) def get_function(space): w_func = self.wget(space, name) return space.unwrap(w_func) appcaller = func_with_new_name(appcaller, name) appcaller.get_function = get_function return appcaller def _freeze_(self): return True # hint for the annotator: applevel instances are constants class ApplevelCache(SpaceCache): """NOT_RPYTHON The cache mapping each applevel instance to its lazily built w_dict""" def build(self, app): "NOT_RPYTHON. Called indirectly by Applevel.getwdict()." if self.space.config.objspace.geninterp and app.can_use_geninterp: return PyPyCacheDir.build_applevelinterp_dict(app, self.space) else: return build_applevel_dict(app, self.space) # __________ pure applevel version __________ def build_applevel_dict(self, space): "NOT_RPYTHON" from pypy.interpreter.pycode import PyCode w_glob = space.newdict() space.setitem(w_glob, space.wrap('__name__'), space.wrap(self.modname)) space.exec_(self.code, w_glob, w_glob, hidden_applevel=self.hidden_applevel) return w_glob # __________ geninterplevel version __________ class PyPyCacheDir: "NOT_RPYTHON" # similar to applevel, but using translation to interp-level. # This version maintains a cache folder with single files. def build_applevelinterp_dict(cls, self, space): "NOT_RPYTHON" # N.B. 'self' is the ApplevelInterp; this is a class method, # just so that we have a convenient place to store the global state. if not cls._setup_done: cls._setup() from pypy.translator.geninterplevel import translate_as_module import marshal scramble = md5.new(cls.seed) scramble.update(marshal.dumps(self.code)) key = scramble.hexdigest() initfunc = cls.known_code.get(key) if not initfunc: # try to get it from file name = key if self.filename: prename = os.path.splitext(os.path.basename(self.filename))[0] else: prename = 'zznoname' name = "%s_%s" % (prename, name) try: __import__("pypy._cache."+name) except ImportError, x: # print x pass else: initfunc = cls.known_code[key] if not initfunc: # build it and put it into a file initfunc, newsrc = translate_as_module( self.code, self.filename, self.modname) fname = cls.cache_path.join(name+".py").strpath f = file(get_tmp_file_name(fname), "w") print >> f, """\ # self-destruct on double-click: if __name__ == "__main__": from pypy import _cache import os namestart = os.path.join(os.path.split(_cache.__file__)[0], '%s') for ending in ('.py', '.pyc', '.pyo'): try: os.unlink(namestart+ending) except os.error: pass""" % name print >> f print >> f, newsrc print >> f, "from pypy._cache import known_code" print >> f, "known_code[%r] = %s" % (key, initfunc.__name__) f.close() rename_tmp_to_eventual_file_name(fname) w_glob = initfunc(space) return w_glob build_applevelinterp_dict = classmethod(build_applevelinterp_dict) _setup_done = False def _setup(cls): """NOT_RPYTHON""" lp = py.path.local import pypy, os p = lp(pypy.__file__).new(basename='_cache').ensure(dir=1) cls.cache_path = p ini = p.join('__init__.py') try: if not ini.check(): raise ImportError # don't import if only a .pyc file left!!! from pypy._cache import known_code, \ GI_VERSION_RENDERED except ImportError: GI_VERSION_RENDERED = 0 from pypy.translator.geninterplevel import GI_VERSION cls.seed = md5.new(str(GI_VERSION)).digest() if GI_VERSION != GI_VERSION_RENDERED or GI_VERSION is None: for pth in p.listdir(): if pth.check(file=1): try: pth.remove() except: pass f = file(get_tmp_file_name(str(ini)), "w") f.write("""\ # This folder acts as a cache for code snippets which have been # compiled by compile_as_module(). # It will get a new entry for every piece of code that has # not been seen, yet. # # Caution! Only the code snippet is checked. If something # is imported, changes are not detected. Also, changes # to geninterplevel or gateway are also not checked. # Exception: There is a checked version number in geninterplevel.py # # If in doubt, remove this file from time to time. GI_VERSION_RENDERED = %r known_code = {} # self-destruct on double-click: def harakiri(): import pypy._cache as _c import py lp = py.path.local for pth in lp(_c.__file__).dirpath().listdir(): try: pth.remove() except: pass if __name__ == "__main__": harakiri() del harakiri """ % GI_VERSION) f.close() rename_tmp_to_eventual_file_name(str(ini)) import pypy._cache cls.known_code = pypy._cache.known_code cls._setup_done = True _setup = classmethod(_setup) def gethostname(_cache=[]): if not _cache: try: import socket hostname = socket.gethostname() except: hostname = '' _cache.append(hostname) return _cache[0] def get_tmp_file_name(fname): return '%s~%s~%d' % (fname, gethostname(), os.getpid()) def rename_tmp_to_eventual_file_name(fname): # generated files are first written to the host- and process-specific # file 'tmpname', and then atomically moved to their final 'fname' # to avoid problems if py.py is started several times in parallel tmpname = get_tmp_file_name(fname) try: os.rename(tmpname, fname) except (OSError, IOError): os.unlink(fname) # necessary on Windows os.rename(tmpname, fname) # ____________________________________________________________ def appdef(source, applevel=ApplevelClass): """ NOT_RPYTHON: build an app-level helper function, like for example: myfunc = appdef('''myfunc(x, y): return x+y ''') """ if not isinstance(source, str): source = str(py.code.Source(source).strip()) assert source.startswith("def "), "can only transform functions" source = source[4:] p = source.find('(') assert p >= 0 funcname = source[:p].strip() source = source[p:] return applevel("def %s%s\n" % (funcname, source)).interphook(funcname) applevel = ApplevelClass # backward compatibility app2interp = appdef # backward compatibility class applevel_temp(ApplevelClass): hidden_applevel = False def getwdict(self, space): # no cache return build_applevel_dict(self, space) class applevelinterp_temp(ApplevelClass): hidden_applevel = False def getwdict(self, space): # no cache return PyPyCacheDir.build_applevelinterp_dict(self, space) # app2interp_temp is used for testing mainly def app2interp_temp(func, applevel_temp=applevel_temp): """ NOT_RPYTHON """ return appdef(func, applevel_temp)