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