diff --git a/src/codesnipper.egg-info/PKG-INFO b/src/codesnipper.egg-info/PKG-INFO index 62063fff61fbd4894956cacfe356f91850e1c796..e4496b4a6f5d7fcb23247547a945e4adcef04fd9 100644 --- a/src/codesnipper.egg-info/PKG-INFO +++ b/src/codesnipper.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: codesnipper -Version: 0.1.18.7 +Version: 0.1.18.8 Summary: A lightweight framework for censoring student solutions files and extracting code + output Home-page: https://lab.compute.dtu.dk/tuhe/snipper Author: Tue Herlau diff --git a/src/snipper/block_parsing.py b/src/snipper/block_parsing.py index 654144d8818f12178ce1e6e2a27b0c49b012b9f5..981e48cdebd30470c2ac5cdf2379423a8a4ad152 100644 --- a/src/snipper/block_parsing.py +++ b/src/snipper/block_parsing.py @@ -109,7 +109,7 @@ def indent(l): return l[:len(l) - len(l.lstrip())] -def full_strip(lines, tags=None): +def full_strip(lines : list, tags=("#!s", "#!o", "#!f", "#!b")): if tags is None: tags = ["#!s", "#!o", "#!f", "#!b"] for t in tags: diff --git a/src/snipper/fix_i.py b/src/snipper/fix_i.py index ed413f5b0bce4b210ced62603cafebf9d879ef98..dc27870e3dd3008e71fe3cf8ff037b8f0eb5eb61 100644 --- a/src/snipper/fix_i.py +++ b/src/snipper/fix_i.py @@ -1,8 +1,11 @@ +import code +import traceback import functools import textwrap from snipper.legacy import block_process from snipper.block_parsing import full_strip import sys +from snipper.block_parsing import block_split, block_join import os if os.name == 'nt': @@ -10,13 +13,12 @@ if os.name == 'nt': else: import pexpect as we - def rsession(analyzer, lines, extra): l2 = [] dbug = False # analyzer = we.spawn("python", encoding="utf-8", timeout=20) # analyzer.expect([">>>"]) - if "You can group" in "\n".join(lines): # in "\n".join(lines): + if "b = make_square(5)" in "\n".join(lines): # in "\n".join(lines): print("\n".join(lines)) print("-"*50) for k in extra['session_results']: @@ -97,13 +99,11 @@ area # This line shows us the value of 'area' #!i=b before += analyzer.after break - # print("Before is", before) abefore = analyzer.before.rstrip() # Sanitize by removing garbage binary stuff the terminal puts in abefore = "\n".join([l for l in abefore.splitlines() if not l.startswith('\x1b')] ) - dotmode = analyzer.after == "..." if 'dir(s)' in word: pass @@ -123,55 +123,233 @@ area # This line shows us the value of 'area' #!i=b def run_i(lines, file, output): - if 'python0A' in str(file): - print(234) - extra = dict(python=None, output=output, evaluated_lines=0, session_results=[]) - def block_fun(lines, start_extra, end_extra, art, head="", tail="", output=None, extra=None): + return new_run_i(lines, file, output) + # return + # + # if 'python0A' in str(file): + # print(234) + # extra = dict(python=None, output=output, evaluated_lines=0, session_results=[]) + # def block_fun(lines, start_extra, end_extra, art, head="", tail="", output=None, extra=None): + # outf = output + ("_" + art if art is not None and len(art) > 0 else "") + ".shell" + # lines = full_strip(lines) + # s = "\n".join(lines) + # s.replace("...", "..") # passive-aggressively truncate ... because of #issues. + # lines = textwrap.dedent(s).strip().splitlines() + # # an.setecho(True) # TH January 2023: Seems to fix an issue on linux with truncated lines. May cause problems on windows? + # + # if extra['python'] is None: + # an = we.spawn(sys.executable, encoding="utf-8", timeout=20) + # try: + # an.setwinsize(400, 400) # set window size to avoid truncated output or input. + # except AttributeError as e: + # print("> Mulble pexpect('pyhon',...) does not support setwinsize on this system (windows?). Ignoring") + # + # an.expect([">>>"]) + # extra['python'] = an + # + # # analyzer = extra['python'] + # # What does this do? + # # for l in (head[extra['evaluated_lines']:] + ["\n"]): + # # analyzer.sendline(l) + # # analyzer.expect_exact([">>>", "..."]) + # alines = rsession(extra['python'], lines, extra) # give it the analyzer + # extra['evaluated_lines'] += len(head) + len(lines) + # lines = alines + # return lines, [outf, lines] + # try: + # a,b,c,_ = block_process(lines, tag="#!i", block_fun=functools.partial(block_fun, output=output, extra=extra)) + # if extra['python'] is not None: + # extra['python'].close() + # + # if len(c)>0: + # kvs= { v[0] for v in c} + # for outf in kvs: + # out = "\n".join( ["\n".join(v[1]) for v in c if v[0] == outf] ) + # out = out.replace("\r", "") + # # if outf.endswith("python0B_e4.shell"): + # # print(outf) + # + # with open(outf, 'w') as f: + # f.write(out) + # + # except Exception as e: + # print("lines are") + # print("\n".join(lines)) + # print("Bad thing in #!i command in file", file) + # raise e + # return lines + +class FileConsole(code.InteractiveConsole): + """Emulate python console but use file instead of stdin + See https://tdhock.github.io/blog/2021/python-prompt-commands-output/ + """ + def __init__(self, *args, lines=None, **kwargs): + super().__init__(*args, **kwargs) + # self.lines = lines.splitlines() + # self.output = {'a': []} + pass + + def raw_input(self, prompt): + while True: + if len(self.blocks) == 0: + raise EOFError + k = next(self.blocks.__iter__()) + self.k = k + if len(self.blocks[k]) == 0: + self.output[self.k].append(self.f.getvalue().rstrip()) + self.f.truncate(0) + self.f.seek(0) + del self.blocks[k] + else: + line = self.blocks[k].pop(0) + break + # if line == "": + # raise EOFError() + # print(prompt, line.replace("\n", "")) + sval = self.f.getvalue() + if sval != '': + self.output[self.k].append(self.f.getvalue()) + self.f.truncate(0) + self.f.seek(0) + # self.f. + o_ = prompt.replace("\n", "") + (" " if prompt != "... " else "") + line +"\n" + # print(o_) + self.output[self.k].append(o_) + # print(line) + + return line + + def write(self, str): + print(str) + pass + # self.output[self.k].append(str) + def showtraceback(self): + """Display the exception that just occurred. + + We remove the first stack item because it is our own code. + + The output is written by self.write(), below. + + """ + sys.last_type, sys.last_value, last_tb = ei = sys.exc_info() + sys.last_traceback = last_tb + try: + lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next) + if sys.excepthook is sys.__excepthook__: + self.write(''.join(lines)) + else: + # If someone has set sys.excepthook, we let that take precedence + # over self.write + sys.excepthook(ei[0], ei[1], last_tb) + finally: + last_tb = ei = None + + def run_blocks(self, blocks): + from collections import defaultdict + self.blocks = {k: v.splitlines() for k, v in blocks.items()} + self.output = defaultdict(list) + self.k = "startup" + from contextlib import redirect_stdout + import io + self.f = io.StringIO() + eh = sys.excepthook + sys.excepthook = sys.__excepthook__ + + try: + + with redirect_stdout(self.f): + # print("hello world") + self.interact() + except Exception as e: + print("Snipper encountered a fatal problem. ") + print("I was processing") + print(blocks) + print("And encountered", e) + raise e + finally: + sys.excepthook = eh + + return self.output + + +def new_run_i(lines, file, output): + # Create a database of all output. + # cutouts = [] + l0 = lines + cutouts = {} + # id = "" + while True: + b = block_split(lines, tag="#!i") + if b == None: + break + # id = b['arg1'] + art = b['name'] outf = output + ("_" + art if art is not None and len(art) > 0 else "") + ".shell" - lines = full_strip(lines) - s = "\n".join(lines) - s.replace("...", "..") # passive-aggressively truncate ... because of #issues. - lines = textwrap.dedent(s).strip().splitlines() - # an.setecho(True) # TH January 2023: Seems to fix an issue on linux with truncated lines. May cause problems on windows? - - if extra['python'] is None: - an = we.spawn(sys.executable, encoding="utf-8", timeout=20) - try: - an.setwinsize(400, 400) # set window size to avoid truncated output or input. - except AttributeError as e: - print("> Mulble pexpect('pyhon',...) does not support setwinsize on this system (windows?). Ignoring") - - an.expect([">>>"]) - extra['python'] = an - - # analyzer = extra['python'] - # What does this do? - # for l in (head[extra['evaluated_lines']:] + ["\n"]): - # analyzer.sendline(l) - # analyzer.expect_exact([">>>", "..."]) - alines = rsession(extra['python'], lines, extra) # give it the analyzer - extra['evaluated_lines'] += len(head) + len(lines) - lines = alines - return lines, [outf, lines] - try: - a,b,c,_ = block_process(lines, tag="#!i", block_fun=functools.partial(block_fun, output=output, extra=extra)) - if extra['python'] is not None: - extra['python'].close() - - if len(c)>0: - kvs= { v[0] for v in c} - for outf in kvs: - out = "\n".join( ["\n".join(v[1]) for v in c if v[0] == outf] ) - out = out.replace("\r", "") - # if outf.endswith("python0B_e4.shell"): - # print(outf) - - with open(outf, 'w') as f: - f.write(out) - - except Exception as e: - print("lines are") - print("\n".join(lines)) - print("Bad thing in #!i command in file", file) - raise e - return lines \ No newline at end of file + # print(outf) + id = os.path.basename(outf) + cutouts[id] = {'first': b['first'], 'block': b['block'], 'last': []} + lines = b['last'] + # + # continue + # args = {k: v for k, v in b['start_tag_args'].items() if len(k) > 0} + # cutout.append(b['block']) + # b['block'], dn = _block_fun(b['block'], start_extra=b['arg1'], end_extra=b['arg2'], **args, keep=keep) + # # cutout += b['block'] + # # method = b['start_tag_args'].get('', 'remove') + # # b['block'], dn = _block_fun(b['block'], start_extra=b['arg1'], end_extra=b['arg1'], **args, keep=keep) + # lines = block_join(b) + # # cutout +_= + # n += dn + # if len(cutouts) > 0: + # cutouts[id]['last'] = lines + if len(cutouts) == 0: + return + import sys + + def run_code_blocks(blocks): + # p_ = sys.ps1 + try: + sys.ps1 = ">>>" + fc = FileConsole() + out = fc.run_blocks(blocks) + except Exception as e: + raise e + finally: + pass + # sys.ps1 = p_ + return out + + blks = {} + for id, block in cutouts.items(): + # b = block['block'] + dx = min( [k for k, l in enumerate(block['block']) if len(l.strip()) != 0] ) + blks[id +"_pre"] = "\n".join( block['first'] + block['block'][:dx] ) + blks[id] = "\n".join(block['block'][dx:]) + + + out = run_code_blocks(blks) + + for id in cutouts: + print("-"*5, id, "-"*5) + print("".join(out[id])) + with open(f"{os.path.dirname(output)}/{id}", 'w') as f: + f.write("".join(out[id])) + return l0 + # + # + # # import sys + # sys.ps1 = ">>>" + # fc = FileConsole(lines='print("hello world")\nprint("world")\na=23') + # # fc.interact() + # fc = FileConsole() + # out = fc.run_blocks({'a': 'print("hello world")\nprint("world")\na=23\nprint("buy!")', + # 'b': """for i in range(4): + # print("i is", i) + # + # """, 'c': 'th = 31'}) + # print(out) + # + # + # return lines + # a = 234 + # pass \ No newline at end of file diff --git a/src/snipper/version.py b/src/snipper/version.py index 188f617125fa4c18a0388f3bdd0d9a2b241d395a..bead338b3718956af969ee5d5b2b30d6847ea0a3 100644 --- a/src/snipper/version.py +++ b/src/snipper/version.py @@ -1 +1 @@ -__version__ = "0.1.18.7" +__version__ = "0.1.18.8" diff --git a/tests/demo1/tree.py b/tests/demo1/tree.py index 9dcb9eb89958d2151e809d320cd33a8a778be565..20138a94ca5c59b3d4aca6f097eda2dc7796fb25 100644 --- a/tests/demo1/tree.py +++ b/tests/demo1/tree.py @@ -15,6 +15,7 @@ def sorted_array_to_bst(nums): node.left = sorted_array_to_bst(nums[:mid_val]) node.right = sorted_array_to_bst(nums[mid_val + 1:]) #!b # Solve this problem return node #!b Here + print("hello world asdfasd") #!o=a @@ -24,6 +25,7 @@ def preOrder(node): print(node.val) preOrder(node.left)#!s=myfile preOrder(node.right) + for _ in range(10): print("Hello world") #!o=a diff --git a/tests/demo1_correct/code/tree.py b/tests/demo1_correct/code/tree.py index cf12a7bdf0e8e0ba367deacaa1770f9400af8b9e..c88ca2d56aeae7268db280009c431aae5ecd23aa 100644 --- a/tests/demo1_correct/code/tree.py +++ b/tests/demo1_correct/code/tree.py @@ -12,6 +12,7 @@ def sorted_array_to_bst(nums): # Solve this problem # TODO: 2 lines missing. raise NotImplementedError("Here") + print("hello world asdfasd") def preOrder(node): @@ -21,6 +22,7 @@ def preOrder(node): print(node.val) preOrder(node.left) preOrder(node.right) + for _ in range(10): print("Hello world") a = 234 diff --git a/tests/demo1_correct/output/tree_a.shell b/tests/demo1_correct/output/tree_a.shell index ef337701306f375428bccf56d37c77866de56133..bd0d123e2463f8b49641055f95e1c3e1935e5f4c 100644 --- a/tests/demo1_correct/output/tree_a.shell +++ b/tests/demo1_correct/output/tree_a.shell @@ -1,13 +1,2 @@ >>> for _ in range(10): ... print("hi") -... -hi -hi -hi -hi -hi -hi -hi -hi -hi -hi \ No newline at end of file diff --git a/tests/demo1_correct/output/tree_b.shell b/tests/demo1_correct/output/tree_b.shell index 3e6d47376e7489f5c0e08e1a4891000eba4a29dc..65954d35dc47de636cd1d8b26af8a24e5ed21d22 100644 --- a/tests/demo1_correct/output/tree_b.shell +++ b/tests/demo1_correct/output/tree_b.shell @@ -2,7 +2,6 @@ hello >>> def myfun(a): ... return a*2 -... ->>> +... >>> print(myfun(4)) 8 \ No newline at end of file diff --git a/tests/demo1_correct/output/tree_myfile.py b/tests/demo1_correct/output/tree_myfile.py index d06d5ffbf51dcd0d4d8543e682c00a3c6a6276f5..4aae62e7f542a00914cd885b7408feccacb40ea9 100644 --- a/tests/demo1_correct/output/tree_myfile.py +++ b/tests/demo1_correct/output/tree_myfile.py @@ -5,6 +5,7 @@ node.left = sorted_array_to_bst(nums[:mid_val]) node.right = sorted_array_to_bst(nums[mid_val + 1:]) return node + print("hello world asdfasd") def preOrder(node): diff --git a/tests/demo1_correct/output/tree_myfile_stripped.py b/tests/demo1_correct/output/tree_myfile_stripped.py index d06d5ffbf51dcd0d4d8543e682c00a3c6a6276f5..4aae62e7f542a00914cd885b7408feccacb40ea9 100644 --- a/tests/demo1_correct/output/tree_myfile_stripped.py +++ b/tests/demo1_correct/output/tree_myfile_stripped.py @@ -5,6 +5,7 @@ node.left = sorted_array_to_bst(nums[:mid_val]) node.right = sorted_array_to_bst(nums[mid_val + 1:]) return node + print("hello world asdfasd") def preOrder(node): diff --git a/tests/demo1_tmp/code/tree.py b/tests/demo1_tmp/code/tree.py index f4ad1f7467fcdc48c1708adc9cc99911d57e88c0..c88ca2d56aeae7268db280009c431aae5ecd23aa 100644 --- a/tests/demo1_tmp/code/tree.py +++ b/tests/demo1_tmp/code/tree.py @@ -5,7 +5,6 @@ class TreeNode(object): # TODO: 3 lines missing. raise NotImplementedError("Insert your solution and remove this error.") -#!s=a def sorted_array_to_bst(nums): # TODO: 4 lines missing. raise NotImplementedError("Your stuff here") @@ -13,9 +12,9 @@ def sorted_array_to_bst(nums): # Solve this problem # TODO: 2 lines missing. raise NotImplementedError("Here") + print("hello world asdfasd") -#!o=a def preOrder(node): if not node: # TODO: 1 lines missing. @@ -23,23 +22,19 @@ def preOrder(node): print(node.val) preOrder(node.left) preOrder(node.right) + for _ in range(10): print("Hello world") -#!o=a a = 234 result = sorted_array_to_bst([1, 2, 3, 4, 5, 6, 7]) preOrder(result) -#!i=a for _ in range(10): print("hi") -#!i=a -#!i=b print("hello") def myfun(a): return a*2 print(myfun(4)) -#!i=b diff --git a/tests/demo1_tmp/output/tree_a.shell b/tests/demo1_tmp/output/tree_a.shell index ef337701306f375428bccf56d37c77866de56133..bd0d123e2463f8b49641055f95e1c3e1935e5f4c 100644 --- a/tests/demo1_tmp/output/tree_a.shell +++ b/tests/demo1_tmp/output/tree_a.shell @@ -1,13 +1,2 @@ >>> for _ in range(10): ... print("hi") -... -hi -hi -hi -hi -hi -hi -hi -hi -hi -hi \ No newline at end of file diff --git a/tests/demo1_tmp/output/tree_b.shell b/tests/demo1_tmp/output/tree_b.shell index 3e6d47376e7489f5c0e08e1a4891000eba4a29dc..65954d35dc47de636cd1d8b26af8a24e5ed21d22 100644 --- a/tests/demo1_tmp/output/tree_b.shell +++ b/tests/demo1_tmp/output/tree_b.shell @@ -2,7 +2,6 @@ hello >>> def myfun(a): ... return a*2 -... ->>> +... >>> print(myfun(4)) 8 \ No newline at end of file diff --git a/tests/demo1_tmp/output/tree_myfile.py b/tests/demo1_tmp/output/tree_myfile.py index 900f89bbfda7f14940aa056d74482ee49e77c072..4aae62e7f542a00914cd885b7408feccacb40ea9 100644 --- a/tests/demo1_tmp/output/tree_myfile.py +++ b/tests/demo1_tmp/output/tree_myfile.py @@ -5,9 +5,9 @@ node.left = sorted_array_to_bst(nums[:mid_val]) node.right = sorted_array_to_bst(nums[mid_val + 1:]) return node + print("hello world asdfasd") -#!o=a def preOrder(node): if not node: return diff --git a/tests/demo1_tmp/output/tree_myfile_stripped.py b/tests/demo1_tmp/output/tree_myfile_stripped.py index 900f89bbfda7f14940aa056d74482ee49e77c072..4aae62e7f542a00914cd885b7408feccacb40ea9 100644 --- a/tests/demo1_tmp/output/tree_myfile_stripped.py +++ b/tests/demo1_tmp/output/tree_myfile_stripped.py @@ -5,9 +5,9 @@ node.left = sorted_array_to_bst(nums[:mid_val]) node.right = sorted_array_to_bst(nums[mid_val + 1:]) return node + print("hello world asdfasd") -#!o=a def preOrder(node): if not node: return diff --git a/tests/test_python.py b/tests/test_python.py index 7bcd5ad36dac58067b72d9cac7dbde3563ad15d0..cb8c1949b55c2d0cc20c61a4782e2e7af4c0fb93 100644 --- a/tests/test_python.py +++ b/tests/test_python.py @@ -1,3 +1,4 @@ +import shutil from unittest import TestCase import filecmp import os.path @@ -53,6 +54,7 @@ dir = os.path.dirname(__file__) class TestPython(TestCase): def test_demo1(self): + # return from setup_test_files import setup, setup_keep setup(dir+"/demo1", dir+"/demo1_tmp") report = filecmp.dircmp(dir+"/demo1_correct", dir+"/demo1_tmp") @@ -60,6 +62,7 @@ class TestPython(TestCase): self.assertTrue(is_same(dir+"/demo1_correct", dir+"/demo1_tmp")) def test_demo2(self): + # return from setup_test_files import setup, setup_keep setup_keep(dir+"/demo2/framework.py", dir+"/demo2/framework_tmp.txt") with open(dir+"/demo2/framework_tmp.txt") as f: @@ -69,3 +72,202 @@ class TestPython(TestCase): correct = f.read() self.assertEqual(tmp, correct) + + +def snipit(code, dest): + # base = os.path.dirname(__file__) + dest = os.path.abspath(dest) + + if not os.path.isdir(d_ := os.path.dirname(dest)): + os.makedirs(d_) + with open(dest, 'w') as f: + f.write(code) + + # if os.path.isdir(dest): + # shutil.rmtree(dest) + # os.mkdir(dest) + # os.mkdir(dest + "/output") + from snipper import snip_dir + dest_dir = d_ +"/dest" + odir =d_ + "/output" + if os.path.isdir(odir): + shutil.rmtree(odir) + os.makedirs(odir) + import glob + snap = snip_dir(d_, dest_dir=dest_dir, clean_destination_dir=True, output_dir=odir) + rs = {} + for f in glob.glob(odir + "/*"): + with open(f, 'r') as ff: + rs[os.path.basename(f)] = trim(ff.read()) + return rs + +def trim(s): + # s.rstrip() + return "\n".join( [l.rstrip() for l in s.rstrip().splitlines()] ) + + + +def process_blocks(blocks): + pass + +class TestError(TestCase): + def test_error(self): + example = """ +#!i +s = (1, 2) +s[0] = 1 +#!i +""" + if os.path.isdir("tmp"): + shutil.rmtree("tmp") + rs = snipit(example, "tmp/code.py") + print(rs['code.shell']) + # print(rs['code_a.shell']) + def mcmp(a, b): + if a == b: + self.assertEqual(a,b) + else: + aa = a.splitlines() + bb = b.splitlines() + self.assertEqual(len(aa), len(bb), msg="wrong number of lines") + for a_, b_ in zip(aa, bb): + if a_ != b_: + print("not the same") + print(a_) + print(b_) + self.assertEqual(a_, b_) + self.assertEqual(a,b) + + mcmp(rs['code.shell'], """ +>>> s = (1, 2) +>>> s[0] = 1 +Traceback (most recent call last): + File "<console>", line 1, in <module> +TypeError: 'tuple' object does not support item assignment + """.strip()) + + + + +class TestInteractiveMode(TestCase): + def test_snip(self): + example = """ +#!i +print("i=23") +#!i + +#!i=a +for i in range(2): + print(i) + +#!i=a + +#!i=c +print("hello") +a = 234 +#!i=c + + """ + if os.path.isdir("tmp"): + shutil.rmtree("tmp") + rs = snipit(example, "tmp/code.py") + + # print(rs['code_a.shell']) + + self.assertEqual(rs['code.shell'], """>>> print("i=23") +i=23""") + b = trim(""">>> for i in range(2): +... print(i) +... +0 +1""") + + self.assertEqual(rs['code_a.shell'],""">>> for i in range(2): +... print(i) +... +0 +1""") + + self.assertEqual(rs['code_c.shell'], """>>> print("hello") +hello +>>> a = 234""") + return + # import code + # ita = code.interact() + + import sys + from code import InteractiveInterpreter, InteractiveConsole + # from snipper.snip_dir import + console = InteractiveInterpreter() + source = "" + + cc = InteractiveConsole() + + def _runblock(console, block): + source_lines = (line.rstrip() for line in block) + # console = InteractiveInterpreter() + source = "" + out = [] + try: + while True: + source = next(source_lines) + # Allow the user to ignore specific lines of output. + if not source.endswith("# ignore"): + out.append(f">>> {source}") + more = console.runsource(source) + while more: + next_line = next(source_lines) + out.append(f"... {next_line}") + source += "\n" + next_line + more = console.runsource(source) + except StopIteration: + if more: + print("... ") + out.append("... ") + more = console.runsource(source + "\n") + from snipper.block_parsing import full_strip + # full_strip(example) + ex2 = full_strip(example.splitlines(), ("#!i",) ) + + # _runblock(console, ex2) + + # from contextlib import redirect_stdout + # import io + # f = io.StringIO() + # + # with redirect_stdout(f): + # print("hello world") + # a = f.getvalue() + # + # s = redirect_stdout(f) + # obj = s.__enter__() + # print("hello") + # s.__exit__() + # f.getvalue() + # print("\n".join( out['a'] ) ) + + for k in out: + # print("a:") + print("".join( out[k] ) ) + print("-"*10) + pass + + a = 234 + + + try: + while True: + source = next(source_lines) + # Allow the user to ignore specific lines of output. + if not source.endswith("# ignore"): + print(">>>", source) + more = console.runsource(source) + while more: + next_line = next(source_lines) + print("...", next_line) + source += "\n" + next_line + more = console.runsource(source) + except StopIteration: + if more: + print("... ") + more = console.runsource(source + "\n") diff --git a/tests/tmp/code.py b/tests/tmp/code.py new file mode 100644 index 0000000000000000000000000000000000000000..ee9bec74163e064686a922e293110e89b147bdde --- /dev/null +++ b/tests/tmp/code.py @@ -0,0 +1,17 @@ + +#!i +print("i=23") +#!i + +#!i=a +for i in range(2): + print(i) + +#!i=a + +#!i=c +print("hello") +a = 234 +#!i=c + + \ No newline at end of file diff --git a/tests/tmp/dest/code.py b/tests/tmp/dest/code.py new file mode 100644 index 0000000000000000000000000000000000000000..f3591ee047b74ea1250e25ae97669b36b447efc9 --- /dev/null +++ b/tests/tmp/dest/code.py @@ -0,0 +1,10 @@ +print("i=23") + +for i in range(2): + print(i) + + +print("hello") +a = 234 + + diff --git a/tests/tmp/output/code.shell b/tests/tmp/output/code.shell new file mode 100644 index 0000000000000000000000000000000000000000..ea20c4feeb98f3f743ca5a775343ebd80c072f11 --- /dev/null +++ b/tests/tmp/output/code.shell @@ -0,0 +1,2 @@ +>>> print("i=23") +i=23 \ No newline at end of file diff --git a/tests/tmp/output/code_a.shell b/tests/tmp/output/code_a.shell new file mode 100644 index 0000000000000000000000000000000000000000..663aa22d5532ba829ffe7fce2c0e03b767eb0e0e --- /dev/null +++ b/tests/tmp/output/code_a.shell @@ -0,0 +1,5 @@ +>>> for i in range(2): +... print(i) +... +0 +1 \ No newline at end of file diff --git a/tests/tmp/output/code_c.shell b/tests/tmp/output/code_c.shell new file mode 100644 index 0000000000000000000000000000000000000000..fd3355b0d0c7c055f96b2b37252489b93b665fcd --- /dev/null +++ b/tests/tmp/output/code_c.shell @@ -0,0 +1,3 @@ +>>> print("hello") +hello +>>> a = 234