Skip to main content

A lightweight framework for censoring student solutions files and extracting code + output

Project description

Snipper

A lightweight framework for removing code from student solutions.

Installation

pip install codesnipper

What it does

This project address the following three challenges for administering a python-based course

  • You need to maintain a (working) version for debugging as well as a version handed out to students (with code missing)
  • You ideally want to make references in source code to course material "(see equation 2.1 in exercise 5)" but these tend to go out of date
  • You want to include code snippets and code output in lectures notes/exercises/beamer slides
  • You want to automatically create student solutions

This framework address these problems and allow you to maintain a single, working project repository.

The project is currently used in 02465 at DTU. An example of student code can be found at:

A set of lectures notes where all code examples/output are automatically generated from the working repository can be found a

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).

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:

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

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 myfun():
    print("Hello") 

A file foo.txt containing the captured output

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)

>>> 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!

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:

def myfun(): #!s
    """
    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

We can manually compile this example by first loading the aux-files and the bibliographies as follows:

# 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.

# 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:

"""
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).
    """
    return 42 #!s

Note this example uses the low-level api. Normally you would just pass the bibtex and aux-file to the main censor-file command.

Additional features:

  • You can name tags using #!s=bar to get a foo_bar.py snippet. This is useful when you need to cut multiple sessions. This also works for the other tags.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

codesnipper-0.0.2.tar.gz (13.3 kB view details)

Uploaded Source

Built Distribution

codesnipper-0.0.2-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file codesnipper-0.0.2.tar.gz.

File metadata

  • Download URL: codesnipper-0.0.2.tar.gz
  • Upload date:
  • Size: 13.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.8.6

File hashes

Hashes for codesnipper-0.0.2.tar.gz
Algorithm Hash digest
SHA256 a388cac5f0d0c1e6bc4310d98a145b81fa47f85bf9b5bb8bd77929060fa2233d
MD5 f326a3296971b3c6d2faebf8e0e47a93
BLAKE2b-256 e59217be55325b9f9aca4a1052de33d2f3d707df6fdac3db71e6679604afc636

See more details on using hashes here.

File details

Details for the file codesnipper-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: codesnipper-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 14.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.56.0 CPython/3.8.6

File hashes

Hashes for codesnipper-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 452f7e6131878e5430fd9392eafdb4f185948fc1e4d48e03ebae6bd605f6c8ac
MD5 99ea95e69d5e16a0c6a5283a6bf7030a
BLAKE2b-256 873e0895278034bc89cbed7404f6406696f995527b4956dd473994225a9f0aa1

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page