Source

pypy / pypy / objspace / thunk.py

Armin Rigo de30cf9 


Carl Friedrich B… 7315d59 
Armin Rigo de30cf9 













Armin Rigo 996efc2 









Armin Rigo de30cf9 




Armin Rigo be001ba 
Armin Rigo de30cf9 





Armin Rigo 72716ae 




Armin Rigo de30cf9 



Carl Friedrich B… 8e7d1ae 
Armin Rigo de30cf9 










Carl Friedrich B… 8e7d1ae 

Armin Rigo de30cf9 






Carl Friedrich B… 8e7d1ae 




Carl Friedrich B… ede526d 




Armin Rigo de30cf9 
Carl Friedrich B… ede526d 
Armin Rigo de30cf9 



Carl Friedrich B… ede526d 












Armin Rigo de30cf9 





Armin Rigo 648159b 

Armin Rigo de30cf9 
Amaury Forgeot d… 68c34e4 
Armin Rigo de30cf9 

Armin Rigo 648159b 
Carl Friedrich B… ede526d 






Armin Rigo de30cf9 


Armin Rigo 648159b 
Armin Rigo de30cf9 
Carl Friedrich B… ede526d 

Armin Rigo de30cf9 


Armin Rigo 996efc2 
Carl Friedrich B… 76d61c6 
Samuele Pedroni 6d30865 
Armin Rigo 996efc2 



Armin Rigo de30cf9 














Maciej Fijalkows… acf9d15 
Carl Friedrich B… a4f5018 
Armin Rigo de30cf9 








































Armin Rigo 50f5c28 






Armin Rigo de30cf9 









Carl Friedrich B… af8f16a 
Carl Friedrich B… 7315d59 
Samuele Pedroni 6d30865 
Carl Friedrich B… 7315d59 
Samuele Pedroni 6d30865 
Carl Friedrich B… 7315d59 
Armin Rigo de30cf9 
Carl Friedrich B… 7315d59 
Armin Rigo de30cf9 
Carl Friedrich B… 7315d59 
Armin Rigo 996efc2 
Armin Rigo de30cf9 
"""Example usage:

    $ py.py -o thunk
    >>> from __pypy__ import thunk, lazy, become
    >>> def f():
    ...     print 'computing...'
    ...     return 6*7
    ...
    >>> x = thunk(f)
    >>> x
    computing...
    42
    >>> x
    42
    >>> y = thunk(f)
    >>> type(y)
    computing...
    <pypy type 'int'>

    >>> @lazy
    ... def g(n):
    ...     print 'computing...'
    ...     return n + 5
    ...
    >>> y = g(12)
    >>> y
    computing...
    17
"""

from pypy.objspace.proxy import patch_space_in_place
from pypy.interpreter import gateway, baseobjspace, argument
from pypy.interpreter.error import OperationError
from pypy.interpreter.function import Method

# __________________________________________________________________________

# 'w_obj.w_thunkalias' points to another object that 'w_obj' has turned into
baseobjspace.W_Root.w_thunkalias = None

# adding a name in __slots__ after class creation doesn't "work" in Python,
# but in this case it has the effect of telling the annotator that this
# attribute is allowed to be moved up to this class.
baseobjspace.W_Root.__slots__ += ('w_thunkalias',)

class W_Thunk(baseobjspace.W_Root, object):
    def __init__(w_self, w_callable, args):
        w_self.w_callable = w_callable
        w_self.args = args
        w_self.operr = None

# special marker to say that w_self has not been computed yet
w_NOT_COMPUTED_THUNK = W_Thunk(None, None)
W_Thunk.w_thunkalias = w_NOT_COMPUTED_THUNK


def _force(space, w_self):
    w_alias = w_self.w_thunkalias
    while w_alias is not None:
        if w_alias is w_NOT_COMPUTED_THUNK:
            assert isinstance(w_self, W_Thunk)
            if w_self.operr is not None:
                raise w_self.operr
            w_callable = w_self.w_callable
            args       = w_self.args
            if w_callable is None or args is None:
                raise OperationError(space.w_RuntimeError,
                                 space.wrap("thunk is already being computed"))
            w_self.w_callable = None
            w_self.args       = None
            try:
                w_alias = space.call_args(w_callable, args)
            except OperationError, operr:
                w_self.operr = operr
                raise
            if _is_circular(w_self, w_alias):
                operr = OperationError(space.w_RuntimeError,
                                       space.wrap("circular thunk alias"))
                w_self.operr = operr
                raise operr
            w_self.w_thunkalias = w_alias
        # XXX do path compression?
        w_self = w_alias
        w_alias = w_self.w_thunkalias
    return w_self

def _is_circular(w_obj, w_alias):
    assert (w_obj.w_thunkalias is None or
            w_obj.w_thunkalias is w_NOT_COMPUTED_THUNK)
    while 1:
        if w_obj is w_alias:
            return True
        w_next = w_alias.w_thunkalias
        if w_next is None:
            return False
        if w_next is w_NOT_COMPUTED_THUNK:
            return False
        w_alias = w_next

def force(space, w_self):
    if w_self.w_thunkalias is not None:
        w_self = _force(space, w_self)
    return w_self

def thunk(w_callable, __args__):
    """thunk(f, *args, **kwds) -> an object that behaves like the
    result of the call f(*args, **kwds).  The call is performed lazily."""
    return W_Thunk(w_callable, __args__)
app_thunk = gateway.interp2app(thunk)

def is_thunk(space, w_obj):
    """Check if an object is a thunk that has not been computed yet."""
    while 1:
        w_alias = w_obj.w_thunkalias
        if w_alias is None:
            return space.w_False
        if w_alias is w_NOT_COMPUTED_THUNK:
            return space.w_True
        w_obj = w_alias
app_is_thunk = gateway.interp2app(is_thunk)

def become(space, w_target, w_source):
    """Globally replace the target object with the source one."""
    w_target = force(space, w_target)
    if not _is_circular(w_target, w_source):
        w_target.w_thunkalias = w_source
    return space.w_None
app_become = gateway.interp2app(become)

def lazy(space, w_callable):
    """Decorator to make a callable return its results wrapped in a thunk."""
    meth = Method(space, space.w_fn_thunk,
                  w_callable, space.type(w_callable))
    return space.wrap(meth)
app_lazy = gateway.interp2app(lazy)

# __________________________________________________________________________

nb_forcing_args = {}

def setup():
    nb_forcing_args.update({
        'setattr': 2,   # instead of 3
        'setitem': 2,   # instead of 3
        'get': 2,       # instead of 3
        # ---- irregular operations ----
        'wrap': 0,
        'str_w': 1,
        'int_w': 1,
        'float_w': 1,
        'uint_w': 1,
        'unicode_w': 1,
        'bigint_w': 1,
        'interpclass_w': 1,
        'unwrap': 1,
        'is_true': 1,
        'is_w': 2,
        'newtuple': 0,
        'newlist': 0,
        'newdict': 0,
        'newslice': 0,
        'call_args': 1,
        'marshal_w': 1,
        'log': 1,
        })
    for opname, _, arity, _ in baseobjspace.ObjSpace.MethodTable:
        nb_forcing_args.setdefault(opname, arity)
    for opname in baseobjspace.ObjSpace.IrregularOpTable:
        assert opname in nb_forcing_args, "missing %r" % opname

setup()
del setup

# __________________________________________________________________________

def proxymaker(space, opname, parentfn):
    nb_args = nb_forcing_args[opname]
    if nb_args == 0:
        proxy = None
    elif nb_args == 1:
        def proxy(w1, *extra):
            w1 = force(space, w1)
            return parentfn(w1, *extra)
    elif nb_args == 2:
        def proxy(w1, w2, *extra):
            w1 = force(space, w1)
            w2 = force(space, w2)
            return parentfn(w1, w2, *extra)
    elif nb_args == 3:
        def proxy(w1, w2, w3, *extra):
            w1 = force(space, w1)
            w2 = force(space, w2)
            w3 = force(space, w3)
            return parentfn(w1, w2, w3, *extra)
    elif nb_args == 4:
        def proxy(w1, w2, w3, w4, *extra):
            w1 = force(space, w1)
            w2 = force(space, w2)
            w3 = force(space, w3)
            w4 = force(space, w4)
            return parentfn(w1, w2, w3, w4, *extra)
    else:
        raise NotImplementedError("operation %r has arity %d" %
                                  (opname, nb_args))
    return proxy

def Space(*args, **kwds):
    # for now, always make up a wrapped StdObjSpace
    from pypy.objspace import std
    space = std.Space(*args, **kwds)
    patch_space_in_place(space, 'thunk', proxymaker)
    space.resolve_target = lambda w_arg: _force(space, w_arg)
    w___pypy__ = space.getbuiltinmodule("__pypy__")
    space.w_fn_thunk = space.wrap(app_thunk)
    space.setattr(w___pypy__, space.wrap('thunk'),
                  space.w_fn_thunk)
    space.setattr(w___pypy__, space.wrap('is_thunk'),
                  space.wrap(app_is_thunk))
    space.setattr(w___pypy__, space.wrap('become'),
                 space.wrap(app_become))
    space.setattr(w___pypy__, space.wrap('lazy'),
                 space.wrap(app_lazy))
    return space