diff --git a/0.1.11 b/0.1.11 deleted file mode 100644 index b5f99d0ce8e8c3b214d0a7d4e7a694490eeffed9..0000000000000000000000000000000000000000 --- a/0.1.11 +++ /dev/null @@ -1,10 +0,0 @@ -Requirement already satisfied: codesnipper in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (0.1.11) -Requirement already satisfied: wexpect in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from codesnipper) (4.0.0) -Requirement already satisfied: pybtex in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from codesnipper) (0.24.0) -Requirement already satisfied: pexpect in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from codesnipper) (4.8.0) -Requirement already satisfied: ptyprocess>=0.5 in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from pexpect->codesnipper) (0.7.0) -Requirement already satisfied: six in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from pybtex->codesnipper) (1.16.0) -Requirement already satisfied: latexcodec>=1.0.4 in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from pybtex->codesnipper) (2.0.1) -Requirement already satisfied: PyYAML>=3.01 in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from pybtex->codesnipper) (6.0) -Requirement already satisfied: pywin32>=220 in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from wexpect->codesnipper) (304) -Requirement already satisfied: psutil>=5.0.0 in c:\users\tuhe\appdata\local\programs\python\python310\lib\site-packages (from wexpect->codesnipper) (5.9.1) diff --git a/setup.py b/setup.py index 7309a30b4a993fa6129733762e50c0cf22ef7791..8f1847547645ab503defc73c09ff2eb7f2d369a0 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,8 @@ # Use: pipreqs.exe slider --no-pin --force for requirements_pip.txt # https://packaging.python.org/tutorials/packaging-projects/ # py -m build && twine upload dist/* +# Linux> python -m build && twine upload dist/* +# Local install: sudo pip install -e ./ import setuptools import pkg_resources diff --git a/src/codesnipper.egg-info/PKG-INFO b/src/codesnipper.egg-info/PKG-INFO index 1415e4f4a4163f11e0dfb7633e48481046a139e1..055fae08a3ba7de7f27371026f392ee3c4273a02 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.11 +Version: 0.1.14 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/codesnipper.egg-info/requires.txt b/src/codesnipper.egg-info/requires.txt index 4ba7d8fcd57f03899543a71a0d303d13cb5072a3..cdc5a5a821191c5ff35eb9ae362a55f75f8cc512 100644 --- a/src/codesnipper.egg-info/requires.txt +++ b/src/codesnipper.egg-info/requires.txt @@ -1,3 +1,4 @@ pexpect wexpect pybtex +numpy diff --git a/src/snipper/block_parsing.py b/src/snipper/block_parsing.py index d8996d86bf7c37ad76f343f29a77faea42cba068..ebca172b8154c4aa4b23fdb1d94da18417930ed8 100644 --- a/src/snipper/block_parsing.py +++ b/src/snipper/block_parsing.py @@ -37,7 +37,12 @@ def block_split(lines, tag): def get_tag_args(line): # line = line.strip() k = line.find(" ") + if 'nodoc' in line: + print("Nodoc!!") tag_args = ((line[:k + 1] if k >= 0 else line)[len(tag):] ).strip().split(";") + if 'nodoc' in tag_args: + print("nodoc.") + tag_args = [t.strip() for t in tag_args] # if len(tag_args) == 0: # return {'': ''} # No name. @@ -46,7 +51,8 @@ def block_split(lines, tag): if '' not in tag_args: tag_args[''] = '' - + if "dse" in tag_args: + print("DSE!!!") return tag_args if i is None: diff --git a/src/snipper/fix_cite.py b/src/snipper/fix_cite.py index a76120a635acc9b2791f50c33e4b57a01906274c..b27dc91d73113ae4b8cd2ca6cac62a637aaf4eec 100644 --- a/src/snipper/fix_cite.py +++ b/src/snipper/fix_cite.py @@ -11,7 +11,8 @@ def fix_citations(lines, references, strict=True, file=None): """ if str(file).endswith(".rst"): print(file) - lines = fix_aux(lines, aux=references.get('aux', {}) ) + # lines = fix_aux(lines, ) + lines = fix_single_reference(lines, aux=references.get('aux', {}), cmd="\\ref", strict=True) for cm in references.get('commands', []): # Probably just go with this one. lines = fix_aux_special(lines, aux=cm['aux'], command=cm['command'], output=cm['output']) @@ -29,11 +30,10 @@ def fix_aux_special(lines, aux, command='\\nref', output='\cite[%s]{my_bibtex_en l2 = fix_single_reference(lines, aux=daux, cmd=command, strict=True) return l2 -def fix_aux(lines, aux, strict=True): - print("fix_cite.py/fix_aux() deprecated...") - l2 = fix_single_reference(lines, aux=aux, cmd="\\ref", strict=True) - # print("\n".join(l2)) - return l2 +# def fix_aux(lines, aux, strict=True): +# print("fix_cite.py/fix_aux() deprecated...") +# print("\n".join(l2)) +# return l2 def fix_bibtex(lines, bibtex, rst_mode=False): @@ -64,7 +64,7 @@ def fix_bibtex(lines, bibtex, rst_mode=False): s = s[:i] + rtxt + s[j+1:] i = i + len(rtxt) - if len(all_refs) > 0: + if len(all_refs) > 0 and not rst_mode: # Don't add the license head in RST mode. The user is responsible for including references in sphinx conf.py. if not s.startswith(COMMENT): s = f"{COMMENT}\n{COMMENT}\n" + s i = s.find(COMMENT, s.find(COMMENT)+1) diff --git a/src/snipper/fix_i.py b/src/snipper/fix_i.py index 28b5bfbe733929b4083e490cdae58d86d511caf9..296dc0ccb1842780a315aa7fe0e73c47a064bdb3 100644 --- a/src/snipper/fix_i.py +++ b/src/snipper/fix_i.py @@ -9,65 +9,125 @@ 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 "BetterBasicDog" in "\n".join(lines) and False: + print("\n".join(lines)) + print("-"*50) + for k in extra['session_results']: + print(k['input']) + print(k['output']) + + import time + an = we.spawn("python", encoding="utf-8", timeout=20) + an.expect([">>>"]) + l3 = """ +import numpy as np +from scipy.linalg import norm +x = np.asarray([3, 4]) +x # What is x? +norm(x)""" + lines2 = l3.strip().splitlines() + + for l in lines2: + an.sendline(l) + an.expect_exact([">>>", "..."]) + print("INPUT", l) + print(">>>", an.before.strip()) + if len(an.after.strip()) > 4: + print(">>>>>>>>>>>>> That was a long after?") + # analyzer.be + + print('*' * 50) + # analyzer = an + dbug = True + lines = "\n".join(lines).replace("\r", "").splitlines() + + for i, l in enumerate(lines): + l2.append(l) + if l.startswith(" ") and i < len(lines)-1 and not lines[i+1].startswith(" "): + if not lines[i+1].strip().startswith("else:") and not lines[i+1].strip().startswith("elif") : + l2.append("") # Empty line instead? + + lines = l2 + alines = [] + in_dot_mode = False + if len(lines[-1]) > 0 and (lines[-1].startswith(" ") or lines[-1].startswith("\t")): + lines += [""] + + + for i, word in enumerate(lines): + if dbug: + print("> Sending...", word) + analyzer.sendline(word.rstrip()) + import time + before = "" + while True: + time.sleep(0.05) + analyzer.expect_exact([">>>", "..."]) + if dbug and "total_cost" in word: + aaa = 23234 + before += analyzer.before + # if dbug: + print("> analyzer.before...", analyzer.before.strip(), "...AFTER...", analyzer.after.strip()) + # AFTER = + if analyzer.before.endswith("\n"): + print("> BREAKING LOOP") + break + pass + else: + 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 + if 'help(s.find)' in word: + pass + if dotmode: + alines.append(">>>" +abefore.rstrip() if not in_dot_mode else "..." + abefore.rstrip()) + in_dot_mode = True + else: + alines.append( ("..." if in_dot_mode else ">>>") + abefore.rstrip()) + in_dot_mode = False + if dbug: + print("-"*50) + print("\n".join(alines)) + extra['session_results'].append({'input': '\n'.join(lines), 'output': '\n'.join(alines)}) + return alines + + def run_i(lines, file, output): - extra = dict(python=None, output=output, evaluated_lines=0) + 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("python", encoding="utf-8", timeout=20) an.expect([">>>"]) extra['python'] = an - analyzer = extra['python'] - def rsession(analyzer, lines): - l2 = [] - for i, l in enumerate(lines): - l2.append(l) - if l.startswith(" ") and i < len(lines)-1 and not lines[i+1].startswith(" "): - if not lines[i+1].strip().startswith("else:") and not lines[i+1].strip().startswith("elif") : - l2.append("\n") - - lines = l2 - alines = [] - in_dot_mode = False - if len(lines[-1]) > 0 and (lines[-1].startswith(" ") or lines[-1].startswith("\t")): - lines += [""] - - for i, word in enumerate(lines): - analyzer.sendline(word) - before = "" - while True: - analyzer.expect_exact([">>>", "..."]) - before += analyzer.before - if analyzer.before.endswith("\n"): - break - else: - before += analyzer.after - - dotmode = analyzer.after == "..." - if 'dir(s)' in word: - pass - if 'help(s.find)' in word: - pass - if dotmode: - alines.append(">>>" + analyzer.before.rstrip() if not in_dot_mode else "..." + analyzer.before.rstrip()) - in_dot_mode = True - else: - alines.append( ("..." if in_dot_mode else ">>>") + analyzer.before.rstrip()) - in_dot_mode = False - return alines - - for l in (head[extra['evaluated_lines']:] + ["\n"]): - analyzer.sendline(l) - analyzer.expect_exact([">>>", "..."]) - - - alines = rsession(analyzer, lines) + # 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] @@ -81,6 +141,8 @@ def run_i(lines, file, output): 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) diff --git a/src/snipper/fix_s.py b/src/snipper/fix_s.py index 5a97096a6321ddab15efa6cd46f900777476b92a..a6eff7e8024db8eb6011f39db9b9f4eff1d7aa27 100644 --- a/src/snipper/fix_s.py +++ b/src/snipper/fix_s.py @@ -2,6 +2,8 @@ from collections import defaultdict import os from snipper.block_parsing import block_iterate from snipper.snipper_main import full_strip +from snipper.block_parsing import indent + def get_s(lines): """ Return snips from 'lines' """ @@ -13,7 +15,11 @@ def get_s(lines): else: # In this case the #! tags are kept in. pass - # print("keepting tags.") + if 'dse' in c['start_tag_args']: + print("asdfasdfs") + print(c['start_tag_args']) + if 'nodoc' in c['start_tag_args'] and c['start_tag_args']['nodoc']: + print("No documentation!") blocks[c['name']].append(c) output = {} @@ -23,9 +29,54 @@ def get_s(lines): # c['block']['args'] # slines = slines[ 23] # co. + + + + # if slines[f.lineno].strip().startswith('"' * 3): + # print("got a docstrnig") + # for k in range(f.lineno, f.end_lineno + 1): + # l = slines[k] if k != f.lineno else slines[k].strip()[3:] + # if l.find('"' * 3) >= 0: + # break + # else: + # k = -1 + # if k > 0: + # print("Docstring detected") + # for i in range(f.lineno, k + 1): + # ll2[i] = None output[name] = slines return output +def rm_docstring(lines): + source = "\n".join(slines) + import ast + node = ast.parse(source) + classes = [n for n in node.body if isinstance(n, ast.ClassDef)] + functions = [n for n in node.body if isinstance(n, ast.FunctionDef)] + ll2 = slines.copy() + + def rm_ds(f, ll2): + if slines[f.lineno].strip().startswith('"' * 3): + # print("got a docstrnig") + for k in range(f.lineno, f.end_lineno + 1): + l = slines[k] if k != f.lineno else slines[k].strip()[3:] + if l.find('"' * 3) >= 0: + break + else: + k = -1 + if k > 0: + # print("Docstring detected") + for i in range(f.lineno, k + 1): + ll2[i] = None + + for f in functions: + rm_ds(f, ll2) + for c in classes: + for f in c.body: + rm_ds(f, ll2) + return [l for l in ll2 if l is not None] + + # def _s_block_process(): # # pass @@ -39,12 +90,23 @@ def save_s(lines, output_dir, file_path): # save file snips to disk for name, ll in content.items(): if file_path is not None: file_path = file_path.replace("\\", "/") - ll = [f"# {file_path}"] + ll + ll = [f"{indent(ll[0])}# {file_path}"] + ll + out = "\n".join(ll) - with open(output_dir + "/" + os.path.basename(file_path)[:-3] + ("_" + name if len(name) > 0 else name) + ".py", 'w') as f: + fname = output_dir + "/" + os.path.basename(file_path)[:-3] + ("_" + name if len(name) > 0 else name) + ".py" + with open(fname, 'w') as f: f.write(out) + # Dedent it for better plotting. + fname_stripped = fname[:-3] + "_stripped.py" + id = [indent(l) for l in ll if len(l.strip()) > 0] + id_len = [len(i) for i in id] + mindex = id_len[id_len.index(min(id_len))] + out2 = "\n".join([l[mindex:] for l in ll]) + + with open(fname_stripped, 'w') as f: + f.write(out2) s1 = """ L1 diff --git a/src/snipper/version.py b/src/snipper/version.py index 6dbb672ffb8889efcd413672815cdfb59af8a81a..f3b45743bddc02aadc17cb38557a5867e733caf7 100644 --- a/src/snipper/version.py +++ b/src/snipper/version.py @@ -1,4 +1 @@ -__version__ = "0.1.12" - -# /builds/02465material/02465public -# /builds/02465material/02465public \ No newline at end of file +__version__ = "0.1.15"