The attached script is an attempt to get __str__ to work in rpython.
After annotation, it looks for str(someinstance), rewriting the block to call 
__str__.
Then the modified blocks are fed back into the annotator.

It seems to work. (And now i know a lot more about the internals of the 
translation process...)

I had a think about over-riding consider_op_str. It would seem natural for this 
code
to go there, but the annotator is not really set up to handle rewriting.

Why do i not need to manually call the flow operation on the __str__ ? (see the 
flow_str function) 
It seems the annotator somehow must re-trigger the flowing operation when it
finds that __str__ is being called.

thanks,

Simon.
#!/usr/bin/env python

import sys

from pypy.translator.translator import TranslationContext
from pypy.translator import driver as _driver
from pypy.objspace.flow import FlowObjSpace
from pypy.objspace.flow.model import Constant, Variable, SpaceOperation
from pypy.annotation.classdef import ClassDef
from pypy.annotation.model import SomeString
from pypy.tool.error import AnnotatorError


def dump_blocks(annotator):
    print '-'*60
    blocks = annotator.annotated.keys()
    for block in blocks:
        print
        print block
        for op in block.operations:
            print '\t', op
            

DEFAULTS = {
  'translation.backend': None,
  'translation.type_system': None,
  'translation.verbose': True,
}

def flow_str(translator, cls):
    space = FlowObjSpace(translator.flowconfig)
    graph = space.build_flow(cls.__str__)
    translator._prebuilt_graphs[cls.__str__] = graph
    

def make(ep, verbose=False):

    driver = _driver.TranslationDriver(overrides=DEFAULTS)
    driver.setup(ep, None)
    config = driver.config
    
    translator = driver.translator
    
    driver.annotate()
    annotator = translator.annotator

    if verbose:
        dump_blocks(annotator)

    #
    # Rewrite this op, when v1 is SomeInstance with __str__ method:
    #
    # v2 = str(v1)
    # ---->
    # v22 = getattr(v1, ('__str__'))
    # v2 = simple_call(v22)
    # 

    not_none = []
    blocks = annotator.annotated.keys()
    print "start rewrite... found %d blocks" % len(blocks)
    dirty = []
    for block in blocks:
        ops = block.operations
        idx = 0
        while idx < len(ops):
            assert type(ops)==list, (type(ops), type(block), ops)
            op = ops[idx]
            increment = 1
            if op.opname == 'str':
                args = op.args
                assert len(args)==1
                arg = args[0]
                tp = annotator.gettype(arg)
                if isinstance(tp, ClassDef):
                    classdef = tp
                    classdesc = classdef.classdesc
                    all_ns = [cd.classdesc.classdict for cd in classdef.getmro()]
                    found = [ns for ns in all_ns if '__str__' in ns]
                    if found:
                        arg0 = arg
                        arg1 = Constant('__str__')
                        v = Variable()
                        newop = SpaceOperation('getattr', [arg0, arg1], v)
                        ops.insert(idx, newop)
                        newop = SpaceOperation('simple_call', [v], op.result)
                        not_none.append(op.result)
                        # v was already annotated as SomeString, so if __str__ does not return
                        # SomeString (or None) then the annotation below will fail.
                        ops[idx+1]=newop
                        increment = 2
                        dirty.append((annotator.annotated[block], block))
            idx += increment
    
    print "rewrote %d blocks" % len(dirty)
    if verbose:
        dump_blocks(annotator)
    
    for graph, block in dirty:
        annotator.processblock(graph, block)
    annotator.complete()
    
    # check that __str__ does not return None
    for v in not_none:
        tp = annotator.binding(v)
        print tp
        if not isinstance(tp, SomeString) or tp.can_be_none():
            raise AnnotatorError("__str__ must return SomeString (not None), got %s instead" % tp)
    
    driver.rtype_lltype()
    driver.source_c()
    path = driver.compile_c()

    return path
    



_______________________________________________
[email protected]
http://codespeak.net/mailman/listinfo/pypy-dev

Reply via email to