Author: Armin Rigo <[email protected]>
Branch: stmgc-c7
Changeset: r70768:a76fc199431f
Date: 2014-04-19 13:48 +0200
http://bitbucket.org/pypy/pypy/changeset/a76fc199431f/

Log:    in-progress

diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -44,9 +44,19 @@
     return func_with_new_name(opimpl, "opcode_impl_for_%s" % operationname)
 
 
-stmonly_jitdriver = jit.JitDriver(greens=[], reds=['next_instr', 'ec',
-                                                   'self', 'co_code'],
-                                  stm_do_transaction_breaks=True)
+# ____________________________________________________________
+
+class PyPyJitDriver(jit.JitDriver):
+    reds = ['frame', 'ec']
+    greens = ['next_instr', 'is_being_profiled', 'pycode']
+    virtualizables = ['frame']
+    stm_do_transaction_breaks = True
+    is_main_for_pypy = True   # XXX temporary: turning 'greens' into a string
+                              # is hard-coded in C code.  Don't change 'greens'
+
+stmonly_jitdriver = PyPyJitDriver()
+
+# ____________________________________________________________
 
 opcodedesc = bytecode_spec.opcodedesc
 HAVE_ARGUMENT = bytecode_spec.HAVE_ARGUMENT
@@ -61,6 +71,7 @@
         # For the sequel, force 'next_instr' to be unsigned for performance
         next_instr = r_uint(next_instr)
         co_code = pycode.co_code
+        rstm.push_marker(intmask(next_instr) * 2 + 1, pycode)
 
         try:
             while True:
@@ -71,8 +82,11 @@
                         self=self, co_code=co_code,
                         next_instr=next_instr, ec=ec)
                 next_instr = self.handle_bytecode(co_code, next_instr, ec)
+                rstm.update_marker_num(intmask(next_instr) * 2 + 1)
         except ExitFrame:
             return self.popvalue()
+        finally:
+            rstm.pop_marker()
 
     def handle_bytecode(self, co_code, next_instr, ec):
         try:
@@ -467,6 +481,8 @@
                                       opcodedesc.LOAD_FAST.index):
                         return next_instr
 
+            rstm.update_marker_num(intmask(next_instr) * 2 + 1)
+
     @jit.unroll_safe
     def unrollstack(self, unroller_kind):
         while self.blockstack_non_empty():
diff --git a/pypy/module/pypyjit/interp_jit.py 
b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -12,6 +12,7 @@
 from pypy.interpreter.pycode import CO_GENERATOR
 from pypy.interpreter.pyframe import PyFrame
 from pypy.interpreter.pyopcode import ExitFrame, Yield
+from pypy.interpreter.pyopcode import PyPyJitDriver
 from opcode import opmap
 
 
@@ -36,16 +37,10 @@
 def should_unroll_one_iteration(next_instr, is_being_profiled, bytecode):
     return (bytecode.co_flags & CO_GENERATOR) != 0
 
-class PyPyJitDriver(JitDriver):
-    reds = ['frame', 'ec']
-    greens = ['next_instr', 'is_being_profiled', 'pycode']
-    virtualizables = ['frame']
-
 pypyjitdriver = PyPyJitDriver(get_printable_location = get_printable_location,
                               should_unroll_one_iteration =
                               should_unroll_one_iteration,
-                              name='pypyjit',
-                              stm_do_transaction_breaks=True)
+                              name='pypyjit')
 
 class __extend__(PyFrame):
 
diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py
--- a/rpython/rlib/jit.py
+++ b/rpython/rlib/jit.py
@@ -490,7 +490,7 @@
                  get_printable_location=None, confirm_enter_jit=None,
                  can_never_inline=None, should_unroll_one_iteration=None,
                  name='jitdriver', check_untranslated=True,
-                 stm_do_transaction_breaks=False):
+                 stm_do_transaction_breaks=None):
         if greens is not None:
             self.greens = greens
         self.name = name
@@ -526,7 +526,8 @@
         self.can_never_inline = can_never_inline
         self.should_unroll_one_iteration = should_unroll_one_iteration
         self.check_untranslated = check_untranslated
-        self.stm_do_transaction_breaks = stm_do_transaction_breaks
+        if stm_do_transaction_breaks is not None:
+            self.stm_do_transaction_breaks = stm_do_transaction_breaks
 
     def _freeze_(self):
         return True
diff --git a/rpython/rlib/rstm.py b/rpython/rlib/rstm.py
--- a/rpython/rlib/rstm.py
+++ b/rpython/rlib/rstm.py
@@ -122,6 +122,16 @@
     invoke_around_extcall(before_external_call, after_external_call,
                           enter_callback_call, leave_callback_call)
 
[email protected](1)
+def push_marker(odd_num, object):
+    llop.stm_push_marker(lltype.Void, odd_num, object)
+
+def update_marker_num(odd_num):
+    llop.stm_update_marker_num(lltype.Void, odd_num)
+
+def pop_marker():
+    llop.stm_pop_marker(lltype.Void)
+
 # ____________________________________________________________
 
 def make_perform_transaction(func, CONTAINERP):
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -451,6 +451,12 @@
     'stm_ignored_start':      LLOp(canrun=True),
     'stm_ignored_stop':       LLOp(canrun=True),
 
+    'stm_push_marker':        LLOp(canrun=True),
+    'stm_update_marker_num':  LLOp(canrun=True),
+    'stm_pop_marker':         LLOp(canrun=True),
+    'stm_expand_marker':      LLOp(),
+    'stm_setup_expand_marker_for_pypy': LLOp(),
+
 ##    'stm_allocate_nonmovable_int_adr': LLOp(sideeffects=False, 
canmallocgc=True),
 ##    'stm_become_inevitable':  LLOp(canmallocgc=True),
 ##    'stm_stop_all_other_threads': LLOp(canmallocgc=True),
diff --git a/rpython/rtyper/lltypesystem/opimpl.py 
b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -680,8 +680,14 @@
 def op_stm_ignored_stop():
     pass
 
-def op_stm_ptr_eq(x, y):
-    return op_ptr_eq(x, y)
+def op_stm_push_marker(odd_num, object):
+    pass
+
+def op_stm_update_marker_num(odd_num):
+    pass
+
+def op_stm_pop_marker():
+    pass
 
 def op_stm_get_tid(x):
     raise NotImplementedError
diff --git a/rpython/translator/c/genc.py b/rpython/translator/c/genc.py
--- a/rpython/translator/c/genc.py
+++ b/rpython/translator/c/genc.py
@@ -881,46 +881,10 @@
             print >> f, '\t%d,' % (i,)
     print >> f, '\t-1'
     print >> f, '};'
-    print >> f, '''
-void pypy_stm_setup_prebuilt(void)
-{
-    object_t **pp = rpy_prebuilt;
-    long *ph = rpy_prebuilt_hashes;
-    int i = 0;
-    int *wri = weakref_indices;
-    for ( ; *pp; pp++, ph++, i++) {
-        if (i == *wri) {
-            *pp = stm_setup_prebuilt_weakref(*pp);
-            wri++;
-        }
-        else {
-            *pp = stm_setup_prebuilt(*pp);
-        }
-        stm_set_prebuilt_identityhash(*pp, *ph);
-    }
-
-    object_t ***cur = (object_t ***)
-       pypy_g_rpython_memory_gctypelayout_GCData.gcd_inst_static_root_start;
-    object_t ***end = (object_t ***)
-       pypy_g_rpython_memory_gctypelayout_GCData.gcd_inst_static_root_nongcend;
-    for ( ; cur != end; cur++) {
-        **cur = stm_setup_prebuilt(**cur);
-    }
-}
-
-void pypy_stm_register_thread_local(void)
-{
-    stm_register_thread_local(&stm_thread_local);
-    stm_thread_local.mem_clear_on_abort = (char *)&pypy_g_ExcData;
-    stm_thread_local.mem_bytes_to_clear_on_abort = sizeof(pypy_g_ExcData);
-}
-
-void pypy_stm_unregister_thread_local(void)
-{
-    stm_flush_timing(&stm_thread_local, 1);  // XXX temporary
-    stm_unregister_thread_local(&stm_thread_local);
-}
-'''
+    print >> f
+    print >> f, '#include "preimpl.h"'
+    print >> f, '#include "src/rtyper.h"'
+    print >> f, '#include "src_stm/extracode.h"'
 
 def commondefs(defines):
     from rpython.rlib.rarithmetic import LONG_BIT, LONGLONG_BIT
diff --git a/rpython/translator/stm/funcgen.py 
b/rpython/translator/stm/funcgen.py
--- a/rpython/translator/stm/funcgen.py
+++ b/rpython/translator/stm/funcgen.py
@@ -207,126 +207,30 @@
     return '%s = (%s)&stm_thread_local.shadowstack;' % (
         result, cdecl(funcgen.lltypename(op.result), ''))
 
+def stm_push_marker(funcgen, op):
+    arg0 = funcgen.expr(op.args[0])
+    arg1 = funcgen.expr(op.args[1])
+    return 'STM_PUSH_MARKER(stm_thread_local, %s, %s);' % (arg0, arg1)
 
-##def stm_initialize(funcgen, op):
-##    return '''stm_initialize();
-##    stm_clear_on_abort(&pypy_g_ExcData, sizeof(pypy_g_ExcData));
-##    '''
+def stm_update_marker_num(funcgen, op):
+    arg0 = funcgen.expr(op.args[0])
+    return 'STM_UPDATE_MARKER_NUM(stm_thread_local, %s);' % (arg0,)
 
-##def stm_finalize(funcgen, op):
-##    return 'stm_finalize();'
+def stm_pop_marker(funcgen, op):
+    return 'STM_POP_MARKER(stm_thread_local);'
 
-##def stm_barrier(funcgen, op):
-##    category_change = op.args[0].value
-##    # XXX: how to unify the stm_barrier llop generation in
-##    #      writebarrier.py and threadlocalref.py?
-##    if isinstance(category_change, str):
-##        frm, middle, to = category_change
-##    else: # rstr
-##        frm, middle, to = (category_change.chars[0],
-##                           category_change.chars[1],
-##                           category_change.chars[2])
-##    assert middle == '2'
-##    assert frm < to
-##    if to == 'W':
-##        if frm >= 'V':
-##            funcname = 'stm_repeat_write_barrier'
-##        else:
-##            funcname = 'stm_write_barrier'
-##    elif to == 'V':
-##        funcname = 'stm_write_barrier_noptr'
-##    elif to == 'R':
-##        if frm >= 'Q':
-##            funcname = 'stm_repeat_read_barrier'
-##        else:
-##            funcname = 'stm_read_barrier'
-##    elif to == 'I':
-##        funcname = 'stm_immut_read_barrier'
-##    else:
-##        raise AssertionError(category_change)
-##    assert op.args[1].concretetype == op.result.concretetype
-##    arg = funcgen.expr(op.args[1])
-##    result = funcgen.expr(op.result)
-##    return '%s = (%s)%s((gcptr)%s);' % (
-##        result, cdecl(funcgen.lltypename(op.result), ''),
-##        funcname, arg)
+def stm_expand_marker(funcgen, op):
+    result = funcgen.expr(op.result)
+    return '%s = _stm_expand_marker();' % (result,)
 
-##def stm_ptr_eq(funcgen, op):
-##    args = [funcgen.expr(v) for v in op.args]
-##    result = funcgen.expr(op.result)
-##    # check for prebuilt arguments
-##    for i, j in [(0, 1), (1, 0)]:
-##        if isinstance(op.args[j], Constant):
-##            if op.args[j].value:     # non-NULL
-##                return ('%s = stm_pointer_equal_prebuilt((gcptr)%s, 
(gcptr)%s);'
-##                        % (result, args[i], args[j]))
-##            else:
-##                # this case might be unreachable, but better safe than sorry
-##                return '%s = (%s == NULL);' % (result, args[i])
-##    #
-##    return '%s = stm_pointer_equal((gcptr)%s, (gcptr)%s);' % (
-##        result, args[0], args[1])
-
-##def stm_stop_all_other_threads(funcgen, op):
-##    return 'stm_stop_all_other_threads();'
-
-##def stm_partial_commit_and_resume_other_threads(funcgen, op):
-##    return 'stm_partial_commit_and_resume_other_threads();'
-
-##def stm_get_adr_of_nursery_current(funcgen, op):
-##    result = funcgen.expr(op.result)
-##    return '%s = (%s)&stm_nursery_current;' % (
-##        result, cdecl(funcgen.lltypename(op.result), ''))
-
-##def stm_get_adr_of_nursery_nextlimit(funcgen, op):
-##    result = funcgen.expr(op.result)
-##    return '%s = (%s)&stm_nursery_nextlimit;' % (
-##        result, cdecl(funcgen.lltypename(op.result), ''))
-
-##def stm_get_adr_of_active(funcgen, op):
-##    result = funcgen.expr(op.result)
-##    return '%s = (%s)&stm_active;' % (
-##        result, cdecl(funcgen.lltypename(op.result), ''))
-
-##def stm_get_adr_of_private_rev_num(funcgen, op):
-##    result = funcgen.expr(op.result)
-##    return '%s = (%s)&stm_private_rev_num;' % (
-##        result, cdecl(funcgen.lltypename(op.result), ''))
-
-##def stm_get_adr_of_read_barrier_cache(funcgen, op):
-##    result = funcgen.expr(op.result)
-##    return '%s = (%s)&stm_read_barrier_cache;' % (
-##        result, cdecl(funcgen.lltypename(op.result), ''))
-    
-    
-##def stm_weakref_allocate(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    arg1 = funcgen.expr(op.args[1])
-##    arg2 = funcgen.expr(op.args[2])
-##    result = funcgen.expr(op.result)
-##    return '%s = stm_weakref_allocate(%s, %s, %s);' % (result, arg0, 
-##                                                       arg1, arg2)
-
-##def stm_allocate_nonmovable_int_adr(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    result = funcgen.expr(op.result)
-##    return '%s = stm_allocate_public_integer_address(%s);' % (result, arg0)
-
-##def stm_get_tid(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    result = funcgen.expr(op.result)
-##    return '%s = ((struct rpyobj_s*)%s)->tid;' % (result, arg0)
-
-##def stm_enter_callback_call(funcgen, op):
-##    result = funcgen.expr(op.result)
-##    return '%s = stm_enter_callback_call();' % (result,)
-
-##def stm_leave_callback_call(funcgen, op):
-##    arg0 = funcgen.expr(op.args[0])
-##    return 'stm_leave_callback_call(%s);' % (arg0,)
-
-##def stm_minor_collect(funcgen, op):
-##    return 'stm_minor_collect();'
-
-##def stm_major_collect(funcgen, op):
-##    return 'stm_major_collect();'
+def stm_setup_expand_marker_for_pypy(funcgen, op):
+    # hack hack hack
+    node = funcgen.db.gettypedefnode(op.args[0].concretetype.TO)
+    typename = funcgen.db.gettype(op.args[0].concretetype.TO)
+    names = [''.join(arg.value.chars) for arg in op.args[1:]]
+    names = [node.c_struct_field_name('inst_' + name) for name in names]
+    offsets = ['offsetof(%s, %s)' % (cdecl(typename, ''), name)
+               for name in names]
+    assert len(offsets) == 4
+    return 'pypy_stm_setup_expand_marker(%s, %s, %s, %s);' % (
+        offsets[0], offsets[1], offsets[2], offsets[3])
diff --git a/rpython/translator/stm/src_stm/extracode.h 
b/rpython/translator/stm/src_stm/extracode.h
new file mode 100644
--- /dev/null
+++ b/rpython/translator/stm/src_stm/extracode.h
@@ -0,0 +1,115 @@
+
+void pypy_stm_setup_prebuilt(void)
+{
+    object_t **pp = rpy_prebuilt;
+    long *ph = rpy_prebuilt_hashes;
+    int i = 0;
+    int *wri = weakref_indices;
+    for ( ; *pp; pp++, ph++, i++) {
+        if (i == *wri) {
+            *pp = stm_setup_prebuilt_weakref(*pp);
+            wri++;
+        }
+        else {
+            *pp = stm_setup_prebuilt(*pp);
+        }
+        stm_set_prebuilt_identityhash(*pp, *ph);
+    }
+
+    object_t ***cur = (object_t ***)
+       pypy_g_rpython_memory_gctypelayout_GCData.gcd_inst_static_root_start;
+    object_t ***end = (object_t ***)
+       pypy_g_rpython_memory_gctypelayout_GCData.gcd_inst_static_root_nongcend;
+    for ( ; cur != end; cur++) {
+        **cur = stm_setup_prebuilt(**cur);
+    }
+}
+
+void pypy_stm_register_thread_local(void)
+{
+    stm_register_thread_local(&stm_thread_local);
+    stm_thread_local.mem_clear_on_abort = (char *)&pypy_g_ExcData;
+    stm_thread_local.mem_bytes_to_clear_on_abort = sizeof(pypy_g_ExcData);
+}
+
+void pypy_stm_unregister_thread_local(void)
+{
+    stm_flush_timing(&stm_thread_local, 1);  // XXX temporary
+    stm_unregister_thread_local(&stm_thread_local);
+}
+
+
+/************************************************************/
+/*** HACK: hard-coded logic to expand the marker into     ***/
+/*** a string, suitable for running in PyPy               ***/
+
+static long g_co_filename_ofs;
+static long g_co_name_ofs;
+static long g_co_firstlineno_ofs;
+static long g_co_lnotab_ofs;
+
+static char *_RPyString_AsString_Real(RPyString *obj)
+{
+    stm_char *src = _RPyString_AsString(obj);
+    return STM_SEGMENT->segment_base + (uintptr_t)src;
+}
+
+static void _stm_expand_marker_for_pypy(uintptr_t odd_number,
+                                        object_t *following_object,
+                                        char *outputbuf, size_t outputbufsize)
+{
+    RPyString *co_filename =
+        *(RPyString **)(((char *)following_object) + g_co_filename_ofs);
+    RPyString *co_name =
+        *(RPyString **)(((char *)following_object) + g_co_name_ofs);
+    long co_firstlineno =
+        *(long *)(((char *)following_object) + g_co_firstlineno_ofs);
+    RPyString *co_lnotab =
+        *(RPyString **)(((char *)following_object) + g_co_lnotab_ofs);
+
+    char *ntrunc = "", *fntrunc = "";
+
+    long remaining = outputbufsize - 32;
+    long nlen = RPyString_Size(co_name);
+    char *name = _RPyString_AsString_Real(co_name);
+    if (nlen > remaining / 2) {
+        nlen = remaining / 2;
+        ntrunc = "...";
+    }
+    remaining -= nlen;
+
+    long fnlen = RPyString_Size(co_filename);
+    char *fn = _RPyString_AsString_Real(co_filename);
+    if (fnlen > remaining) {
+        fn += (fnlen - remaining);
+        fnlen = remaining;
+        fntrunc = "...";
+    }
+
+    long tablen = RPyString_Size(co_lnotab);
+    char *tab = _RPyString_AsString_Real(co_lnotab);
+    uintptr_t next_instr = odd_number >> 1;
+    long line = co_firstlineno;
+    uintptr_t i, addr = 0;
+    for (i = 0; i < tablen; i += 2) {
+        addr += ((unsigned char *)tab)[i];
+        if (addr > next_instr)
+            break;
+        line += ((unsigned char *)tab)[i + 1];
+    }
+
+    snprintf(outputbuf, outputbufsize, "File \"%s%.*s\", line %ld, in %.*s%s",
+             fntrunc, (int)fnlen, fn, line, (int)nlen, name, ntrunc);
+}
+
+void pypy_stm_setup_expand_marker(long co_filename_ofs,
+                                  long co_name_ofs,
+                                  long co_firstlineno_ofs,
+                                  long co_lnotab_ofs)
+{
+    g_co_filename_ofs = co_filename_ofs;
+    g_co_name_ofs = co_name_ofs;
+    g_co_firstlineno_ofs = co_firstlineno_ofs;
+    g_co_lnotab_ofs = co_lnotab_ofs;
+    stmcb_expand_marker = _stm_expand_marker_for_pypy;
+}
diff --git a/rpython/translator/stm/src_stm/stmgcintf.h 
b/rpython/translator/stm/src_stm/stmgcintf.h
--- a/rpython/translator/stm/src_stm/stmgcintf.h
+++ b/rpython/translator/stm/src_stm/stmgcintf.h
@@ -26,6 +26,11 @@
 void _pypy_stm_become_inevitable(const char *);
 void pypy_stm_become_globally_unique_transaction(void);
 
+void pypy_stm_setup_expand_marker(long co_filename_ofs,
+                                  long co_name_ofs,
+                                  long co_firstlineno_ofs,
+                                  long co_lnotab_ofs);
+
 
 static inline void pypy_stm_become_inevitable(const char *msg)
 {
diff --git a/rpython/translator/stm/test/test_ztranslated.py 
b/rpython/translator/stm/test/test_ztranslated.py
--- a/rpython/translator/stm/test/test_ztranslated.py
+++ b/rpython/translator/stm/test/test_ztranslated.py
@@ -240,6 +240,7 @@
         assert 'ok\n' in data
 
     def test_abort_info(self):
+        py.test.skip("goes away")
         class Parent(object):
             pass
         class Foobar(Parent):
@@ -492,3 +493,60 @@
         data = cbuilder.cmdexec('')
         assert '-84\n' in data
         assert '-1298\n' in data
+
+    def test_pypy_marker(self):
+        class PyCode(object):
+            def __init__(self, co_filename, co_name,
+                         co_firstlineno, co_lnotab):
+                self.co_filename = co_filename
+                self.co_name = co_name
+                self.co_firstlineno = co_firstlineno
+                self.co_lnotab = co_lnotab
+
+        def run_interpreter(pycode):
+            print 'starting', pycode.co_name
+            rstm.push_marker(1, pycode)
+            for i in range(10):
+                p = llop.stm_expand_marker(rffi.CCHARP)
+                print rffi.charp2str(p)
+                rstm.update_marker_num(i * 2 + 1)
+            rstm.pop_marker()
+            print 'stopping', pycode.co_name
+
+        def main(argv):
+            pycode1 = PyCode("/tmp/foobar.py", "baz", 40, "\x00\x01\x05\x01")
+            pycode2 = PyCode("/tmp/foobaz.py", "bar", 70, "\x00\x01\x04\x02")
+            llop.stm_setup_expand_marker_for_pypy(
+                lltype.Void, pycode1,
+                "co_filename", "co_name", "co_firstlineno", "co_lnotab")
+
+            run_interpreter(pycode1)
+            run_interpreter(pycode2)
+            return 0
+
+        t, cbuilder = self.compile(main)
+        data = cbuilder.cmdexec('')
+        assert ('starting baz\n'
+                'File "/tmp/foobar.py", line 41, in baz\n'
+                'File "/tmp/foobar.py", line 41, in baz\n'
+                'File "/tmp/foobar.py", line 41, in baz\n'
+                'File "/tmp/foobar.py", line 41, in baz\n'
+                'File "/tmp/foobar.py", line 41, in baz\n'
+                'File "/tmp/foobar.py", line 42, in baz\n'
+                'File "/tmp/foobar.py", line 42, in baz\n'
+                'File "/tmp/foobar.py", line 42, in baz\n'
+                'File "/tmp/foobar.py", line 42, in baz\n'
+                'File "/tmp/foobar.py", line 42, in baz\n'
+                'stopping baz\n') in data
+        assert ('starting bar\n'
+                'File "/tmp/foobaz.py", line 71, in bar\n'
+                'File "/tmp/foobaz.py", line 71, in bar\n'
+                'File "/tmp/foobaz.py", line 71, in bar\n'
+                'File "/tmp/foobaz.py", line 71, in bar\n'
+                'File "/tmp/foobaz.py", line 73, in bar\n'
+                'File "/tmp/foobaz.py", line 73, in bar\n'
+                'File "/tmp/foobaz.py", line 73, in bar\n'
+                'File "/tmp/foobaz.py", line 73, in bar\n'
+                'File "/tmp/foobaz.py", line 73, in bar\n'
+                'File "/tmp/foobaz.py", line 73, in bar\n'
+                'stopping bar\n') in data
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to