Author: Maciej Fijalkowski <fij...@gmail.com> Branch: Changeset: r67222:8fd575d74e94 Date: 2013-10-08 17:12 +0200 http://bitbucket.org/pypy/pypy/changeset/8fd575d74e94/
Log: (fijal, arigo) merge fileops2, which brings some sanity (and more featureS) to file implementation in RPython diff --git a/rpython/flowspace/model.py b/rpython/flowspace/model.py --- a/rpython/flowspace/model.py +++ b/rpython/flowspace/model.py @@ -252,6 +252,23 @@ from rpython.translator.tool.graphpage import try_show try_show(self) + def get_graph(self): + import gc + pending = [self] # pending blocks + seen = {self: True, None: True} + for x in pending: + for y in gc.get_referrers(x): + if isinstance(y, FunctionGraph): + return y + elif isinstance(y, Link): + block = y.prevblock + if block not in seen: + pending.append(block) + seen[block] = True + elif isinstance(y, dict): + pending.append(y) # go back from the dict to the real obj + return pending + view = show diff --git a/rpython/flowspace/specialcase.py b/rpython/flowspace/specialcase.py --- a/rpython/flowspace/specialcase.py +++ b/rpython/flowspace/specialcase.py @@ -1,3 +1,4 @@ +import os from rpython.flowspace.model import Constant, const SPECIAL_CASES = {} @@ -37,6 +38,18 @@ return space.frame.do_operation('simple_call', const(isinstance), w_instance, w_type) +@register_flow_sc(open) +def sc_open(space, *args_w): + from rpython.rlib.rfile import create_file + + return space.frame.do_operation("simple_call", const(create_file), *args_w) + +@register_flow_sc(os.tmpfile) +def sc_os_tmpfile(space): + from rpython.rlib.rfile import create_temp_rfile + + return space.frame.do_operation("simple_call", const(create_temp_rfile)) + # _________________________________________________________________________ # a simplified version of the basic printing routines, for RPython programs class StdOutBuffer: diff --git a/rpython/rlib/rfile.py b/rpython/rlib/rfile.py --- a/rpython/rlib/rfile.py +++ b/rpython/rlib/rfile.py @@ -1,55 +1,241 @@ -""" This file makes open() and friends RPython +""" This file makes open() and friends RPython. Note that RFile should not +be used directly and instead it's magically appearing each time you call +python builtin open() """ import os -from rpython.annotator.model import SomeObject, SomeString, SomeInteger -from rpython.rtyper.extregistry import ExtRegistryEntry -from rpython.rtyper.extfunc import register_external +from rpython.rtyper.lltypesystem import rffi, lltype +from rpython.rtyper.tool import rffi_platform as platform +from rpython.translator.tool.cbuild import ExternalCompilationInfo +from rpython.rlib.rarithmetic import r_uint, intmask +from rpython.rlib import rposix +from rpython.rlib.rstring import StringBuilder -class SomeFile(SomeObject): - def method_write(self, s_arg): - assert isinstance(s_arg, SomeString) +eci = ExternalCompilationInfo(includes=['stdio.h', 'unistd.h', 'sys/types.h']) - def method_read(self, s_arg=None): - if s_arg is not None: - assert isinstance(s_arg, SomeInteger) - return SomeString(can_be_None=False) +def llexternal(*args): + return rffi.llexternal(*args, compilation_info=eci) - def method_close(self): - pass +FILE = lltype.Struct('FILE') # opaque type maybe - def method_seek(self, s_arg, s_whence=None): - assert isinstance(s_arg, SomeInteger) - if s_whence is not None: - assert isinstance(s_whence, SomeInteger) +class CConfig(object): + _compilation_info_ = eci - def rtyper_makekey(self): - return self.__class__, + off_t = platform.SimpleType('off_t') - def rtyper_makerepr(self, rtyper): - from rpython.rtyper.lltypesystem.rfile import FileRepr +CC = platform.configure(CConfig) +OFF_T = CC['off_t'] - return FileRepr(rtyper) +c_open = llexternal('fopen', [rffi.CCHARP, rffi.CCHARP], lltype.Ptr(FILE)) +c_close = llexternal('fclose', [lltype.Ptr(FILE)], rffi.INT) +c_write = llexternal('fwrite', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, + lltype.Ptr(FILE)], rffi.SIZE_T) +c_read = llexternal('fread', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, + lltype.Ptr(FILE)], rffi.SIZE_T) +c_feof = llexternal('feof', [lltype.Ptr(FILE)], rffi.INT) +c_ferror = llexternal('ferror', [lltype.Ptr(FILE)], rffi.INT) +c_clearerror = llexternal('clearerr', [lltype.Ptr(FILE)], lltype.Void) +c_fseek = llexternal('fseek', [lltype.Ptr(FILE), rffi.LONG, rffi.INT], + rffi.INT) +c_tmpfile = llexternal('tmpfile', [], lltype.Ptr(FILE)) +c_fileno = llexternal('fileno', [lltype.Ptr(FILE)], rffi.INT) +c_ftell = llexternal('ftell', [lltype.Ptr(FILE)], lltype.Signed) +c_fflush = llexternal('fflush', [lltype.Ptr(FILE)], rffi.INT) +c_ftruncate = llexternal('ftruncate', [rffi.INT, OFF_T], rffi.INT) +c_fgets = llexternal('fgets', [rffi.CCHARP, rffi.INT, lltype.Ptr(FILE)], + rffi.CCHARP) -class FileEntry(ExtRegistryEntry): - _about_ = open +BASE_BUF_SIZE = 4096 +BASE_LINE_SIZE = 100 - def compute_result_annotation(self, s_name, s_mode=None): - assert isinstance(s_name, SomeString) - if s_mode is not None: - assert isinstance(s_mode, SomeString) - return SomeFile() +def create_file(filename, mode="r", buffering=-1): + assert buffering == -1 + assert filename is not None + assert mode is not None + ll_name = rffi.str2charp(filename) + try: + ll_mode = rffi.str2charp(mode) + try: + ll_f = c_open(ll_name, ll_mode) + if not ll_f: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + finally: + lltype.free(ll_mode, flavor='raw') + finally: + lltype.free(ll_name, flavor='raw') + return RFile(ll_f) - def specialize_call(self, hop): - return hop.r_result.rtype_constructor(hop) +def create_temp_rfile(): + res = c_tmpfile() + if not res: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + return RFile(res) -class OSTempfileEntry(ExtRegistryEntry): - _about_ = os.tmpfile +class RFile(object): + def __init__(self, ll_file): + self.ll_file = ll_file - def compute_result_annotation(self): - return SomeFile() + def write(self, value): + assert value is not None + ll_file = self.ll_file + if not ll_file: + raise ValueError("I/O operation on closed file") + assert value is not None + ll_value = rffi.get_nonmovingbuffer(value) + try: + # note that since we got a nonmoving buffer, it is either raw + # or already cannot move, so the arithmetics below are fine + total_bytes = 0 + ll_current = ll_value + while total_bytes < len(value): + bytes = c_write(ll_current, 1, len(value) - r_uint(total_bytes), + ll_file) + if bytes == 0: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + total_bytes += bytes + ll_current = rffi.cast(rffi.CCHARP, + rffi.cast(lltype.Unsigned, ll_value) + + total_bytes) + finally: + rffi.free_nonmovingbuffer(value, ll_value) - def specialize_call(self, hop): - return hop.r_result.rtype_tempfile(hop) + def close(self): + if self.ll_file: + # double close is allowed + res = c_close(self.ll_file) + self.ll_file = lltype.nullptr(FILE) + if res == -1: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + def read(self, size=-1): + # XXX CPython uses a more delicate logic here + ll_file = self.ll_file + if not ll_file: + raise ValueError("I/O operation on closed file") + if size < 0: + # read the entire contents + buf = lltype.malloc(rffi.CCHARP.TO, BASE_BUF_SIZE, flavor='raw') + try: + s = StringBuilder() + while True: + returned_size = c_read(buf, 1, BASE_BUF_SIZE, ll_file) + if returned_size == 0: + if c_feof(ll_file): + # ok, finished + return s.build() + errno = c_ferror(ll_file) + c_clearerror(ll_file) + raise OSError(errno, os.strerror(errno)) + s.append_charpsize(buf, returned_size) + finally: + lltype.free(buf, flavor='raw') + else: + raw_buf, gc_buf = rffi.alloc_buffer(size) + try: + returned_size = c_read(raw_buf, 1, size, ll_file) + if returned_size == 0: + if not c_feof(ll_file): + errno = c_ferror(ll_file) + raise OSError(errno, os.strerror(errno)) + s = rffi.str_from_buffer(raw_buf, gc_buf, size, + rffi.cast(lltype.Signed, returned_size)) + finally: + rffi.keep_buffer_alive_until_here(raw_buf, gc_buf) + return s + + def seek(self, pos, whence=0): + ll_file = self.ll_file + if not ll_file: + raise ValueError("I/O operation on closed file") + res = c_fseek(ll_file, pos, whence) + if res == -1: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + + def fileno(self): + if self.ll_file: + return intmask(c_fileno(self.ll_file)) + raise ValueError("I/O operation on closed file") + + def tell(self): + if self.ll_file: + res = intmask(c_ftell(self.ll_file)) + if res == -1: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + return res + raise ValueError("I/O operation on closed file") + + def flush(self): + if self.ll_file: + res = c_fflush(self.ll_file) + if res != 0: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + return + raise ValueError("I/O operation on closed file") + + def truncate(self, arg=-1): + if self.ll_file: + if arg == -1: + arg = self.tell() + res = c_ftruncate(self.fileno(), arg) + if res == -1: + errno = rposix.get_errno() + raise OSError(errno, os.strerror(errno)) + return + raise ValueError("I/O operation on closed file") + + def __del__(self): + self.close() + + def _readline1(self, raw_buf): + result = c_fgets(raw_buf, BASE_LINE_SIZE, self.ll_file) + if not result: + if c_feof(self.ll_file): # ok + return 0 + errno = c_ferror(self.ll_file) + raise OSError(errno, os.strerror(errno)) + # + # Assume that fgets() works as documented, and additionally + # never writes beyond the final \0, which the CPython + # fileobject.c says appears to be the case everywhere. + # The only case where the buffer was not big enough is the + # case where the buffer is full, ends with \0, and doesn't + # end with \n\0. + strlen = 0 + while raw_buf[strlen] != '\0': + strlen += 1 + if (strlen == BASE_LINE_SIZE - 1 and + raw_buf[BASE_LINE_SIZE - 2] != '\n'): + return -1 # overflow! + # common case + return strlen + + def readline(self): + if self.ll_file: + raw_buf, gc_buf = rffi.alloc_buffer(BASE_LINE_SIZE) + try: + c = self._readline1(raw_buf) + if c >= 0: + return rffi.str_from_buffer(raw_buf, gc_buf, + BASE_LINE_SIZE, c) + # + # this is the rare case: the line is longer than BASE_LINE_SIZE + s = StringBuilder() + while True: + s.append_charpsize(raw_buf, BASE_LINE_SIZE - 1) + c = self._readline1(raw_buf) + if c >= 0: + break + # + s.append_charpsize(raw_buf, c) + return s.build() + finally: + rffi.keep_buffer_alive_until_here(raw_buf, gc_buf) + raise ValueError("I/O operation on closed file") diff --git a/rpython/rlib/test/test_rfile.py b/rpython/rlib/test/test_rfile.py --- a/rpython/rlib/test/test_rfile.py +++ b/rpython/rlib/test/test_rfile.py @@ -78,3 +78,100 @@ f() self.interpret(f, []) + + def test_fileno(self): + fname = str(self.tmpdir.join('file_5')) + + def f(): + f = open(fname, "w") + try: + return f.fileno() + finally: + f.close() + + res = self.interpret(f, []) + assert res > 2 + + def test_tell(self): + fname = str(self.tmpdir.join('file_tell')) + + def f(): + f = open(fname, "w") + f.write("xyz") + try: + return f.tell() + finally: + f.close() + + res = self.interpret(f, []) + assert res == 3 + + def test_flush(self): + fname = str(self.tmpdir.join('file_flush')) + + def f(): + f = open(fname, "w") + f.write("xyz") + f.flush() + f2 = open(fname) + assert f2.read() == "xyz" + f2.close() + f.close() + + self.interpret(f, []) + + def test_truncate(self): + fname = str(self.tmpdir.join('file_trunc')) + + def f(): + f = open(fname, "w") + f.write("xyz") + f.seek(0) + f.truncate(2) + f.close() + f2 = open(fname) + assert f2.read() == "xy" + f2.close() + + f() + self.interpret(f, []) + + +class TestDirect: + def setup_class(cls): + cls.tmpdir = udir.join('test_rfile_direct') + cls.tmpdir.ensure(dir=True) + + def test_readline(self): + fname = str(self.tmpdir.join('file_readline')) + j = 0 + expected = [] + with open(fname, 'w') as f: + for i in range(250): + s = ''.join([chr(32+(k&63)) for k in range(j, j + i)]) + j += 1 + print >> f, s + expected = open(fname).readlines() + expected += ['', ''] + assert len(expected) == 252 + + f = rfile.create_file(fname, 'r') + for j in range(252): + got = f.readline() + assert got == expected[j] + f.close() + + def test_readline_without_eol_at_the_end(self): + fname = str(self.tmpdir.join('file_readline_without_eol_at_the_end')) + for n in [1, 10, 97, 98, 99, 100, 101, 102, 103, 150, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 250]: + s = ''.join([chr(32+(k&63)) for k in range(n)]) + with open(fname, 'wb') as f: + f.write(s) + + f = rfile.create_file(fname, 'r') + got = f.readline() + assert got == s + got = f.readline() + assert got == '' + f.close() diff --git a/rpython/rlib/types.py b/rpython/rlib/types.py --- a/rpython/rlib/types.py +++ b/rpython/rlib/types.py @@ -39,8 +39,12 @@ return model.SomeUnicodeString(no_nul=True) -def str(): - return model.SomeString() +def str(can_be_None=False): + return model.SomeString(can_be_None=can_be_None) + + +def bytearray(): + return model.SomeByteArray() def str0(): diff --git a/rpython/rtyper/lltypesystem/rfile.py b/rpython/rtyper/lltypesystem/rfile.py deleted file mode 100644 --- a/rpython/rtyper/lltypesystem/rfile.py +++ /dev/null @@ -1,195 +0,0 @@ - -import os -from rpython.rlib import rposix -from rpython.rlib.rarithmetic import r_uint -from rpython.annotator import model as annmodel -from rpython.rtyper.rtyper import Repr -from rpython.rlib.rstring import StringBuilder -from rpython.rtyper.lltypesystem import lltype, rffi, llmemory -from rpython.rtyper.lltypesystem.rstr import string_repr, STR -from rpython.translator.tool.cbuild import ExternalCompilationInfo -from rpython.rtyper.annlowlevel import hlstr -from rpython.rtyper.lltypesystem.lloperation import llop - -FILE = lltype.Struct('FILE') # opaque type maybe -FILE_WRAPPER = lltype.GcStruct("FileWrapper", ('file', lltype.Ptr(FILE))) - -eci = ExternalCompilationInfo(includes=['stdio.h']) - -def llexternal(*args): - return rffi.llexternal(*args, compilation_info=eci) - -c_open = llexternal('fopen', [rffi.CCHARP, rffi.CCHARP], lltype.Ptr(FILE)) -c_close = llexternal('fclose', [lltype.Ptr(FILE)], rffi.INT) -c_write = llexternal('fwrite', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, - lltype.Ptr(FILE)], rffi.SIZE_T) -c_read = llexternal('fread', [rffi.CCHARP, rffi.SIZE_T, rffi.SIZE_T, - lltype.Ptr(FILE)], rffi.SIZE_T) -c_feof = llexternal('feof', [lltype.Ptr(FILE)], rffi.INT) -c_ferror = llexternal('ferror', [lltype.Ptr(FILE)], rffi.INT) -c_clearerror = llexternal('clearerr', [lltype.Ptr(FILE)], lltype.Void) -c_fseek = llexternal('fseek', [lltype.Ptr(FILE), rffi.LONG, rffi.INT], - rffi.INT) -c_tmpfile = llexternal('tmpfile', [], lltype.Ptr(FILE)) - -def ll_open(name, mode): - file_wrapper = lltype.malloc(FILE_WRAPPER) - ll_name = rffi.str2charp(name) - ll_mode = rffi.str2charp(mode) - try: - ll_f = c_open(ll_name, ll_mode) - if not ll_f: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - file_wrapper.file = ll_f - finally: - lltype.free(ll_name, flavor='raw') - lltype.free(ll_mode, flavor='raw') - return file_wrapper - -def ll_tmpfile(): - file_wrapper = lltype.malloc(FILE_WRAPPER) - res = c_tmpfile() - if not res: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - file_wrapper.file = res - return file_wrapper - -def ll_write(file_wrapper, value): - ll_file = file_wrapper.file - if not ll_file: - raise ValueError("I/O operation on closed file") - value = hlstr(value) - assert value is not None - ll_value = rffi.get_nonmovingbuffer(value) - try: - # note that since we got a nonmoving buffer, it is either raw - # or already cannot move, so the arithmetics below are fine - total_bytes = 0 - ll_current = ll_value - while total_bytes < len(value): - bytes = c_write(ll_current, 1, len(value) - r_uint(total_bytes), - ll_file) - if bytes == 0: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - total_bytes += bytes - ll_current = rffi.cast(rffi.CCHARP, - rffi.cast(lltype.Unsigned, ll_value) + - total_bytes) - finally: - rffi.free_nonmovingbuffer(value, ll_value) - -BASE_BUF_SIZE = 4096 - -def ll_read(file_wrapper, size): - ll_file = file_wrapper.file - if not ll_file: - raise ValueError("I/O operation on closed file") - if size < 0: - # read the entire contents - buf = lltype.malloc(rffi.CCHARP.TO, BASE_BUF_SIZE, flavor='raw') - try: - s = StringBuilder() - while True: - returned_size = c_read(buf, 1, BASE_BUF_SIZE, ll_file) - if returned_size == 0: - if c_feof(ll_file): - # ok, finished - return s.build() - errno = c_ferror(ll_file) - c_clearerror(ll_file) - raise OSError(errno, os.strerror(errno)) - s.append_charpsize(buf, returned_size) - finally: - lltype.free(buf, flavor='raw') - else: - raw_buf, gc_buf = rffi.alloc_buffer(size) - try: - returned_size = c_read(raw_buf, 1, size, ll_file) - if returned_size == 0: - if not c_feof(ll_file): - errno = c_ferror(ll_file) - raise OSError(errno, os.strerror(errno)) - s = rffi.str_from_buffer(raw_buf, gc_buf, size, - rffi.cast(lltype.Signed, returned_size)) - finally: - rffi.keep_buffer_alive_until_here(raw_buf, gc_buf) - return s -def ll_seek(file_wrapper, pos, whence): - ll_file = file_wrapper.file - if not ll_file: - raise ValueError("I/O operation on closed file") - res = c_fseek(ll_file, pos, whence) - if res == -1: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - -def ll_close(file_wrapper): - if file_wrapper.file: - # double close is allowed - res = c_close(file_wrapper.file) - file_wrapper.file = lltype.nullptr(FILE) - if res == -1: - errno = rposix.get_errno() - raise OSError(errno, os.strerror(errno)) - -class FileRepr(Repr): - lowleveltype = lltype.Ptr(FILE_WRAPPER) - - def __init__(self, typer): - Repr.__init__(self) - - def rtype_constructor(self, hop): - repr = hop.rtyper.getrepr(annmodel.SomeString()) - arg_0 = hop.inputarg(repr, 0) - if len(hop.args_v) == 1: - arg_1 = hop.inputconst(string_repr, "r") - else: - arg_1 = hop.inputarg(repr, 1) - hop.exception_is_here() - open = hop.rtyper.getannmixlevel().delayedfunction( - ll_open, [annmodel.SomeString()] * 2, - annmodel.SomePtr(self.lowleveltype)) - v_open = hop.inputconst(lltype.typeOf(open), open) - return hop.genop('direct_call', [v_open, arg_0, arg_1], - resulttype=self) - - def rtype_tempfile(self, hop): - tmpfile = hop.rtyper.getannmixlevel().delayedfunction( - ll_tmpfile, [], annmodel.SomePtr(self.lowleveltype)) - v_tmpfile = hop.inputconst(lltype.typeOf(tmpfile), tmpfile) - hop.exception_is_here() - return hop.genop('direct_call', [v_tmpfile], resulttype=self) - - - def rtype_method_write(self, hop): - args_v = hop.inputargs(self, string_repr) - hop.exception_is_here() - return hop.gendirectcall(ll_write, *args_v) - - def rtype_method_close(self, hop): - r_self = hop.inputarg(self, 0) - hop.exception_is_here() - return hop.gendirectcall(ll_close, r_self) - - def rtype_method_read(self, hop): - r_self = hop.inputarg(self, 0) - if len(hop.args_v) != 2: - arg_1 = hop.inputconst(lltype.Signed, -1) - else: - arg_1 = hop.inputarg(lltype.Signed, 1) - hop.exception_is_here() - return hop.gendirectcall(ll_read, r_self, arg_1) - - def rtype_method_seek(self, hop): - r_self = hop.inputarg(self, 0) - arg_1 = hop.inputarg(lltype.Signed, 1) - if len(hop.args_v) != 3: - arg_2 = hop.inputconst(lltype.Signed, os.SEEK_SET) - else: - arg_2 = hop.inputarg(lltype.Signed, 2) - hop.exception_is_here() - return hop.gendirectcall(ll_seek, r_self, arg_1, arg_2) - diff --git a/rpython/rtyper/module/ll_os.py b/rpython/rtyper/module/ll_os.py --- a/rpython/rtyper/module/ll_os.py +++ b/rpython/rtyper/module/ll_os.py @@ -1685,7 +1685,7 @@ def tmpnam_llimpl(): return rffi.charp2str(os_tmpnam(lltype.nullptr(rffi.CCHARP.TO))) - return extdef([], None, llimpl=tmpnam_llimpl, + return extdef([], str, llimpl=tmpnam_llimpl, export_name="ll_os.ll_os_tmpnam") # --------------------------- os.stat & variants --------------------------- diff --git a/rpython/translator/tool/graphpage.py b/rpython/translator/tool/graphpage.py --- a/rpython/translator/tool/graphpage.py +++ b/rpython/translator/tool/graphpage.py @@ -200,7 +200,7 @@ dotgen.emit_edge(nameof(cdef), nameof(prevcdef), color="red") prevcdef = cdef cdef = cdef.basedef - + self.source = dotgen.generate(target=None) def followlink(self, name): @@ -224,7 +224,7 @@ dotgen.emit('mclimit=15.0') self.do_compute(dotgen, *args, **kwds) - + self.source = dotgen.generate(target=None) # link the function names to the individual flow graphs @@ -264,7 +264,7 @@ data = self.labelof(classdef, classdef.shortname) dotgen.emit_node(nameof(classdef), label=data, shape="box") dotgen.emit_edge(nameof(classdef.basedef), nameof(classdef)) - + def labelof(self, obj, objname): name = objname i = 1 @@ -409,22 +409,11 @@ elif isinstance(obj, Link): try_show(obj.prevblock) elif isinstance(obj, Block): - import gc - pending = [obj] # pending blocks - seen = {obj: True, None: True} - for x in pending: - for y in gc.get_referrers(x): - if isinstance(y, FunctionGraph): - y.show() - return - elif isinstance(y, Link): - block = y.prevblock - if block not in seen: - pending.append(block) - seen[block] = True - elif isinstance(y, dict): - pending.append(y) # go back from the dict to the real obj - graph = IncompleteGraph(pending) + graph = obj.get_graph() + if isinstance(graph, FunctionGraph): + graph.show() + return + graph = IncompleteGraph(graph) SingleGraphPage(graph).display() else: raise TypeError("try_show(%r object)" % (type(obj).__name__,)) @@ -449,7 +438,7 @@ seen[block] = True return pending else: - raise TypeError("try_get_functiongraph(%r object)" % (type(obj).__name__,)) + raise TypeError("try_get_functiongraph(%r object)" % (type(obj).__name__,)) class IncompleteGraph: name = '(incomplete graph)' _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit