From 73bccb8160d3fe1ad307c7f5d495142e14347b3b Mon Sep 17 00:00:00 2001 From: Tue Herlau <tuhe@dtu.dk> Date: Sun, 5 Sep 2021 17:31:26 +0200 Subject: [PATCH] Refactor citation + tags and update README.md --- README.md | 284 ++++++++++++------ docs/README.jinja.md | 167 +++++----- docs/build_docs.py | 56 ++-- docs/index.png | Bin 0 -> 30083 bytes examples/cs101_instructor/b_tag.py | 15 + examples/cs101_instructor/f_tag.py | 6 + examples/cs101_instructor/i_tag.py | 8 + examples/cs101_instructor/o_tag.py | 11 + .../{homework1.py => references.py} | 12 +- examples/cs101_instructor/s_tag.py | 17 ++ examples/cs101_output/homework1.py | 17 ++ examples/cs101_output/i_tag_a.shell | 6 + examples/cs101_output/i_tag_b.shell | 5 + examples/cs101_output/o_tag_a.txt | 5 + examples/cs101_output/o_tag_b.txt | 3 + examples/cs101_output/s_tag.py | 39 +++ examples/cs101_students/b_tag.py | 12 + examples/cs101_students/f_tag.py | 7 + examples/cs101_students/i_tag.py | 10 + examples/cs101_students/o_tag.py | 11 + examples/cs101_students/references.py | 18 ++ examples/cs101_students/s_tag.py | 18 ++ examples/process_cs101.py | 26 +- examples/process_cs101_references.py | 15 + .../__pycache__/block_parsing.cpython-38.pyc | Bin 0 -> 2572 bytes src/snipper/__pycache__/fix_bf.cpython-38.pyc | Bin 0 -> 2163 bytes .../__pycache__/fix_cite.cpython-38.pyc | Bin 2453 -> 2762 bytes src/snipper/__pycache__/fix_o.cpython-38.pyc | Bin 0 -> 1387 bytes src/snipper/__pycache__/fix_s.cpython-38.pyc | Bin 3728 -> 1384 bytes src/snipper/__pycache__/legacy.cpython-38.pyc | Bin 0 -> 1790 bytes .../__pycache__/load_citations.cpython-38.pyc | Bin 0 -> 4979 bytes .../__pycache__/snip_dir.cpython-38.pyc | Bin 1978 -> 2103 bytes .../__pycache__/snipper_main.cpython-38.pyc | Bin 10073 -> 5835 bytes src/snipper/block_parsing.py | 11 +- src/snipper/fix_bf.py | 67 +++++ src/snipper/fix_cite.py | 20 +- src/snipper/fix_i.py | 0 src/snipper/fix_o.py | 38 +++ src/snipper/legacy.py | 75 +++++ src/snipper/snip_dir.py | 27 +- src/snipper/snipper_main.py | 191 ++---------- 41 files changed, 788 insertions(+), 409 deletions(-) create mode 100644 docs/index.png create mode 100644 examples/cs101_instructor/b_tag.py create mode 100644 examples/cs101_instructor/f_tag.py create mode 100644 examples/cs101_instructor/i_tag.py create mode 100644 examples/cs101_instructor/o_tag.py rename examples/cs101_instructor/{homework1.py => references.py} (62%) create mode 100644 examples/cs101_instructor/s_tag.py create mode 100644 examples/cs101_output/homework1.py create mode 100644 examples/cs101_output/i_tag_a.shell create mode 100644 examples/cs101_output/i_tag_b.shell create mode 100644 examples/cs101_output/o_tag_a.txt create mode 100644 examples/cs101_output/o_tag_b.txt create mode 100644 examples/cs101_output/s_tag.py create mode 100644 examples/cs101_students/b_tag.py create mode 100644 examples/cs101_students/f_tag.py create mode 100644 examples/cs101_students/i_tag.py create mode 100644 examples/cs101_students/o_tag.py create mode 100644 examples/cs101_students/references.py create mode 100644 examples/cs101_students/s_tag.py create mode 100644 examples/process_cs101_references.py create mode 100644 src/snipper/__pycache__/block_parsing.cpython-38.pyc create mode 100644 src/snipper/__pycache__/fix_bf.cpython-38.pyc create mode 100644 src/snipper/__pycache__/fix_o.cpython-38.pyc create mode 100644 src/snipper/__pycache__/legacy.cpython-38.pyc create mode 100644 src/snipper/__pycache__/load_citations.cpython-38.pyc create mode 100644 src/snipper/fix_i.py create mode 100644 src/snipper/fix_o.py create mode 100644 src/snipper/legacy.py diff --git a/README.md b/README.md index 4541d88..5a4acc3 100644 --- a/README.md +++ b/README.md @@ -20,82 +20,205 @@ The project is currently used in **02465** at DTU. An example of student code ca A set of lectures notes where all code examples/output are automatically generated from the working repository can be found a - https://lab.compute.dtu.dk/tuhe/books (see **Sequential decision making**) -## How it works -The basic functionality is quite simple. You start with your working script in your private repository and add special tags to the script. -In this case I have added the tags `#!b` (cut a block) and `#!f` (cut function scope). +# Usage +All examples can be found in the `/examples` directory. The idea is all our (complete) files are found in the instructor directory and snipper keeps everything up-to-date: +```text +examples/cs101_instructor # This directory contains the (hidden) instructor files. You edit these +examples/cs101_students # This directory contains the (processed) student files. Don't edit these +examples/cs101_output # This contains automatically generated contents (snippets, etc.). +``` +The basic functionality is you insert special comment tags in your source, such as `#!b` or `#!s` and the script then process +the sources based on the tags. The following will show most basic usages: + +## The #f!-tag +Let's start with the simplest example, blocking out a function (see `examples/cs101_instructor/f_tag.py`; actually it will work for any scope) +You insert a comment like: `#!f <exception message>` like so: ```python -def myfun(): #!f The error I am going to raise - """ The function docstring will not be removed""" - print("This is a function") - return 42 - -def a_long_function(): - a = 234 - print("a line") - print("a line") #!b - print("a line") - print("a line") #!b Insert three missing print statements. - print("a line") - return a - -if __name__ == "__main__": - myfun() +def myfun(a,b): #!f return the sum of a and b + """ The doc-string is not removed. """ + sm = a+b + return sm ``` -This will produce the following file: +To compile this (and all other examples) use the script `examples/process_cs101.py` ```python -def myfun(): - """ The function docstring will not be removed""" - # TODO: 2 lines missing. - raise NotImplementedError("The error I am going to raise") - -def a_long_function(): - a = 234 - print("a line") - # TODO: 3 lines missing. - raise NotImplementedError("Insert three missing print statements.") - print("a line") - return a - if __name__ == "__main__": - myfun() + from snipper.snip_dir import snip_dir + snip_dir("./cs101_instructor", "./cs101_students", output_dir="./cs101_output") +``` +The output can be found in `examples/students/f_tag.py`. It will cut out the body of the function but leave any return statement and docstrings. It will also raise an exception (and print how many lines are missing) to help students. +```python +""" +""" +def myfun(a,b): + """ The doc-string is not removed. """ + # TODO: 1 lines missing. + raise NotImplementedError("return the sum of a and b") + return sm ``` -You can also use the framework to capture code snippets, outputs and interactive python output. -To do this, save the following in `foo.py` + +## The #b!-tag +The #!b-tag allows you more control over what is cut out. The instructor file: ```python -def myfun(): #!s This snippet will be saved to foo.py in the output directory. - print("Hello") #!s - -print("Do not capture me") -for i in range(4): #!o - print("Output", i) -print("Goodbuy world") #!o -print("don't capture me") - -# Interactive pythong example -print("Hello World") #!i #!i # this is a single-line cutout. -```` -These block-tags will create a file `foo.py` (in the output directory) containing +def primes_sieve(limit): + limitn = limit+1 #!b + primes = range(2, limitn) + for i in primes: + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) #!b Compute the list `primes` here of all primes up to `limit` + return primes +width, height = 2, 4 +print("Area of square of width", width, "and height", height, "is:") +print(width*height) #!b #!b Compute and print area here +print("and that is a fact!") +``` +Is compiled into: +```python +""" +""" +def primes_sieve(limit): + # TODO: 8 lines missing. + raise NotImplementedError("Compute the list `primes` here of all primes up to `limit`") + return primes +width, height = 2, 4 +print("Area of square of width", width, "and height", height, "is:") +# TODO: 1 lines missing. +raise NotImplementedError("Compute and print area here") +print("and that is a fact!") +``` +This allows you to cut out text across scopes, but still allows you to insert exceptions. + + + +## The #s!-tag +The #!s-tag is useful for making examples to include in exercises and lecture notes. The #!s (snip) tag cuts out the text between +tags and places it in files found in the output-directory. As an example, here is the instructor file: ```python -def myfun(): - print("Hello") -``` -A file `foo.txt` containing the captured output -```txt -Output 0 -Output 1 -Output 2 -Output 3 -Goodbuy world -``` -and a typeset version of an interactive python session in `foo.pyi` (use `pycon` in minted; this gitlab server appears not to support `pycon`) +width, height = 2, 4 +print("Area of square of width", width, "and height", height, "is:") #!s +print(width*height) #!s # This is an example of a simple cutout +print("and that is a fact!") +print("An extra cutout") #!s #!s # This will be added to the above cutout +def primes_sieve(limit): #!s=a # A named cutout + limitn = limit+1 + primes = range(2, limitn) + for i in primes: #!s=b A nested/named cutout. + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) #!s=b + return primes #!s=a +``` +Note it allows + - naming using the #!s=<name> command + - automatically join snippets with the same name (useful to cut out details) + - The named tags will be matched, and do not have to strictly contained in each other + +This example will produce three files +`cs101_output/s_tag.py`, `cs101_output/s_tag_a.py`, and `cs101_output/s_tag_b.py` containing the output: +```python +# s_tag.py +print("Area of square of width", width, "and height", height, "is:") +print(width*height) #!s # This is an example of a simple cutout +print("and that is a fact!") +print("An extra cutout") #!s #!s # This will be added to the above cutout +def primes_sieve(limit): +print(width*height) +print("and that is a fact!") +print("An extra cutout") #!s #!s # This will be added to the above cutout +def primes_sieve(limit): + limitn = limit+1 + primes = range(2, limitn) + for i in primes: +print("An extra cutout") +def primes_sieve(limit): + limitn = limit+1 + primes = range(2, limitn) + for i in primes: + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) +print("An extra cutout") +def primes_sieve(limit): + limitn = limit+1 + primes = range(2, limitn) + for i in primes: + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) + return primes +``` +and +```python + +``` +and finally: +```python + +``` +I recommend using `\inputminted{filename}` to insert the cutouts in LaTeX. + + +## The #o!-tag +The #!o-tag allows you to capture output from the code, which can be useful when showing students the expected +behavior of their scripts. Like the #!s-tag, the #!o-tags can be named. + +As an example, Consider the instructor file +```python +if __name__ == "__main__": + print("Here are the first 4 square numbers") #!o=a + for k in range(1,5): + print(k*k, "is a square") + #!o=a + print("This line will not be part of a cutout.") + width, height = 2, 4 #!o=b + print("Area of square of width", width, "and height", height, "is:") + print(width*height) + print("and that is a fact!") #!o=b +``` +This example will produce two files `cs101_output/o_tag_a.txt`, `cs101_output/o_tag_b.txt`: +```python + +``` +and +```python + +``` + +## The #i!-tag +The #!i-tag allows you to create interactive python shell-snippets that can be imported using the minted `pycon` environment ('\inputminted{python}{input.shell}'). + As an example, consider the instructor file +```python +for animal in ["Dog", "cat", "wolf"]: #!i=a + print("An example of a four legged animal is", animal) #!i=a +#!i=b +def myfun(a,b): + return a+b +myfun(3,4) #!i=b +# Snipper will automatically insert an 'enter' after the function definition. +``` +This example will produce two files `cs101_output/i_tag_a.shell`, `cs101_output/i_tag_b.shell`: ```pycon ->>> print("hello world") -Hello World" + ``` -All these files can be directly imported into `LaTeX` using e.g. `minted`: You never need to mix `LaTeX` code and python again! +and +```pycon + +``` +Note that apparently there + is no library for converting python code to shell sessions so I had to write it myself, which means it can properly get confused with multi-line statements (lists, etc.). On the plus-side, it will automatically insert newlines after the end of scopes. + My parse is also known to be a bit confused if your code outputs `...` since it has to manually parse the interactive python session and this normally indicates a new line. + +## References and citations (`\ref` and `\cite`) +One of the most annoying parts of maintaining student code is to constantly write "see equation on slide 41 bottom" only to have the reference go stale because slide 23 got removed. Well now anymore, now you can direcly refence anything with a bibtex or aux file! +Let's consider the following example of a simple document with a couple of references: (see `examples/latex/index.pdf`): + + -## References: Bibliography references can be loaded from `references.bib`-files and in-document references from the `.aux` file. For this example, we will insert references shown in the `examples/latex/index.tex`-document. To do so, we can use these tags: ```python @@ -112,39 +235,20 @@ def myfun(): #!s ``` We can manually compile this example by first loading the aux-files and the bibliographies as follows: ```python -# load_references.py - from snipper.citations import get_bibtex, get_aux - bibfile = "latex/library.bib" - auxfile = 'latex/index.aux' - bibtex = get_bibtex(bibfile) - aux = get_aux(auxfile) + ``` Next, we load the python file containing the reference code and fix all references based on the aux and bibliography data. ```python -# load_references.py - file = "citations.py" - with open(file, 'r') as f: - lines = f.read().splitlines() - lines = fix_aux(lines, aux=aux) - lines = fix_aux_special(lines, aux=aux, command='\\nref', bibref='herlau') - lines = fix_bibtex(lines, bibtex=bibtex) - with open('output/citations.py', 'w') as f: - f.write("\n".join(lines)) + ``` The middle command is a convenience feature: It allows us to specify a special citation command `\nref{..}` which always compiles to `\cite[\ref{...}]{herlau}`. This is useful if e.g. `herlau` is the bibtex key for your lecture notes. The result is as follows: ```python -""" -References: - [Ber07] Dimitri P. Bertsekas. Dynamic Programming and Optimal Control, Vol. II. Athena Scientific, 3rd edition, 2007. ISBN 1886529302. - [Her21] Tue Herlau. Sequential decision making. (See 02465_Notes.pdf), 2021. -""" def myfun(): #!s """ - To solve this exercise, look at eq. (1) in Section 1. - You can also look at (Ber07) and (Her21) - More specifically, look at (Ber07, Equation 117) and (Her21, Figure 1) - - We can also write a special tag to reduce repetition: (Her21, Figure 1) and (Her21, Section 1). + To solve this exercise, look at \ref{eq1} in \ref{sec1}. + You can also look at \cite{bertsekasII} and \cite{herlau} + More specifically, look at \cite[Equation 117]{bertsekasII} and \cite[\ref{fig1}]{herlau} + We can also write a special tag to reduce repetition: \nref{fig1} and \nref{sec1}. """ return 42 #!s ``` diff --git a/docs/README.jinja.md b/docs/README.jinja.md index add4775..28e0cd2 100644 --- a/docs/README.jinja.md +++ b/docs/README.jinja.md @@ -20,82 +20,113 @@ The project is currently used in **02465** at DTU. An example of student code ca A set of lectures notes where all code examples/output are automatically generated from the working repository can be found a - https://lab.compute.dtu.dk/tuhe/books (see **Sequential decision making**) -## How it works -The basic functionality is quite simple. You start with your working script in your private repository and add special tags to the script. -In this case I have added the tags `#!b` (cut a block) and `#!f` (cut function scope). +# Usage +All examples can be found in the `/examples` directory. The idea is all our (complete) files are found in the instructor directory and snipper keeps everything up-to-date: +```text +examples/cs101_instructor # This directory contains the (hidden) instructor files. You edit these +examples/cs101_students # This directory contains the (processed) student files. Don't edit these +examples/cs101_output # This contains automatically generated contents (snippets, etc.). +``` +The basic functionality is you insert special comment tags in your source, such as `#!b` or `#!s` and the script then process +the sources based on the tags. The following will show most basic usages: + +## The #f!-tag +Let's start with the simplest example, blocking out a function (see `examples/cs101_instructor/f_tag.py`; actually it will work for any scope) +You insert a comment like: `#!f <exception message>` like so: +```python +{{ cs101_instructor.f_tag_py }} +``` +To compile this (and all other examples) use the script `examples/process_cs101.py` +```python +{{ process_cs101_py }} +``` +The output can be found in `examples/students/f_tag.py`. It will cut out the body of the function but leave any return statement and docstrings. It will also raise an exception (and print how many lines are missing) to help students. +```python +{{ cs101_students.f_tag_py }} +``` + +## The #b!-tag +The #!b-tag allows you more control over what is cut out. The instructor file: ```python -def myfun(): #!f The error I am going to raise - """ The function docstring will not be removed""" - print("This is a function") - return 42 - -def a_long_function(): - a = 234 - print("a line") - print("a line") #!b - print("a line") - print("a line") #!b Insert three missing print statements. - print("a line") - return a - -if __name__ == "__main__": - myfun() -``` -This will produce the following file: +{{ cs101_instructor.b_tag_py }} +``` +Is compiled into: +```python +{{ cs101_students.b_tag_py }} +``` +This allows you to cut out text across scopes, but still allows you to insert exceptions. + + + +## The #s!-tag +The #!s-tag is useful for making examples to include in exercises and lecture notes. The #!s (snip) tag cuts out the text between +tags and places it in files found in the output-directory. As an example, here is the instructor file: ```python -def myfun(): - """ The function docstring will not be removed""" - # TODO: 2 lines missing. - raise NotImplementedError("The error I am going to raise") - -def a_long_function(): - a = 234 - print("a line") - # TODO: 3 lines missing. - raise NotImplementedError("Insert three missing print statements.") - print("a line") - return a - -if __name__ == "__main__": - myfun() -``` -You can also use the framework to capture code snippets, outputs and interactive python output. -To do this, save the following in `foo.py` +{{ cs101_instructor.s_tag_py }} +``` +Note it allows + - naming using the #!s=<name> command + - automatically join snippets with the same name (useful to cut out details) + - The named tags will be matched, and do not have to strictly contained in each other + +This example will produce three files +`cs101_output/s_tag.py`, `cs101_output/s_tag_a.py`, and `cs101_output/s_tag_b.py` containing the output: +```python +{{ cs101_output.s_tag_py }} +``` +and +```python +{{ cs101_output.s_tag_a_py }} +``` +and finally: +```python +{{ cs101_output.s_tag_b_py }} +``` +I recommend using `\inputminted{filename}` to insert the cutouts in LaTeX. + + +## The #o!-tag +The #!o-tag allows you to capture output from the code, which can be useful when showing students the expected +behavior of their scripts. Like the #!s-tag, the #!o-tags can be named. + +As an example, Consider the instructor file ```python -def myfun(): #!s This snippet will be saved to foo.py in the output directory. - print("Hello") #!s - -print("Do not capture me") -for i in range(4): #!o - print("Output", i) -print("Goodbuy world") #!o -print("don't capture me") - -# Interactive pythong example -print("Hello World") #!i #!i # this is a single-line cutout. -```` -These block-tags will create a file `foo.py` (in the output directory) containing +{{ cs101_instructor.o_tag_py }} +``` +This example will produce two files `cs101_output/o_tag_a.txt`, `cs101_output/o_tag_b.txt`: +```python +{{ cs101_output.o_tag_a_py }} +``` +and +```python +{{ cs101_output.o_tag_b_py }} +``` + +## The #i!-tag +The #!i-tag allows you to create interactive python shell-snippets that can be imported using the minted `pycon` environment ('\inputminted{python}{input.shell}'). + As an example, consider the instructor file ```python -def myfun(): - print("Hello") -``` -A file `foo.txt` containing the captured output -```txt -Output 0 -Output 1 -Output 2 -Output 3 -Goodbuy world -``` -and a typeset version of an interactive python session in `foo.pyi` (use `pycon` in minted; this gitlab server appears not to support `pycon`) +{{ cs101_instructor.i_tag_py }} +``` +This example will produce two files `cs101_output/i_tag_a.shell`, `cs101_output/i_tag_b.shell`: ```pycon ->>> print("hello world") -Hello World" +{{ cs101_output.i_tag_a_py }} +``` +and +```pycon +{{ cs101_output.i_tag_b_py }} ``` -All these files can be directly imported into `LaTeX` using e.g. `minted`: You never need to mix `LaTeX` code and python again! +Note that apparently there + is no library for converting python code to shell sessions so I had to write it myself, which means it can properly get confused with multi-line statements (lists, etc.). On the plus-side, it will automatically insert newlines after the end of scopes. + My parse is also known to be a bit confused if your code outputs `...` since it has to manually parse the interactive python session and this normally indicates a new line. + +## References and citations (`\ref` and `\cite`) +One of the most annoying parts of maintaining student code is to constantly write "see equation on slide 41 bottom" only to have the reference go stale because slide 23 got removed. Well now anymore, now you can direcly refence anything with a bibtex or aux file! +Let's consider the following example of a simple document with a couple of references: (see `examples/latex/index.pdf`): + + -## References: Bibliography references can be loaded from `references.bib`-files and in-document references from the `.aux` file. For this example, we will insert references shown in the `examples/latex/index.tex`-document. To do so, we can use these tags: ```python diff --git a/docs/build_docs.py b/docs/build_docs.py index 46e7a45..35d26b8 100644 --- a/docs/build_docs.py +++ b/docs/build_docs.py @@ -19,47 +19,47 @@ def my_nup(path): if __name__ == "__main__": from snipper.fix_s import save_s from snipper.snipper_main import censor_file - + data = {} EX_BASE = "../examples" np = EX_BASE + "/latex" + import glob + + dirs = ['cs101_output', 'cs101_instructor', 'cs101_students'] + def dir_import(dir): + dat = {} + for f in glob.glob(dir+"/*.*"): + name = os.path.basename(f).replace(".", '_') + with open(f, 'r') as f: + s = [s for s in f.read().rstrip().splitlines() if s.strip() != ''] + dat[name] = '\n'.join(s) + return dat + + for d in dirs: + data[d] = dir_import("../examples/"+d) + # for f in glob.glob("../examples/"+d+"/*.*"): + # name = os.path.basename(f).replace(".",'_') + # with open(f, 'r') as f: + # data[d][name] = f.read() + + data = {**data, **dir_import('../examples')} + data['resources'] = 'https://gitlab.compute.dtu.dk/tuhe/snipper/-/blob/main' - # if os.path.isdir(np): - # shutil.rmtree(np) - # os.makedirs(np) - # slider_cli(latexfile=f"{np}/index.tex", force=True) - # np_basic1 = EX_BASE + "/basic1" - # if os.path.isdir(np_basic1): - # shutil.rmtree(np_basic1) - # shutil.copytree(np, np_basic1) - # shutil.copyfile("myoverlay.svg", np_basic1 +"/osvgs/myoverlay.svg") - # slider_cli(latexfile=f"{np_basic1}/index.tex") - # - # - # my_nup(np + "/index.pdf") - # my_nup(f"{np_basic1}/index.pdf") - # convert.pdf2png(np + "/index.pdf", "./index.png", scale_to=600) - # output = np +"/index_2up.pdf" - # - # data = {} - # data['resources'] = 'https://gitlab.compute.dtu.dk/tuhe/slider/-/raw/main' - # with open(np + "/index.tex", 'r') as f: - # data['basic0_tex'] = f.read() - data = {} # Build the docs. import glob - with open("../example/citations.py", 'r') as f: + with open("../examples/citations.py", 'r') as f: data['citations_orig_py'] = f.read() - for file in glob.glob("../example/output/*.*"): - with open(file, 'r') as f: - data[os.path.basename(file).replace(".", "_")] = f.read() + # for file in glob.glob("../examples/output/*.*"): + # with open(file, 'r') as f: + # data[os.path.basename(file).replace(".", "_")] = f.read() with open("README.jinja.md", 'r') as f: s = jinja2.Environment(loader=jinja2.FileSystemLoader([".", "../example"])).from_string(f.read()).render(data) - with open("../README.md",'w') as f: + with open("../README.md", 'w') as f: f.write(s) + pass \ No newline at end of file diff --git a/docs/index.png b/docs/index.png new file mode 100644 index 0000000000000000000000000000000000000000..53392ac5e0855f43a4b1ac49f61f6b5dad857278 GIT binary patch literal 30083 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 literal 0 HcmV?d00001 diff --git a/examples/cs101_instructor/b_tag.py b/examples/cs101_instructor/b_tag.py new file mode 100644 index 0000000..6f8be07 --- /dev/null +++ b/examples/cs101_instructor/b_tag.py @@ -0,0 +1,15 @@ +def primes_sieve(limit): + limitn = limit+1 #!b + primes = range(2, limitn) + + for i in primes: + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) #!b Compute the list `primes` here of all primes up to `limit` + return primes + +width, height = 2, 4 +print("Area of square of width", width, "and height", height, "is:") +print(width*height) #!b #!b Compute and print area here +print("and that is a fact!") diff --git a/examples/cs101_instructor/f_tag.py b/examples/cs101_instructor/f_tag.py new file mode 100644 index 0000000..354421a --- /dev/null +++ b/examples/cs101_instructor/f_tag.py @@ -0,0 +1,6 @@ + +def myfun(a,b): #!f return the sum of a and b + """ The doc-string is not removed. """ + sm = a+b + return sm + diff --git a/examples/cs101_instructor/i_tag.py b/examples/cs101_instructor/i_tag.py new file mode 100644 index 0000000..111237d --- /dev/null +++ b/examples/cs101_instructor/i_tag.py @@ -0,0 +1,8 @@ +for animal in ["Dog", "cat", "wolf"]: #!i=a + print("An example of a four legged animal is", animal) #!i=a + +#!i=b +def myfun(a,b): + return a+b +myfun(3,4) #!i=b +# Snipper will automatically insert an 'enter' after the function definition. diff --git a/examples/cs101_instructor/o_tag.py b/examples/cs101_instructor/o_tag.py new file mode 100644 index 0000000..fa15370 --- /dev/null +++ b/examples/cs101_instructor/o_tag.py @@ -0,0 +1,11 @@ +if __name__ == "__main__": + print("Here are the first 4 square numbers") #!o=a + for k in range(1,5): + print(k*k, "is a square") + #!o=a + print("This line will not be part of a cutout.") + width, height = 2, 4 #!o=b + print("Area of square of width", width, "and height", height, "is:") + print(width*height) + print("and that is a fact!") #!o=b + diff --git a/examples/cs101_instructor/homework1.py b/examples/cs101_instructor/references.py similarity index 62% rename from examples/cs101_instructor/homework1.py rename to examples/cs101_instructor/references.py index 49ed989..7c6a590 100644 --- a/examples/cs101_instructor/homework1.py +++ b/examples/cs101_instructor/references.py @@ -1,4 +1,4 @@ -def myfun(): #!s +def myfun(): """ Simple aux references \ref{eq1} in \ref{sec1}. Simple bibtex citations: \cite{bertsekasII} and \cite[Somewhere around the middle]{herlau} @@ -9,12 +9,6 @@ def myfun(): #!s Other example of custom command (reference assignment) > \aref2{sec1} """ - print("See \\ref{sec1}") # Also works. - return 42 #!s - -def fun1(l1, l2): - s1 = sum(l1) #!s - s2 = sum(l2) - print("Hello worlds!") #!s - return s1 + s2 + print("See \ref{sec1}") # Also works. + return 42 diff --git a/examples/cs101_instructor/s_tag.py b/examples/cs101_instructor/s_tag.py new file mode 100644 index 0000000..03a8706 --- /dev/null +++ b/examples/cs101_instructor/s_tag.py @@ -0,0 +1,17 @@ +width, height = 2, 4 +print("Area of square of width", width, "and height", height, "is:") #!s +print(width*height) #!s # This is an example of a simple cutout +print("and that is a fact!") +print("An extra cutout") #!s #!s # This will be added to the above cutout + +def primes_sieve(limit): #!s=a # A named cutout + limitn = limit+1 + primes = range(2, limitn) + + for i in primes: #!s=b A nested/named cutout. + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) #!s=b + return primes #!s=a + diff --git a/examples/cs101_output/homework1.py b/examples/cs101_output/homework1.py new file mode 100644 index 0000000..2305ed7 --- /dev/null +++ b/examples/cs101_output/homework1.py @@ -0,0 +1,17 @@ +# homework1.py +def myfun(): + """ + Simple aux references eq. (1) in Section 1. + Simple bibtex citations: (Ber07) and (Her21, Somewhere around the middle) + + Example of custom command (reference notes) + > \nref{fig1} + + Other example of custom command (reference assignment) + > (Assignment 2, Section 1) + """ + print("See \Section 1") # Also works. + return 42 + s1 = sum(l1) + s2 = sum(l2) + print("Hello worlds!") \ No newline at end of file diff --git a/examples/cs101_output/i_tag_a.shell b/examples/cs101_output/i_tag_a.shell new file mode 100644 index 0000000..f753af9 --- /dev/null +++ b/examples/cs101_output/i_tag_a.shell @@ -0,0 +1,6 @@ +>>> for animal in ["Dog", "cat", "wolf"]: +... print("An example of a four legged animal is", animal) +... +An example of a four legged animal is Dog +An example of a four legged animal is cat +An example of a four legged animal is wolf \ No newline at end of file diff --git a/examples/cs101_output/i_tag_b.shell b/examples/cs101_output/i_tag_b.shell new file mode 100644 index 0000000..da66455 --- /dev/null +++ b/examples/cs101_output/i_tag_b.shell @@ -0,0 +1,5 @@ +>>> def myfun(a,b): +... return a+b +... +>>> myfun(3,4) +7 \ No newline at end of file diff --git a/examples/cs101_output/o_tag_a.txt b/examples/cs101_output/o_tag_a.txt new file mode 100644 index 0000000..5ea6232 --- /dev/null +++ b/examples/cs101_output/o_tag_a.txt @@ -0,0 +1,5 @@ +Here are the first 4 square numbers +1 is a square +4 is a square +9 is a square +16 is a square diff --git a/examples/cs101_output/o_tag_b.txt b/examples/cs101_output/o_tag_b.txt new file mode 100644 index 0000000..171adc7 --- /dev/null +++ b/examples/cs101_output/o_tag_b.txt @@ -0,0 +1,3 @@ +Area of square of width 2 and height 4 is: +8 +and that is a fact! diff --git a/examples/cs101_output/s_tag.py b/examples/cs101_output/s_tag.py new file mode 100644 index 0000000..fcddb99 --- /dev/null +++ b/examples/cs101_output/s_tag.py @@ -0,0 +1,39 @@ +# s_tag.py +print("Area of square of width", width, "and height", height, "is:") +print(width*height) #!s # This is an example of a simple cutout +print("and that is a fact!") +print("An extra cutout") #!s #!s # This will be added to the above cutout + +def primes_sieve(limit): +print(width*height) +print("and that is a fact!") +print("An extra cutout") #!s #!s # This will be added to the above cutout + +def primes_sieve(limit): + limitn = limit+1 + primes = range(2, limitn) + + for i in primes: +print("An extra cutout") + +def primes_sieve(limit): + limitn = limit+1 + primes = range(2, limitn) + + for i in primes: + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) +print("An extra cutout") + +def primes_sieve(limit): + limitn = limit+1 + primes = range(2, limitn) + + for i in primes: + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) + return primes \ No newline at end of file diff --git a/examples/cs101_students/b_tag.py b/examples/cs101_students/b_tag.py new file mode 100644 index 0000000..b0e5de3 --- /dev/null +++ b/examples/cs101_students/b_tag.py @@ -0,0 +1,12 @@ +""" +""" +def primes_sieve(limit): + # TODO: 8 lines missing. + raise NotImplementedError("Compute the list `primes` here of all primes up to `limit`") + return primes + +width, height = 2, 4 +print("Area of square of width", width, "and height", height, "is:") +# TODO: 1 lines missing. +raise NotImplementedError("Compute and print area here") +print("and that is a fact!") diff --git a/examples/cs101_students/f_tag.py b/examples/cs101_students/f_tag.py new file mode 100644 index 0000000..f1e55a7 --- /dev/null +++ b/examples/cs101_students/f_tag.py @@ -0,0 +1,7 @@ +""" +""" +def myfun(a,b): + """ The doc-string is not removed. """ + # TODO: 1 lines missing. + raise NotImplementedError("return the sum of a and b") + return sm diff --git a/examples/cs101_students/i_tag.py b/examples/cs101_students/i_tag.py new file mode 100644 index 0000000..59106b7 --- /dev/null +++ b/examples/cs101_students/i_tag.py @@ -0,0 +1,10 @@ +""" +""" +for animal in ["Dog", "cat", "wolf"]: + print("An example of a four legged animal is", animal) + +#!i=b +def myfun(a,b): + return a+b +myfun(3,4) +# Snipper will automatically insert an 'enter' after the function definition. diff --git a/examples/cs101_students/o_tag.py b/examples/cs101_students/o_tag.py new file mode 100644 index 0000000..e0d31b0 --- /dev/null +++ b/examples/cs101_students/o_tag.py @@ -0,0 +1,11 @@ +""" +""" +if __name__ == "__main__": + print("Here are the first 4 square numbers") + for k in range(1,5): + print(k*k, "is a square") + print("This line will not be part of a cutout.") + width, height = 2, 4 + print("Area of square of width", width, "and height", height, "is:") + print(width*height) + print("and that is a fact!") diff --git a/examples/cs101_students/references.py b/examples/cs101_students/references.py new file mode 100644 index 0000000..0fd9ab5 --- /dev/null +++ b/examples/cs101_students/references.py @@ -0,0 +1,18 @@ +""" +References: + [Ber07] Dimitri P. Bertsekas. Dynamic Programming and Optimal Control, Vol. II. Athena Scientific, 3rd edition, 2007. ISBN 1886529302. + [Her21] Tue Herlau. Sequential decision making. (See 02465_Notes.pdf), 2021. +""" +def myfun(): + """ + Simple aux references eq. (1) in Section 1. + Simple bibtex citations: (Ber07) and (Her21, Somewhere around the middle) + + Example of custom command (reference notes) + > (Her21, Figure 1) + + Other example of custom command (reference assignment) + > (Assignment 2, Section 1) + """ + print("See Section 1") # Also works. + return 42 diff --git a/examples/cs101_students/s_tag.py b/examples/cs101_students/s_tag.py new file mode 100644 index 0000000..102fcaf --- /dev/null +++ b/examples/cs101_students/s_tag.py @@ -0,0 +1,18 @@ +""" +""" +width, height = 2, 4 +print("Area of square of width", width, "and height", height, "is:") +print(width*height) +print("and that is a fact!") +print("An extra cutout") + +def primes_sieve(limit): + limitn = limit+1 + primes = range(2, limitn) + + for i in primes: + factors = list(range(i, limitn, i)) + for f in factors[1:]: + if f in primes: + primes.remove(f) + return primes diff --git a/examples/process_cs101.py b/examples/process_cs101.py index 0268b19..00bebbc 100644 --- a/examples/process_cs101.py +++ b/examples/process_cs101.py @@ -1,25 +1,3 @@ -def main(): - from snipper.snip_dir import snip_dir - from snipper.load_citations import get_aux, get_bibtex - bibfile = get_bibtex('latex/library.bib') - auxfile = get_aux('latex/index.aux') - - references = dict(bibtex=bibfile, - aux=auxfile, - commands=[ - dict(command='\\aref2', output="(Assignment 2, \\ref{%s})", aux=auxfile), - dict(command='\\href', output="\cite[\\ref{%s}]{herlau}", aux=auxfile), - ] - ) - - - output = snip_dir(source_dir="./cs101_instructorm", dest_dir="./cs101_students", output_dir="./cs101_output", - references=references, - ) - - a = 234 - - - if __name__ == "__main__": - main() \ No newline at end of file + from snipper.snip_dir import snip_dir + snip_dir("./cs101_instructor", "./cs101_students", output_dir="./cs101_output") diff --git a/examples/process_cs101_references.py b/examples/process_cs101_references.py new file mode 100644 index 0000000..5f90dba --- /dev/null +++ b/examples/process_cs101_references.py @@ -0,0 +1,15 @@ +from snipper.snip_dir import snip_dir +from snipper.load_citations import get_aux, get_bibtex + +def main(): + bibfile = get_bibtex('latex/library.bib') + auxfile = get_aux('latex/index.aux') + references = dict(bibtex=bibfile, + aux=auxfile, + commands=[dict(command='\\aref2', output="(Assignment 2, %s)", aux=auxfile), + dict(command='\\nref', output="\cite[%s]{herlau}", aux=auxfile), + ]) + snip_dir("./cs101_instructor", "./cs101_students", output_dir="./cs101_output", references=references) + +if __name__ == "__main__": + main() diff --git a/src/snipper/__pycache__/block_parsing.cpython-38.pyc b/src/snipper/__pycache__/block_parsing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..111a915440a38d68e7f4fef83ab63cc6392d7a7e GIT binary patch literal 2572 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 KEG%3c99#g2TO~;V literal 0 HcmV?d00001 diff --git a/src/snipper/__pycache__/fix_bf.cpython-38.pyc b/src/snipper/__pycache__/fix_bf.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb22389410ff1cb4db353975d3c62a897782d50c GIT binary patch literal 2163 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 MnC4&-Vdl^R08EAIYybcN literal 0 HcmV?d00001 diff --git a/src/snipper/__pycache__/fix_cite.cpython-38.pyc b/src/snipper/__pycache__/fix_cite.cpython-38.pyc index f4f2a6a15da1ceb5b8ad897d8a875183ee0e919c..de2b1ae6ef85a05fa5550eb02dee7e157751f4bc 100644 GIT binary patch delta 1599 zcmbO#d`grrl$V!_fq{YH&v}!?n_Lt5WEjIHYRlLQr?8~3wlGGqq==+&q;R${MzN-d zrf{Wjw=hPrrSPQiwlGAor--HSrSP{fMscKwrwF77wlGF<rU(TyXi7}1v8j(@U|=X= zs9~sKT)>#Zn8~n^DTS$*sTRU#PG?xi<iZfEP|IAxRKrlioWdvxB3ZyBt0Y4TTQ5s3 zL!m+mGt>ljun8Ps6BvRSG&%ilF(;N*XfhVDFfcIO;z-WV%}vZpDZa%HVb!PHV#_Zr zDJU(`WCklKVq;)nC}IaGW=qOUDoF+D<zQf7&}6yAo|ah=pIBOPi#a{D<Q6}a7hhbE znw*)KbBhZk0M)~Ci!~=RFSYm<S5azOYEf!la%%A{w&Iea%;b_=Ov$+`8HxlM7#My{ zwqdmN<OOL=&MZkR$;{6yW@TVtC<cc%qX?q_qa2e6W05dOEo)3(QEJ*RshH%<lGJF` z;@Ilk%6N!<@u_(wMU}OaCo!j*DlsrHWHQt+#7f05)iTyH)i9<obTHH~r7%b`h%sa` zPG%}(31-k_yv3Q9nVg!Fn3S4RBskfTMMMVdZLqtFgg^nnWs{PbT#}rhTVN+JxrD{M z9vp_u$+;;-{GbqFy2YAVlA2q5O9bSB;>^7CoYZ)@e>K^PctK$a(u4@rD3%mZ2r}gu zK`fF2DFNA5#Ld9Kzz6a>C<Yjq#25t_!D7kGpp*x~lQmf#>t#SLW8~6gE|LeaSPP0W z^Ga^9WaVe(X|f>M2y$VO0?2ewKtn78+r!Dgz#sy*hf#o$kFf|OmdpgQ1B!VV7#KkA za0V%KVPs%PVO+pa!?2KX@*CD&Ts2G!SWDOzG8R9WyoF7X(Pr{BwkSD8kX?)n3=CDQ z;22uTbc?mPB(bRE7ISuL<z!cOeWsU;lgrq>mBm30XDoua3G6zM!)|fe<YX2@g4<y7 zGj>}gWsr#=SKZ?B%u7kFa4jmzFS^B+SWu9fmvW0aCpB-f9fx@REw18%oXiq%deh{A z1OX_;-eN4i#h4kzk(iSMik0G9j9Irh;n8!8xul}x7IRT*T9G=)JuF2f6(vP_AS=O6 z0uvy26@kJ+5)=*~Nd`tPMixdECJ9CkMkYoMMjpn=w>bm^xfn$l`546*`53jB!5Svc zld3mj0EJsIM+rj{V+~^plO%%+L##|Ka|t6TFGECXm?c4JlNp?k1!_Qfmc`HQ7GGX! zImpq_tfR>YOL2UT#l@*bpo{_z4R9iW#``Un%)GRGu)-o)1_lOb3WIy(79S{?!ULe# z0^}!9Krw**A;nnaFnKED4gMl{OctpyFfdGRW>Tsz5(5Q{4g&*&n<nEej_lM_P#hF% zvK7H2smK7N1eE)VKprnL1!WOlNOXXl11Vm>$r6;zi_AdE%|QgnAw`xTmK6g7LljeL z5hNaLKtf>W^MgrnY=Ba|-Q)mf{dy3~Pm?i3lc@-lo{L057BYr_L>NOfxo(LS=VcZY zq!#Jr<R_-Y!^@W<CQx+q!6j4E6O${8K$#3&%z*P8I75KrvB&}B2#{4s$rbDxRgiOT haoFVMr<CTT+JRDjG00;)j6BR-oLp>NtQ_)OYyg^nYt;Y% delta 1315 zcmX>lI#rl2l$V!_fq{Wx^Bd#DxttUEWEg!WYRi}krm&>2wlGGqqzI+3rLea!MzN*{ zr*Nfkw=hPrrHG{Pr0}*dMzN>xrSP{fL~*1D1T$!gPHeNO*J5B`s9{WDlw@#Wh?S{j zE@7x)s9}bP)G$jj)G(zm^|I756mrxs1T$zd`$h5PrIyDRrKY78rRF847HcxzVk<5w z%1kcNWV|JkmRS*BoSB!NlZsHH$#RP|Co?a#_!diMURr(;3j+hgE#~Chl$8ucjGUAA zFxo2cf%GBF1gR)y19_8?hf#{Lh<kDZ#|^Ptj9f*G3=9k_nQpNbmn0UI++xm7t(@%6 zsZ@W9GcPkaH77ACHRmNriJK<lEspHe)PmH!lwwV`TP#`mnR!J_Ad9&?^HNePT#JhG zi*B(N6lLa>++xm2&AY`_T#%Dl0(Pe+ZxP7yA`Xyx=ETwph?BVx4!p%!e2X#j7Gu^e zPLQAuI4Ev0msFHQF{KvWVl2AFQdCk=QpCx?zyKjYkygaYz`(#c`8%h+Fo^YwH72hp zHSHE#Mru(`V(DZjrqp^p1_p*qh8l)gwHT&a##*Kt#uSDW#u}y+21y2Sh7=}oh7J(T zEY6V0IGL%CC73~zrAUB*fuV{mCONYtHM)u^w)z%hE!a{`=37j92De!95_3~;F_x`l zyv3H3nN*aT29_-XIq4RcO-g2RNpgN}ft@k~!(={Y(|QSzqd|TLds~y~7HeinYHl&u z^_pyupaH45#h#p>o12)IQl!Daz!1ffl2}@Ciz&wlV!ArW6tETipvX-ut%xr!NKMX6 z%u!-sV1UN17^47V5y;QU%%Io?<H?7a9l*)5h=+lJL6aG5Z;>d-IAM?g3&I6p2Nvmq zq)<cp7CY2VkSm~e2{7_87U?rEFeHQ00oW=A1_oXR1_n@MJA)kM!N|an!nlB;W^z5t zUcMC0TAmuF1*|1(3mJ=_Om<^cWOSIE!5SqBau+xyR<VKuy$EE;<Oi(!Ohtl|1=+k+ zRX{G12N7t_;<CxfEQW-(>*RViTWOGw!0EC`1eDm=5(_{{wn&VDfnoAJHu3r*agdiG z=@A@zC8-rfQXrG1L1E63n3Ds_XvIY`ATe1GAqO&u85GL~Ag%(40J$ZKIk}(+5;tI1 zf#Ze?6i!K*NhPTjI-n2$X<=aEVq{@tVUl3vn4HTlUeChF#VEqa$0)|g$Ed|zWW&I~ z;HSwL0zynhiXh`bF$&^x-V!X%%Pc5JEz(QQEJ-ZM%+D(Z`z}fZCJv?Ja}zW3ia?QG z#0@e5lpnwe0USeMJ77-LM$SUFIBatBQ%ZAE?LgVQ7!(scj66(StXz^@tX!-B^f)g@ diff --git a/src/snipper/__pycache__/fix_o.cpython-38.pyc b/src/snipper/__pycache__/fix_o.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ea6cd8ef8b3a3776ae9e2d534579277d4ab91b8 GIT binary patch literal 1387 zcmWIL<>g{vU|=|Q!6dPhm4V?gh=Yuo7#J8F7#J9ebr={JQW#Pga~Pr^G-DKF3PTE0 z4pT036mu?16bmCm3Udle3qurZ3S%&XChJR(IewZ<x7afCQd09uZt*7N<R@px7Zl|u zrxq6{GlS%zSb%|n0pt*8kX2la3=AoZDNHR4wais2B@8tTDa<8|Sxhy|HB2ciy-X>r zz05HzwJf!)wQRNQ;S70hEDR;g*<3|Mj109Lj0{y8B`hgyHH^(nj0`0#DeTRRV47nA zYYoFfMi8%tqlqz`!IU9|p_ZeLBZ484A&)77p@ww=V-Z^kTNe8QjvB^=j4@2LoV8pv zoGF~*3^iQh47J=~InEmHg^WdON?203KxXAdm2lN?r*Nn6r117K_lwo?)bK3eP664* zvXF5hBO?RIra*=uh6sifz8Y?EhFabf{uG8Bo?5;dzAWAv-V}ip!Ct0X{t}iNevpYJ zYzz26b}%xeFa$Gb3i&0oLW2oJaWXJ4urV+&M1z7VkAZ;!96*ec49$$SY&9%3%qa}n zoJDdeOf}5n40%i`%;F3wEaD8c?CA`(95oy@Y$>eb3^i;iY&Gn&8B*BiGK({$Gnqr= z7$7v*L=KRNAg`x0LPS#-(wKr7G`akW7#SECZZXE+VyI%#E2$`{;>ye|$S*2UD6TB7 z5-zSR)+;Va$uBKYuvN$}NX^qwuVPo%QBW^e*L?Z^|NsA0(kMzmocMT%j`(;@#v)LX z)8xFxnv<EAT6~MUxFoTtBtEsGq$u$gXKG#wl*yb}RC0?gzqF*FwB#0Bc6nk^dhso$ z%#>R!`K2Xkx0rH_Rx%WUlGQI0XRDad;?$zzn3B?r)EJli<kH;KyprOW;=Ig)g4Cjz z;-X|YJ1w&!K3}h(@)jo~v8I*gaexAv4;1u_Jd84oEQ~UY986-2RT4#|dGYyrHaYpp zi8;k~dT<R@%*u-Snv6x<3=9mKOt+Xz64Q%>7#J9;xSSGG6w)$tQmquKBs~?%6N?p+ zOG`>J^U@VcGEx<k74sEJ64P%nazzQp2Zj2@`-g@EgoeaBI|hV=2Dw&=fV3*4<(KBA zDCCyrCgr5+>FM2KEU#iMElJb0&}6*Dk(!sBpOTrEUd3KeS(1^TrvQ$t5KYEgti>6r zIXRmAMWAeVi!-e>FS#T?Kd1N>dqHARNoHcsEl$^p<kW(a%>2AttOZ4xc_p`4vhp+Y zZZYK--(o39EXlaVQCyIdS&~{&a*HjsBD1)p_!bK&f^V^w7iE^D-r_1QO@e0bD4yhu z)a2}VSo{>F=H{2BY6=vwgOUyl$Rpt30f);iHgHrM-C|G4Oi9bkOS#3G3X1L`7La1b z)LTqx1-F<|E220+`r?a9^KLPw-C|D8O({|at0)FXG=vZYiLipB24q*U1~}q4IGGq# z7zG%)7&#dE7{!>_7zG%47<m}Em;@N57&$<qOdO0w`k)wNE0SPfU?`FW8N>%oBYHWh t>50je;Is)2L9kMYji5vZVS(-Du*uC&Da}c>1LcxpP)_Aw<X{qD1^}`UX}tgd literal 0 HcmV?d00001 diff --git a/src/snipper/__pycache__/fix_s.cpython-38.pyc b/src/snipper/__pycache__/fix_s.cpython-38.pyc index b161b2c89e36fa9e9f5a4b97990c7c3ba4833196..b0366cef21b20d77bc837f78f88a9f18d9d97a4b 100644 GIT binary patch delta 713 zcmbOr`+|!vl$V!_fq{XcWQ$2+I_pF}nff{g28I-d6viBeT*fFyMi84Rhbf9Fg&~DG zhdGxeiiMFOg(Zcxg&~SHg)N1>g&~S9g(HeRl_P~So2jTOl{J-p0p~)7X2vM4RIUZw z3mIG(Vl`rzqIjZsQ~6T3QhA!07#ULeQn(lJr}8djY-R-WctCVvK#Dd~FoP!VOOTs1 z8E<i?q^2d7=9HvlCYShWGT!1%%E?d8j?XMfElMm&o&1e+2HP!`yu{qp$&<P4WNxv7 zrB^a>&m-Gq}Z=e2X!sh>d}Pp@?nrCPphBE}NXp;*#Y2+yXn^$$z;lGnp9}7;dp8 z78IoBrQBl8EJ@8R)?~ZInv<EAS{%g&Hm$gb1FR^&w4|W4q=*H?VM@-28VfQT6o9u_ z(^E_0i$Qi3^MM@C#KFkND8k6XD8MMfSR^!gF83w*TZ~-481-NN{r~@e5hKVP#`r2` zy@JYHjOCi#MM9G&@W|AIG~Z&$%FoQZ#gbo;ns<vMDX};e<i%UeIjMQKSj&qtOHwsC ziugeWa6w!ZpORU0i!&`VCpEqxu_WUbdvbnWNormR*tJniIXSnO^Gi!^F{Yurnyomo zEH%DZfPsObSP10o$y&TZjB1l@d4qj~85kI<xNPA-Q{WbNa(+%uYH~?teqM1A6Ugrp z3=9lWqQ!Zc1qG=^df)(yFGwsZ&df_M0tIdn$kZaW$=rN$5g=QN!2V;*zQtH}i?yIA zGq0pb24oH>;Lsh%VUwGmQks)$2MViVF$M+(9!3sE9%c?M4sI4+4rU%k0Y)xHK1Kk< CzPbwl literal 3728 zcmWIL<>g{vU|_hr!6fmXFayJ55C<8vGB7YWFfcF_uV7$cNMT4}%wdQE(M&mvxlB<^ zj37314s$L`6iY5^6e}Y`ibx7e3Tq2v6k7^g3VRDf6nhFs3TF#L6h{hI3U>=b6erje zt|+b)o)q2|hA8e7z9^nl-W2|9rlKXOoT)qu_!crWGe+^J@-Glr$l$^ds}aK#B^V`? zDx4yaD%i}#$dD?WBDg>#RcIk&Gb5NM1fmPeQiP*KQy7C8G(}&6{NSg_c#AtFH7&6; zrz9maxkQuc7FSwnPELGrNl|9OE#9P@{N(KTf};H7)Z*e~7LXPwW@BJr0AXj4&pj9z z7)lswm`WIH7@8TInOqoRO=?+c7;0D~85S_rFxIftFl93pY1A<1F{LmrWU6JYVNGGo zW-a2YVX0wFV`5@pWC&&`<V#@+X3%8zD`I3|V9;dp^V4L$#afh@m!5iyIVUym7E4-Y zUWz8$E!LdOywu`b%q5BGw-_^TF=pLj%)Z5%nU|7Uv67*PkAZ>Vmx;4gOlWaxQE^O3 zX+~;{OMY@`ZfaghaZGVuW<fz}QA}}BGMt^3SrK2XS5SG2Da{BJo5kP=XB1+TVdP-s zV=NM2U|>jQf_fZ8F*7hQfN(O%^E#ktXRKk!WUOTZ1wsm=Bts2jGgB>74O0q3Hb;>{ zp+*f;7y~0iAx8>BFvw%K*s}67^HNhlp$`e-;)0ya5>4hJ4h9B>B2ESdhFcuT`FSNE zCxbl%A^1TO&`8NFNi9k&N#y}K0peZ`CO$?EMkb~r5p?&0JOGL;keiDIm>3v37(q#_ zl}VDJlOcsEg}H^HmMN2=mbrwXhM|M8nJI>;mZg@poiU9OoK!h#*=kt8ez61jpoR?+ zwKc3M3?MU_8Ee^7*lO6b*@|*H7-uu2u-CAsut_q^W|+&I$JD`C%TdEo%Ly{Oh7%Ow zDIDSqDV)75F-)~wwcIsaDGb?6MH@O8397kK!kEIffC*$f#AZnHE@4jL2D!Qkl+d`c znI<q6xz#YFFx0SsTnADu3F2`hcs1M*{pn2N3^fcjTwoW0{UXj#%TvNq!_&c-!qdxC z%Ui>n!Yj$JfOR1QBSQ^S4R14J4Q~pc4MPoYFaweu%qjdfXeQM#rwG_Ev@^6brZJ@m zrU<og)biEvL0nVA3JT*I<`iKYh8pG+5gP`u43{KB4L4Lq6f7eKHkAeJepZnC(-}b~ zfZSWd1>!^850(e}FP%}Gp_adfzlJ%5*9PQb=4QqkX0VIdYnW5SZ5SYNQ^TAh0kR>O z0TiQ}l725i2|O8;^FS#agh6ajmJbFcF&_p7kbgmGn<0fsk|Bjz62xbbWDsWn*;xY- zWldp&xeBa`4XlbCNfif(9n7G~>354YEwiY&<Q6M93xG5IE!Kkk;u6D`Agf>g|Ns9# zM8N14OHN{O2{;pIG8PGgG6^V~g9VDfxq=0hqa;8sg=7XDMv?zajBNj_B*CV~gEN?( zO-_DtVotH09!NEcXF*v5l=neq7bk%|yMQ5uaUo+ZV+~^pQ#Nan5;(dUQ<%lU2_=uI zhM|P9gQ1xbl9Xyer5{r>NG(&5TMA1GOD1D5LkUv~YYJNnLk-gc<`njYj4q(^kr7lh z1T$!I`2Av30Q<X05|pzUP+SCx50Hz%!7s+Zz>v;R!w|~{a?b*W6o!S26PXHGf*CX! ze=*u>GTveZ`%shV7L%UAEk;OKA%zc@O-^QUNpgN}ft@lalt2;7#=yi-Wr{U?(o;*~ zOA^!L6N}P|5ut`K;}@f~CQA`JC?Y@w%PkgA?NOu%Dz}+$v4G0wB2dW`#Q`&9B_p_4 zLvj%hvK?|DH-U68Fmf?!F!C^RFiJ31DH3#Bkvzx=3LpX$gqlo6py)5+1_dW0SCyno zW{N^yeu)C8=1|B@EJ@DD%u81&NlY)&0O=725rQBB6u?C?=mjSz{6QF;D%_x{qJyD^ zF@;f_VIio9XQ*K;VeDXRW)x?rVFD%nTIL!iaC)(-VE|PCU@?|FmK5e%mKx?7mNZZu z$KnT$aY)>Qyr9X12uw}ZB6X0HK%R(V%c}rI#4VP@qIAP37EtzB$rQy>4Dte~o<Isv z_QaxekUY3C0trh2MiEdI#>B!{C4)6Ap<0TxK^B6tPZY=^qatZg0P+==Bo-mk!7Y}& z#N1R(HgJ7-i?yIAGq0pb7Nn5VwIVsSpd>RtuSf{QXD-Mu&=e?wS7u;UkZcQ&o+3?< zm3j;e3{jlHA&x;I@ga`xQS7dME?@>zrco4AmeDPy9HS_vWTPTou(?S&*$@wdDqV2K z2U{o#N<A*%GK+_agPDbqkBNtagOP<vgi(%>g;9c$14@c9axrr;*)Z~eAq$fLqX-ip zrxc?UqX{DiIMwkmf=xupiJ%k?$}ixyP8hhYQ^MHHSj$+#RKt)0Y43>Dg4#Juph8cQ zVF7au(?Ui@hC(4wrCP(dfCb!2lY&&MDa<J>EetiRpt1mzB5W86&1#r~88lg|q!ofv zOG=CK6hO_CVuiG#{9FZfP;M$#SEyoERxB>k2PIKZ@d)xQ2!jePkmJC`1SlJUiwQAM zF_FR`32Gd2fQp-m%!LAw;-Uzg=t0G&CUcPzC|DVjZ!zXT!WAS830D<R%z(lb6pNse zf(ukyNP;pg))GOJwFp#Vfiiz_3AhPzi!HIBAT=-L7HeinYHqP68#qBkv4Im}agiy= zYPS5+l7i9_u(xh8CFi5L;ub5|KCs_FPGevKHONF5Sr|bnyT}F<7@z<E1q29#+zLuz z;Fwwkjww)!w3fMqF^g#da}DD{#$ve=#sw@O79&FqGpJ&BVTi4R#0)6EW^)#~r7+eo z&1RU(l+IYgD$Y>Ln!>bzt%h|WV=Y?=dkwe-0Gr1FisclB6mT;`oS}rXhNYP?g*BU{ zs3C=|hDDqqj|oJku!}RKa5OXZ3)HgLurJ`MVFRU&a0XL`K!zZO2!=v6a23qySH+~P zP^1NFzB1~+{QLj^e{lJ5i!r{68PqyuEZ5`)d-)bqe(^1qg2a-HTdbMIDVasLSaY*e zGK-2VL0Jl%BU$nbQuA(cBqbK7f--FpC<)(UEicL}N!8?l!~+*3=;Ko|i=sHwGILVn zK_*7AL))F;D2Zaq$%$glFD<#nn1&uTY{iLXsqw|}pfm!?^$g$~#KkDU$iXPU$i?Wu zSmepTz))qw<zvX@W2B(0SZtff<zozHBysteaQT=jaQT=iaQT>n#S*!EEEKqWEWw;4 zuJY8p60Tbu@$tEdnR)T?RjeS7Sb@W*ipv%bUcLfVI!xf80u!2Iw>Z;E^O8&Q^K*)C zaVO{J<fJBp%BEs)0*w*@<*kC$B0VS#Hopjzii$w_2wb{>(=WIn00(%HBPd`&zPZIz zYzT4_$Sv{lx46KSE;xf0S%K7mV*ph4-eN2(G60Eyqk}OKDfB=I8Jx#%aoFVMr<CTT q+JQ23F(@yB3Lya|E_M!14m%Dm4kiv|7G4f!9&mv2F><l6FaiLusv@ue diff --git a/src/snipper/__pycache__/legacy.cpython-38.pyc b/src/snipper/__pycache__/legacy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca245802a659eb097945ed2488813d2fc128f72b GIT binary patch literal 1790 zcmWIL<>g{vU|>+bV3Nqn$-wX!#6iYP3=9ko3=9m#3JeSkDGVu$Eeuf%DNIp}Da<J> zEeugiDXb}MEeui2DeS=vnjFcDNSZ-<Sr`}?oMHM)7-|@r8A0R%#)S;cj1mmBj5Q1? z3^k0cOp*-244RC7noPHtb5iqevE>w(6lE4@GTma#xy4wvlA(x|fq~(dsk2o~XmM&$ zaZE{RMrw>pesXDUYF<fkOmSXjK|yL!OmR^%oSl=Jo|s&zS5SG2Ei*4AHLnEZxMGlt z7#Kwui`W?$7^;|+l$1~$4U%SqIl6~|fgv62$OQ}~j0+iSnQE9)7_(W6T51?TA~j4U zOeKuXjN%LnnNpa<8ETpHm<n5Jm{S<D8H$`rm{XXW8Jieu7#FbAFw`*BFoV3gkdcuA z>}e(tuZE$9IfVhl5@)DoC}FB$Y-UVh$z~~HDPc`vtzl|rVq_>}sbQ>PNMj0S&}8#_ z2@2qs%pkXmCFkdYe6LWFn68kQU!stllV6;g@)8uIRiZihdFcvJ6~zjfc?xNnIjNd# zw^-6L^HOfHJNx_ky849_fx_e#r)x!WYC%b6e%>v%#Dap<yp&t41x1;8C7LX^7>lD= zlJj$mZ!u=xV$1?Z1B3vD(k<3>kQ^r{h9y8QV`O3CV&q_yVq#+CVB}yFVw7OyVdP;f z5@ujvNCvqYqzHtC7#J8p+Q5k;jER9Eg|U`7g{hV$ouQUBouQU3ouQV!h8dJtni;bh zCNLFwfx=3Xp_ZeDV*z6gC^k52I6<*7fvLz0B38qh!VHcJ#%z|Nh#KZvuDpm6rW$6D zXbMX+lM6$vM=e(k7t~bNq8TO3DXgH_S;*MT$jDH_oWfSa2nzEO))aOStBJ9Op@vJ6 z0b(P_wxTpfhQbYnX&~3saDhw)#a=CU4RZ}wHq!*gA_0hw8cqnkfDPnUkeUVT3mIy8 zAfgZwB3Hryigj@YNIC$k;?1)HnJ>vu!-*tHq~03#1spZp;B>~bfHQ?-A!99H2}2EE zGh+&KHsb`wqJk8LJb@a%6lO_=1)M3I3mH=wB^he@Q#co})PQt=M4+j1A!9914L3+W zg`tKM<O>kbhM|V5hB=#M0%Os&8txP>Nl+#R(~xk2s^hW&rF!lfo*Ld7esG!x=Kzr3 zLE#4RSqgVATMSdJK&@a6YmI<8LoI6!+X9XnL9qM5;akg`!c)tV!dt_e%{GCl$RmYm z0#mU^30D(ip-2tu0*)FEuv+F6W^;yGW{_A7OAT8Mdm2kHgC?I}5eEYUgWF3`LeCUX zXJEL+$W^6MmReM)kd&CBkeZjGo0FNBs*t5no{^c8s*sjhk(rkc%2QRMPKhZBU`b@D zUyQcD7!_Xr|NsBLCfhAmkbLni?&6ZfqLTR3ijtzlTb!wRDNrVJVo}L0mW<TIlv^w% ziJ3VeRZNOWn%qSo*A#IxFfiO=ElSKwPc7mHv01Y6GxLggK}^;na6V-P7lTE7V4>oI zoXnD2e2&G%sYRg7>RMEkU!*BiBngsbE=f$k#hH|opPU_^R+?8N$iTo5#h#Lx0yc*Y z>`bF5sDk{`l3Q%ar6u{LB}JfId5bBfq6m~FqgV@)@>42{q!<_&iljkCu$1H$#NT30 zEJ}}J%LAKoixuomNGSj+mW#ke0WZYvf};H7)Z${00mVU}oX*GshkT42j9iRdj3SI| zOhSx2jC^385F;Ct03#Qp9HR!42%{PkNQDZc1d|9zj7bNqhl7!Yk&BTBB*#>w!@$7c rr^x~?!Hal6E(H}YU|}RjAza2`lbfGXnv-e=3iV=;V>wt^m^lOiTj8Qs literal 0 HcmV?d00001 diff --git a/src/snipper/__pycache__/load_citations.cpython-38.pyc b/src/snipper/__pycache__/load_citations.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e79c0857a12b2136f7aa6dcb235a3dc9c4293688 GIT binary patch literal 4979 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 VHTys<ULHms77ld|4=#QVcL0osoqqrT literal 0 HcmV?d00001 diff --git a/src/snipper/__pycache__/snip_dir.cpython-38.pyc b/src/snipper/__pycache__/snip_dir.cpython-38.pyc index 50eb77d4a6c15593ccc6bfb963a021aa51a4b489..a8205b9d82839835b55f286b4d28b2c5c8467e71 100644 GIT binary patch delta 1407 zcmdnRzg<8(l$V!_fq{WR@0dwqA|C_8V-N=!voJ6)I503U6uV8-HW6e<Va#EVVozaD zVNGFcVT|IKn4wzFhNNDSfq{XWfq{V;q@{|9fuV*ug&~`%NUVe*g&~C@ogsy>m${a? zh6OCDl+IAgQo@+Uw1ByWaUo-|MG4~q77&Y(0V>M0kg?bgEXuTy5h}`B!?cjGmI<ta zwT7X7A!98>IV(tOEhAWht%jk7aUoMtM+tihQ!`@|V-0K3v=WX5oFFwIgJ2fbFxG<9 zvZOH2VX9>>;YwktVNYR|WSGs6!dAmJn_(_XGt6SJBa7}JOaZH`XQ<^U;aI?3!vV68 zk)ecV0dERB*tL8m{56ctj0*%(I2JNCGuCpZGt_d`aJn$WI@NN6ohb+=g=)A#dKWTw zGPE<KF{W^)aJ6vMu(&WZGuHCda4it7;Vu!W;b~^%VMqgq!vw}ckreJ)-Wo0!hWgk& zF-*05wftbCL~HmKGEQJDPACzp;cI4GAfCbl=66kCEKC5afLIAtSG1^xFOLf>E&<ZH zkg-;vhJONMQ9?RH3U3PE9Hv^qOom#abcR~tOom#K6#iP#6#iPV63H6A8o@NC8nK#s zrW(;2VUX`@m{SDiu-Ax8XX<0D6|WIb;m&56z*Hnu!c`+an;}K8hHo~*TxL*k*6`Md zn={n%f*b|%Lu^nCQ>_Hl3#>H~3mI!AYWOEG6)S<ACIu3iz*H1g!ni;h#Aam3lL5<! z)<{^S2#GT+WSqcMqy&x`2~f~aU@BCq;SFZc6!z0(zQvqaT5*dlDKn`gwc-{>a(-@Z zVqQw|WM<}gX?6w%hAI{XT|0&JSu;W4q<s~emX^Mjp4Q|hW;M1Fh6M~A43pO}D{(R2 zVl6Jn$t)@2n0%kvgVA)d28$MJ5hDWw!{iv2em+h1TbxCudGTqPIjO}_lmD?;sommC zN-R!|FGwuOxW!eJnwDCWnwOkfT*YLk@QX1<Q|1;+N@jA&WJgvffhe}3+>)Zy)LX0t zMVWaeQIjXLDzZhfr03)(P2SHMS}z&JRh(a1l$;u$l35hRk&;?m0%G0b$}cS`C<U{N zL>L$tqS#X_l5<K^QlmtZb5axY;z24i^Abxk^YcI&qL}kbOQM)E%8Nigk7CLxj^av8 zPEIW-NleN~y~UVwi!t{WW8N*sv?w-+cob_|e9Gi@Hi3E}1_p*(Y}w_BMd`&wf(#4{ zMFJojS@McfbEDXca=~6N;s8nTfCxU2dXD0}%mR?jAo1cFP+7so!N|wN!N|hI!^p+R z!^p+N!w7<Wj2uiXj9iRd%q)ydj7<O8m?gln93XW}LX7n+j2w(&j6#eOtSl@%jB<=D zjB1Q*oLr17a3}!M$jJ1Ug@x%48!H#15XfLA9!54sK1LoUAx1u?B2xwi22B<}Kfe%7 z&LVM;Gvz>@5CJ)}Ahk#jO2_9WX66+!fqcLYiocx9B;O)XsaB)_(!&WNKt@5l3-TL; j1@W^@ZhlH>PO2R!aTJ4sO#tK&Mh->}Rt81}kmw%(I2ctm delta 1257 zcmdlku!~<il$V!_fq{WxwYzSj0zU)8V-N=!GczzSI503U6x&VIHsNGXVNGFcVT@v* zSfE<Zf}~D@fq{XOfq?;}yf}=BfuV*eg&~`%h$o$)mZ^jxi*W%{4dX(_Vx<y>1<W88 zBLi5JrG{Z4V=au$xR9}y5iHJH!%)Mxkf|u3ge`@!nX!qnhPf!Ngna=AOz#5bdI+<I zv6iI<WF6BSrdrk#&J^Yv))W>=hS>}$tTimN8RoJyGeRw6s$p5kShNIe49h}BMh1w= zTDB7Q1za_3H7pC67#T{q7x1L8fn-Z~OZaM7ni&`Hr?4+%Y-X%wPiLs*Na09f$Pwsd zsHtanVTg6A<pjG|5KIcyaDoh3$k@r)&d|=7#+1UD!qvi2!{oxy%vj4+!?8fPhO<PZ zhO3#8hars#WJ%$Z6z+7!TJ98%TAmsX7lznFF-*0*wR~XnL~D2#GEQJDHY*XU;caGI zAfCbl<|j;GEHtYJD}h)HRajI}!<)wi7MB2NUC3C=U&A+nvB)f)A%!=EZw^zfKqf=2 zU^+vsP$omIa0-8|ND6<gXo+MEZ;e2WU>b9cXbpD?LyABvlO#ipNR3bpTZ-Tu)*9jI zOnr>CVl`qZ9N8=r7>nMOaMsj|&1Ogus^Oi@Fqat=#5FuMV&)9BJRrA$eG(MIR4Wek z2}_OmLdIHgkYkGffgLCX5}CkMWK_biKpMnmWXO{N%ZS#9rwEHPfP7l?4;)$Ippcrt zSop7oCzwG~#IH&zIKOnVFLRUvC=FJzDCpWLq|cfO0w?XO*tE3twe+-#7#SECCLdx} zW9wj8z|g@s`5m(oKT{DK0|Ub?*5ZPk%#tG3$x<vHj5?E(S+uySIP~=MOG^q$OD0cZ z=@)(p(yhsQi?gUSFFq|ZC$;$2WPMgE&MGE5g<p&@nlh97S)BxKu@&W(6s4x#Vl61j z%qzJy`5~(!_brz6octuPMUy$$LKP)$aTVv67A2>~r(_o0;z&s?E&;RHQ!A2lN>fsA zP3~tCGq}Z^Us@8ylu=#;^3yG*oZ=|1#N_1Ef|A6foYY&4Iky;di}*o~XUx0Bn09OO z1vZ&_K@guUyF9Tdy|_q#fq|ijkAZ<9iY2coH8+aAC>QMVB36(DD0dcdgVb{r=Vcav zTmZ`X#aW<S%*esW!pOtO#mK|R!^j8baWQf+voJC-GW};`l>o^xaWL^Paxn=pvM_Qm zaxjW9iZIrTv9d9;u<$U-F|shKF|u)TF|xp+1|t`v5Xe9#9!54sK1LoUAx1vlA}a<4 z22Ccv5KWFEVUW9|Kn~^yIjJDENDoREF@YS)UXWOlk&~HJ1S(aE<Up!Hx*`4nc}f++ eg!sxPH$SB`C)ExVam5@A3=9IyJWM={9LxY$x++Nk diff --git a/src/snipper/__pycache__/snipper_main.cpython-38.pyc b/src/snipper/__pycache__/snipper_main.cpython-38.pyc index 6a50df2c7fb13fcde076d5ddf2d54c8687e166e5..21d84830317e97a60e1ba13d898e5f34d4cfb140 100644 GIT binary patch delta 3023 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 literal 10073 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 Bo__!U diff --git a/src/snipper/block_parsing.py b/src/snipper/block_parsing.py index 0b1009c..a386989 100644 --- a/src/snipper/block_parsing.py +++ b/src/snipper/block_parsing.py @@ -1,3 +1,7 @@ +""" +Module for new-style block parsing. Make sure all functions are up-to-date and used. +""" + def f2(lines, tag, i=0, j=0): for k in range(i, len(lines)): index = lines[k].find(tag, j if k == i else 0) @@ -26,10 +30,15 @@ def block_split(lines, tag): i, j = f2(lines, tag) def get_tag_args(line): + line = line.strip() k = line.find(" ") - tag_args = (line[:k + 1] if k >= 0 else line)[len(tag):] + tag_args = ((line[:k + 1] if k >= 0 else line)[len(tag):] ).strip() + if len(tag_args) == 0: return {'': ''} # No name. + + # print("TAG ARGS") + # print(tag_args) tag_args = dict([t.split("=") for t in tag_args.split(";")]) return tag_args diff --git a/src/snipper/fix_bf.py b/src/snipper/fix_bf.py index e69de29..36ac1ce 100644 --- a/src/snipper/fix_bf.py +++ b/src/snipper/fix_bf.py @@ -0,0 +1,67 @@ +import functools + +from snipper.legacy import indent, gcoms, block_process + + +def fix_f(lines, debug): + lines2 = [] + i = 0 + while i < len(lines): + l = lines[i] + dx = l.find("#!f") + if dx >= 0: + l_head = l[dx+3:].strip() + l = l[:dx] + lines2.append(l) + id = indent(lines[i+1]) + for j in range(i+1, 10000): + jid = len( indent(lines[j]) ) + if j+1 == len(lines) or ( jid < len(id) and len(lines[j].strip() ) > 0): + break + + if len(lines[j-1].strip()) == 0: + j = j - 1 + funbody = "\n".join( lines[i+1:j] ) + if i == j: + raise Exception("Empty function body") + i = j + comments, funrem = gcoms(funbody) + comments = [id + c for c in comments] + if len(comments) > 0: + lines2 += comments[0].split("\n") + # lines2 += [id+"#!b"] + f = [id + l.strip() for l in funrem.splitlines()] + f[0] = f[0] + "#!b" + + # lines2 += (id+funrem.strip()).split("\n") + errm = l_head if len(l_head) > 0 else "Implement function body" + f[-1] = f[-1] + f' #!b {errm}' + lines2 += f + # lines2 += [f'{id}#!b {errm}'] + + else: + lines2.append(l) + i += 1 + return lines2 + + +def fix_b2(lines, keep=False): + stats = {'n': 0} + def block_fun(lines, start_extra, end_extra, art, stats=None, **kwargs): + id = indent(lines[0]) + lines = lines[1:] if len(lines[0].strip()) == 0 else lines + lines = lines[:-1] if len(lines[-1].strip()) == 0 else lines + cc = len(lines) + ee = end_extra.strip() + if len(ee) >= 2 and ee[0] == '"': + ee = ee[1:-1] + start_extra = start_extra.strip() + if keep: + l2 = ['GARBAGE'] * cc + else: + l2 = ([id+start_extra] if len(start_extra) > 0 else []) + [id + f"# TODO: {cc} lines missing.", id+f'raise NotImplementedError("{ee}")'] + + stats['n'] += cc + return l2, cc + lines2, _, _, cutout = block_process(lines, tag="#!b", block_fun=functools.partial(block_fun, stats=stats)) + return lines2, stats['n'], cutout \ No newline at end of file diff --git a/src/snipper/fix_cite.py b/src/snipper/fix_cite.py index 7fbc1b4..ad5b5ee 100644 --- a/src/snipper/fix_cite.py +++ b/src/snipper/fix_cite.py @@ -1,14 +1,19 @@ from snipper.load_citations import find_tex_cite -from snipper.snipper_main import COMMENT +from snipper.legacy import COMMENT -def fix_citations(): - # This should be the master function. - pass +def fix_citations(lines, references, strict=True): + lines = fix_aux(lines, aux=references.get('aux', {}) ) + for cm in references.get('commands', []): + lines = fix_aux_special(lines, aux=cm['aux'], command=cm['command'], output=cm['output']) + + lines = fix_bibtex(lines, bibtex=references.get('bibtex', {})) + return lines -def fix_aux_special(lines, aux, command='\\nref', bibref='herlau'): - daux = {name: {'nicelabel': f'\\cite[' + v["nicelabel"] + ']{' + bibref + "}"} for name, v in aux.items()} +def fix_aux_special(lines, aux, command='\\nref', output='\cite[%s]{my_bibtex_entry}'): + # out = output % + daux = {name: {'nicelabel': output % v['nicelabel'] } for name, v in aux.items()} l2 = fix_single_reference(lines, aux=daux, cmd=command, strict=True) return l2 @@ -18,7 +23,6 @@ def fix_aux(lines, aux, strict=True): return l2 def fix_bibtex(lines, bibtex): - # lines = fix_references(lines, info, strict=strict) s = "\n".join(lines) i = 0 all_refs = [] @@ -37,7 +41,6 @@ def fix_bibtex(lines, bibtex): s = s[:i] + rtxt + s[j+1:] i = i + len(rtxt) - cpr = "" if not s.startswith(COMMENT): s = f"{COMMENT}\n{COMMENT}\n" + s if len(all_refs) > 0: @@ -51,6 +54,7 @@ def fix_bibtex(lines, bibtex): def fix_references(lines, info, strict=True): + assert False for cmd in info['new_references']: lines = fix_single_reference(lines, cmd, info['new_references'][cmd], strict=strict) return lines diff --git a/src/snipper/fix_i.py b/src/snipper/fix_i.py new file mode 100644 index 0000000..e69de29 diff --git a/src/snipper/fix_o.py b/src/snipper/fix_o.py new file mode 100644 index 0000000..5caf0ac --- /dev/null +++ b/src/snipper/fix_o.py @@ -0,0 +1,38 @@ +import functools +import os + +from snipper.legacy import indent, block_process + + +def run_o(lines, file, output): + def block_fun(lines, start_extra, end_extra, art, output, **kwargs): + id = indent(lines[0]) + outf = output + ("_" + art if art is not None else "") + ".txt" + l2 = [] + l2 += [id + "import sys", id + f"sys.stdout = open('{outf}', 'w')"] + l2 += lines + # l2 += [indent(lines[-1]) + "sys.stdout.close()"] + l2 += [indent(lines[-1]) + "sys.stdout = sys.__stdout__"] + return l2, None + try: + lines2, didfind, extra, _ = block_process(lines, tag="#!o", block_fun=functools.partial(block_fun, output=output) ) + except Exception as e: + print("Bad file: ", file) + print("I was cutting the #!o tag") + print("\n".join( lines) ) + raise(e) + + if didfind: + fp, ex = os.path.splitext(file) + file_run = fp + "_RUN_OUTPUT_CAPTURE" +ex + if os.path.exists(file_run): + print("file found mumble...") + else: + with open(file_run, 'w', encoding="utf-8") as f: + f.write("\n".join(lines2) ) + cmd = "python " + file_run + import subprocess + s = subprocess.check_output(cmd, shell=True) + # s,ok = execute_command(cmd.split(), shell=True) + print(s) + os.remove(file_run) \ No newline at end of file diff --git a/src/snipper/legacy.py b/src/snipper/legacy.py new file mode 100644 index 0000000..c122039 --- /dev/null +++ b/src/snipper/legacy.py @@ -0,0 +1,75 @@ +def indent(l): + v = len(l) - len(l.lstrip()) + return l[:v] + + +COMMENT = '"""' + + +def gcoms(s): + coms = [] + while True: + i = s.find(COMMENT) + if i >= 0: + j = s.find(COMMENT, i+len(COMMENT))+3 + else: + break + if j < 0: + raise Exception("comment tag not closed") + coms.append(s[i:j]) + s = s[:i] + s[j:] + if len(coms) > 10: + print("long comments in file", i) + return coms, s + + +def block_process(lines, tag, block_fun): + i = 0 + didfind = False + lines2 = [] + block_out = [] + cutout = [] + while i < len(lines): + l = lines[i] + dx = l.find(tag) + if dx >= 0: + if l.find(tag, dx+1) > 0: + j = i + else: + for j in range(i + 1, 10000): + if j >= len(lines): + print("\n".join(lines)) + print("very bad end-line j while fixing tag", tag) + raise Exception("Bad line while fixing", tag) + if lines[j].find(tag) >= 0: + break + + pbody = lines[i:j+1] + if i == j: + start_extra = lines[j][dx:lines[j].rfind(tag)].strip() + end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip() + else: + start_extra = lines[i][dx:].strip() + end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip() + + cutout.append(pbody) + tmp_ = start_extra.split("=") + arg = None if len(tmp_) <= 1 else tmp_[1].split(" ")[0] + start_extra = ' '.join(start_extra.split(" ")[1:] ) + + pbody[0] = pbody[0][:dx] + if j > i: + pbody[-1] = pbody[-1][:pbody[-1].find(tag)] + + nlines, extra = block_fun(lines=pbody, start_extra=start_extra, end_extra=end_extra, art=arg, head=lines[:i], tail=lines[j+1:]) + lines2 += nlines + block_out.append(extra) + i = j+1 + didfind = True + if "!b" in end_extra: + assert(False) + else: + lines2.append(l) + i += 1 + + return lines2, didfind, block_out, cutout \ No newline at end of file diff --git a/src/snipper/snip_dir.py b/src/snipper/snip_dir.py index 15657af..6032800 100644 --- a/src/snipper/snip_dir.py +++ b/src/snipper/snip_dir.py @@ -16,41 +16,46 @@ def snip_dir(source_dir, # Sources if exclude == None: exclude = [] + if not os.path.exists(dest_dir): os.makedirs(dest_dir) + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + output_dir = os.path.abspath(output_dir) source_dir = os.path.abspath(source_dir) dest_dir = os.path.abspath(dest_dir) if os.path.samefile( source_dir, dest_dir): raise Exception("Source and destination is the same") + if clean_destination_dir: shutil.rmtree(dest_dir) os.makedirs(dest_dir) - # Now the destination dir is set up. + out = dest_dir hw = {'base': source_dir, 'exclusion': exclude} print(f"[snipper] Synchronizing directories: {hw['base']} -> {out}") - if os.path.exists(out): - shutil.rmtree(out) + if os.path.exists(dest_dir): + shutil.rmtree(dest_dir) base = hw['base'] - shutil.copytree(base, out) + shutil.copytree(base, dest_dir) time.sleep(0.2) - ls = list(Path(out).glob('**/*.*')) + ls = list(Path(dest_dir).glob('**/*.*')) acceptable = [] for l in ls: - split = os.path.normpath(os.path.relpath(l, out)) + split = os.path.normpath(os.path.relpath(l, dest_dir)) m = [fnmatch.fnmatch(split, ex) for ex in exclude] acceptable.append( (l, not any(m) )) # print(acceptable) # now we have acceptable files in list. - - run_out_dirs = ["./output"] + # run_out_dirs = ["./output"] n = 0 # edirs = [os.path.join(out, f_) for f_ in hw['exclusion']] # Exclude directories on exclude list (speed) # edirs = {os.path.normpath(os.path.dirname(f_) if not os.path.isdir(f_) else f_) for f_ in edirs} @@ -74,7 +79,11 @@ def snip_dir(source_dir, # Sources kwargs = {} cut_files = True run_files = True - nrem = censor_file(f, info, paths, run_files=run_files, run_out_dirs=run_out_dirs[:1], cut_files=cut_files, solution_list=solution_list, include_path_base=base, **kwargs) + nrem = censor_file(f, info, run_files=run_files, run_out_dirs=output_dir, cut_files=cut_files, solution_list=solution_list, + include_path_base=base, + base_path=dest_dir, + references=references, + **kwargs) if nrem > 0: print(f"{nrem}> {f}") n += nrem diff --git a/src/snipper/snipper_main.py b/src/snipper/snipper_main.py index dc6bbdc..2425f10 100644 --- a/src/snipper/snipper_main.py +++ b/src/snipper/snipper_main.py @@ -3,11 +3,10 @@ import functools import textwrap import re # from snipper.fix_s import save_s +from snipper.fix_bf import fix_f, fix_b2 +from snipper.fix_o import run_o +from snipper.legacy import indent, block_process -COMMENT = '"""' -def indent(l): - v = len(l) - len(l.lstrip()) - return l[:v] def fix_r(lines): for i,l in enumerate(lines): @@ -15,21 +14,6 @@ def fix_r(lines): lines[i] = indent(l) + l[l.find("#!r") + 3:].lstrip() return lines -def gcoms(s): - coms = [] - while True: - i = s.find(COMMENT) - if i >= 0: - j = s.find(COMMENT, i+len(COMMENT))+3 - else: - break - if j < 0: - raise Exception("comment tag not closed") - coms.append(s[i:j]) - s = s[:i] + s[j:] - if len(coms) > 10: - print("long comments in file", i) - return coms, s def strip_tag(lines, tag): lines2 = [] @@ -43,57 +27,6 @@ def strip_tag(lines, tag): lines2.append(l) return lines2 -def block_process(lines, tag, block_fun): - i = 0 - didfind = False - lines2 = [] - block_out = [] - cutout = [] - while i < len(lines): - l = lines[i] - dx = l.find(tag) - if dx >= 0: - if l.find(tag, dx+1) > 0: - j = i - else: - for j in range(i + 1, 10000): - if j >= len(lines): - print("\n".join(lines)) - print("very bad end-line j while fixing tag", tag) - raise Exception("Bad line while fixing", tag) - if lines[j].find(tag) >= 0: - break - - pbody = lines[i:j+1] - if i == j: - start_extra = lines[j][dx:lines[j].rfind(tag)].strip() - end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip() - else: - start_extra = lines[i][dx:].strip() - end_extra = lines[j][lines[j].rfind(tag) + len(tag):].strip() - - cutout.append(pbody) - tmp_ = start_extra.split("=") - arg = None if len(tmp_) <= 1 else tmp_[1].split(" ")[0] - start_extra = ' '.join(start_extra.split(" ")[1:] ) - - pbody[0] = pbody[0][:dx] - if j > i: - pbody[-1] = pbody[-1][:pbody[-1].find(tag)] - - nlines, extra = block_fun(lines=pbody, start_extra=start_extra, end_extra=end_extra, art=arg, head=lines[:i], tail=lines[j+1:]) - lines2 += nlines - block_out.append(extra) - i = j+1 - didfind = True - if "!b" in end_extra: - assert(False) - else: - lines2.append(l) - i += 1 - - return lines2, didfind, block_out, cutout - def rem_nonprintable_ctrl_chars(txt): """Remove non_printable ascii control characters """ @@ -203,102 +136,6 @@ def run_i(lines, file, output): return lines -def run_o(lines, file, output): - def block_fun(lines, start_extra, end_extra, art, output, **kwargs): - id = indent(lines[0]) - outf = output + ("_" + art if art is not None else "") + ".txt" - l2 = [] - l2 += [id + "import sys", id + f"sys.stdout = open('{outf}', 'w')"] - l2 += lines - # l2 += [indent(lines[-1]) + "sys.stdout.close()"] - l2 += [indent(lines[-1]) + "sys.stdout = sys.__stdout__"] - return l2, None - try: - lines2, didfind, extra, _ = block_process(lines, tag="#!o", block_fun=functools.partial(block_fun, output=output) ) - except Exception as e: - print("Bad file: ", file) - print("I was cutting the #!o tag") - print("\n".join( lines) ) - raise(e) - - if didfind: - fp, ex = os.path.splitext(file) - file_run = fp + "_RUN_OUTPUT_CAPTURE" +ex - if os.path.exists(file_run): - print("file found mumble...") - else: - with open(file_run, 'w', encoding="utf-8") as f: - f.write("\n".join(lines2) ) - cmd = "python " + file_run - - s,ok = execute_command(cmd.split(), shell=True) - print(s) - os.remove(file_run) - -def fix_f(lines, debug): - lines2 = [] - i = 0 - while i < len(lines): - l = lines[i] - dx = l.find("#!f") - if dx >= 0: - l_head = l[dx+3:].strip() - l = l[:dx] - lines2.append(l) - id = indent(lines[i+1]) - for j in range(i+1, 10000): - jid = len( indent(lines[j]) ) - if j+1 == len(lines) or ( jid < len(id) and len(lines[j].strip() ) > 0): - break - - if len(lines[j-1].strip()) == 0: - j = j - 1 - funbody = "\n".join( lines[i+1:j] ) - if i == j: - raise Exception("Empty function body") - i = j - comments, funrem = gcoms(funbody) - comments = [id + c for c in comments] - if len(comments) > 0: - lines2 += comments[0].split("\n") - # lines2 += [id+"#!b"] - f = [id + l.strip() for l in funrem.splitlines()] - f[0] = f[0] + "#!b" - - # lines2 += (id+funrem.strip()).split("\n") - errm = l_head if len(l_head) > 0 else "Implement function body" - f[-1] = f[-1] + f' #!b {errm}' - lines2 += f - # lines2 += [f'{id}#!b {errm}'] - - else: - lines2.append(l) - i += 1 - return lines2 - -def fix_b2(lines, keep=False): - stats = {'n': 0} - def block_fun(lines, start_extra, end_extra, art, stats=None, **kwargs): - id = indent(lines[0]) - lines = lines[1:] if len(lines[0].strip()) == 0 else lines - lines = lines[:-1] if len(lines[-1].strip()) == 0 else lines - cc = len(lines) - ee = end_extra.strip() - if len(ee) >= 2 and ee[0] == '"': - ee = ee[1:-1] - start_extra = start_extra.strip() - if keep: - l2 = ['GARBAGE'] * cc - else: - l2 = ([id+start_extra] if len(start_extra) > 0 else []) + [id + f"# TODO: {cc} lines missing.", id+f'raise NotImplementedError("{ee}")'] - - stats['n'] += cc - return l2, cc - lines2, _, _, cutout = block_process(lines, tag="#!b", block_fun=functools.partial(block_fun, stats=stats)) - return lines2, stats['n'], cutout - - - def full_strip(lines, tags=None): if tags is None: tags = ["#!s", "#!o", "#!f", "#!b"] @@ -315,10 +152,18 @@ def censor_code(lines, keep=True): -def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files=True, solution_list=None, +def censor_file(file, info, run_files=True, run_out_dirs=None, cut_files=True, solution_list=None, censor_files=True, + base_path=None, include_path_base=None, - strict=True): + strict=True, + references=None): + + if references == None: + references = {} + + from snipper.fix_cite import fix_citations + dbug = False with open(file, 'r', encoding='utf8') as f: s = f.read() @@ -330,8 +175,8 @@ def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files= lines[k] = l.replace("# !", "#!") try: - s = fix_cite(lines, info, strict=strict) - lines = s.split("\n") + lines = fix_citations(lines, references, strict=strict) + # lines = s.split("\n") except IndexError as e: print(e) print("Fuckup in file, cite/reference tag not found!>", file) @@ -339,7 +184,7 @@ def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files= if run_files or cut_files: ofiles = [] - for rod in run_out_dirs: + for rod in [run_out_dirs]: # if not os.path.isdir(rod): # os.mkdir(rod) ofiles.append(os.path.join(rod, os.path.basename(file).split(".")[0]) ) @@ -349,7 +194,9 @@ def censor_file(file, info, paths, run_files=True, run_out_dirs=None, cut_files= run_o(lines, file=file, output=ofiles[0]) run_i(lines, file=file, output=ofiles[0]) if cut_files: - save_s(lines, file=file, output=ofiles[0], include_path_base=include_path_base) # save file snips to disk + from snipper.fix_s import save_s + + save_s(lines, file_path=os.path.relpath(file, base_path), output_dir=run_out_dirs) # save file snips to disk lines = full_strip(lines, ["#!s", "#!o", '#!i']) # lines = fix_c(lines) -- GitLab