API docs

check50

check50.import_checks(path)

Import checks module given relative path.

Parameters:

path (str) – relative path from which to import checks module

Returns:

the imported module

Raises:
  • FileNotFoundError – if path / .check50.yaml does not exist
  • yaml.YAMLError – if path / .check50.yaml is not a valid YAML file

This function is particularly useful when a set of checks logically extends another, as is often the case in CS50’s own problems that have a “less comfy” and “more comfy” version. The “more comfy” version can include all of the “less comfy” checks like so:

less = check50.import_checks("../less")
from less import *

Note

the __name__ of the imported module is given by the basename of the specified path (less in the above example).

check50.data(**kwargs)

Add data to the check payload

Params kwargs:key/value mappings to be added to the check payload

Example usage:

check50.data(time=7.3, mem=23)
check50.exists(*paths)

Assert that all given paths exist.

Params paths:files/directories to be checked for existence
Raises:check50.Failure – if any path in paths does not exist

Example usage:

check50.exists("foo.c", "foo.h")
check50.hash(file)

Hashes file using SHA-256.

Parameters:file (str) – name of file to be hashed
Return type:str
Raises:check50.Failure – if file does not exist
check50.include(*paths)

Copy files/directories from the check directory (check50.internal.check_dir), to the current directory

Params paths:files/directories to be copied

Example usage:

check50.include("foo.txt", "bar.txt")
assert os.path.exists("foo.txt") and os.path.exists("bar.txt")
class check50.run(command, env={})

Run a command.

Parameters:
  • command (str) – command to be run
  • env (dict) – environment in which to run command

By default, the command will be run using the same environment as check50, these mappings may be overriden via the env parameter:

check50.run("./foo").stdin("foo").stdout("bar").exit(0)
check50.run("./foo", env={ "HOME": "/" }).stdin("foo").stdout("bar").exit(0)
exit(code=None, timeout=5)

Wait for process to exit or until timeout (5 sec by default) and asserts that process exits with code. If code is None, returns the code the process exited with.

..note:: In order to ensure that spawned child processes do not outlive the check that spawned them, it is good practice to call either method (with no arguments if the exit code doesn’t matter) or .kill() on every spawned process.

Parameters:
  • code (int) – code to assert process exits with
  • timeout (int / float) – maximum number of seconds to wait for the program to end
Raises:

check50.Failure – if code is given and does not match the actual exitcode within timeout

Example usage:

check50.run("./hello").exit(0)

code = check50.run("./hello").exit()
if code != 0:
    raise check50.Failure(f"expected exit code 0, not {code}")
kill()

Kill the process.

Child will first be sent a SIGHUP, followed by a SIGINT and finally a SIGKILL if it ignores the first two.

reject(timeout=1)

Check that the process survives for timeout. Useful for checking whether program is waiting on input.

Parameters:timeout (int / float) – number of seconds to wait
Raises:check50.Failure – if process ends before timeout
stdin(line, prompt=True, timeout=3)

Send line to stdin, optionally expect a prompt.

Parameters:
  • line (str) – line to be send to stdin
  • prompt (bool) – boolean indicating whether a prompt is expected, if True absorbs all of stdout before inserting line into stdin and raises check50.Failure if stdout is empty
  • timeout (int / float) – maximum number of seconds to wait for prompt
Raises:

check50.Failure – if prompt is set to True and no prompt is given

stdout(output=None, str_output=None, regex=True, timeout=3)

Retrieve all output from stdout until timeout (3 sec by default). If output is None, stdout returns all of the stdout outputted by the process, else it returns self.

Parameters:
  • output (str) – optional output to be expected from stdout, raises check50.Failure if no match
  • str_output (str) – what will be displayed as expected output, a human readable form of output
  • regex (bool) – flag indicating whether output should be treated as a regex
  • timeout (int / float) – maximum number of seconds to wait for output
Raises:
  • check50.Mismatch – if output is specified and nothing that the process output matches it
  • check50.Failure – if process times out or if it outputs invalid UTF-8 text.

Example usage:

check50.run("./hello").stdout("[Hh]ello, world!?", "hello, world").exit()

output = check50.run("./hello").stdout()
if not re.match("[Hh]ello, world!?", output):
    raise check50.Mismatch("hello, world", output)
check50.log(line)

Add to check log

Parameters:line (str) – line to be added to the check log

The check log is student-visible via the --log flag to check50.

exception check50.Failure(rationale, help=None)

Exception signifying check failure.

Parameters:
  • rationale (str) – message to be displayed capturing why the check failed
  • help (str) – optional help message to be displayed

Example usage:

out = check50.run("./cash").stdin("4.2").stdout()
if 10 not in out:
    help = None
    if 11 in out:
        help = "did you forget to round your result?"
    raise check50.Failure("Expected a different result", help=help)
exception check50.Mismatch(expected, actual, help=None)

Exception signifying check failure due to a mismatch in expected and actual outputs.

Parameters:
  • expected – the expected value
  • actual – the actual value
  • help (str) – optional help message to be displayed

Example usage:

from re import match
expected = "[Hh]ello, world!?\n"
actual = check50.run("./hello").stdout()
if not match(expected, actual):
    help = None
    if match(expected[:-1], actual):
        help = r"did you forget a newline ('\n') at the end of your printf string?"
    raise check50.Mismatch("hello, world\n", actual, help=help)
check50.check(dependency=None, timeout=60)

Mark function as a check.

Parameters:
  • dependency (function) – the check that this check depends on
  • timeout (int) – maximum number of seconds the check can run

When a check depends on another, the former will only run if the latter passes. Additionally, the dependent check will inherit the filesystem of its dependency. This is particularly useful when writing e.g., a compiles check that compiles a student’s program (and checks that it compiled successfully). Any checks that run the student’s program will logically depend on this check, and since they inherit the resulting filesystem of the check, they will immidiately have access to the compiled program without needing to recompile.

Example usage:

@check50.check() # Mark 'exists' as a check
def exists():
    """hello.c exists"""
    check50.exists("hello.c")

@check50.check(exists) # Mark 'compiles' as a check that depends on 'exists'
def compiles():
    """hello.c compiles"""
    check50.c.compile("hello.c")

@check50.check(compiles)
def prints_hello():
    """prints "Hello, world!\\n"""
    # Since 'prints_hello', depends on 'compiles' it inherits the compiled binary
    check50.run("./hello").stdout("[Hh]ello, world!?\n", "hello, world\n").exit()
exception check50.EOF(value)

Raised when EOF is read from a child. This usually means the child has exited.

check50.c

check50.c.CC = 'clang'

Default compiler for check50.c.compile()

check50.c.CFLAGS = {'ggdb': True, 'lm': True, 'std': 'c11'}

Default CFLAGS for check50.c.compile()

check50.c.compile(*files, exe_name=None, cc='clang', **cflags)

Compile C source files.

Parameters:
  • files – filenames to be compiled
  • exe_name – name of resulting executable
  • cc – compiler to use (check50.c.CC by default)
  • cflags – additional flags to pass to the compiler
Raises:
  • check50.Failure – if compilation failed (i.e., if the compiler returns a non-zero exit status).
  • RuntimeError – if no filenames are specified

If exe_name is None, check50.c.compile() will default to the first file specified sans the .c extension:

check50.c.compile("foo.c", "bar.c") # clang foo.c bar.c -o foo -std=c11 -ggdb -lm

Additional CFLAGS may be passed as keyword arguments like so:

check50.c.compile("foo.c", "bar.c", lcs50=True) # clang foo.c bar.c -o foo -std=c11 -ggdb -lm -lcs50

In the same vein, the default CFLAGS may be overriden via keyword arguments:

check50.c.compile("foo.c", "bar.c", std="c99", lm=False) # clang foo.c bar.c -o foo -std=c99 -ggdb
check50.c.valgrind(command, env={})

Run a command with valgrind. :param command: command to be run :type command: str :param env: environment in which to run command :type env: str :raises check50.Failure: if, at the end of the check, valgrind reports any errors

This function works exactly like check50.run(), with the additional effect that command is run through valgrind and valgrind’s output is automatically reviewed at the end of the check for memory leaks and other bugs. If valgrind reports any issues, the check is failed and student-friendly messages are printed to the log.

Example usage:

check50.c.valgrind("./leaky").stdin("foo").stdout("bar").exit(0)

Note

It is recommended that the student’s code is compiled with the -ggdb flag so that additional information, such as the file and line number at which the issue was detected can be included in the log as well.

check50.flask

class check50.flask.app(path, app_name='app')

Spawn a Flask app.

Parameters:
  • path (str) – path to python file containing Flask app
  • app_name – name of Flask app in file

Example usage:

check50.flask.app("application.py").get("/").status(200)
content(output=None, str_output=None, **kwargs)

Searches for output regex within HTML page. kwargs are passed to BeautifulSoup’s find function to filter for tags.

get(route, data=None, params=None, follow_redirects=True)

Send GET request to app.

Parameters:
  • route (str) – route to send request to
  • data (dict) – form data to include in request
  • params – URL parameters to include in request
  • follow_redirects (bool) – enable redirection (defaults to True)
Returns:

self

Raises:

check50.Failure – if Flask application throws an uncaught exception

Example usage:

check50.flask.app("application.py").get("/buy", params={"q": "02138"}).content()
post(route, data=None, params=None, follow_redirects=True)

Send POST request to app.

Parameters:
  • route (str) – route to send request to
  • data (dict) – form data to include in request
  • params – URL parameters to include in request
  • follow_redirects (bool) – enable redirection (defaults to True)
Raises:

check50.Failure – if Flask application throws an uncaught exception

Example usage:

check50.flask.app("application.py").post("/buy", data={"symbol": "GOOG", "shares": 10}).status(200)
raw_content(output=None, str_output=None)

Searches for output regex match within content of page, regardless of mimetype.

status(code=None)

Check status code in response returned by application. If code is not None, assert that code is returned by application, else simply return the status code.

Parameters:code (int) – code to assert that application returns

Example usage:

check50.flask.app("application.py").status(200)

status = check50.flask.app("application.py").get("/").status()
if status != 200:
    raise check50.Failure(f"expected status code 200, but got {status}")

check50.py

check50.py.append_code(original, codefile)

Append the contents of one file to another.

Parameters:
  • original (str) – name of file that will be appended to
  • codefile (str) – name of file that will be appende

This function is particularly useful when one wants to replace a function in student code with their own implementation of one. If two functions are defined with the same name in Python, the latter definition is taken so overwriting a function is as simple as writing it to a file and then appending it to the student’s code.

Example usage:

# Include a file containing our own implementation of a lookup function.
check50.include("lookup.py")

# Overwrite the lookup function in helpers.py with our own implementation.
check50.py.append_code("helpers.py", "lookup.py")
check50.py.compile(file)

Compile a Python program into byte code :param file: file to be compiled :raises check50.Failure: if compilation fails e.g. if there is a SyntaxError

check50.py.import_(path)

Import a Python program given a raw file path

Parameters:path (str) – path to python file to be imported
Raises:check50.Failure – if path doesn’t exist, or if the Python file at path throws an exception when imported.

check50.internal

Additional check50 internals exposed to extension writers in addition to the standard API

class check50.internal.Register

Class with which functions can be registered to run before / after checks. check50.internal.register should be the sole instance of this class.

after_check(func)

Run func once at the end of the check, then discard func.

after_every(func)

Run func at the end of every check.

before_every(func)

Run func at the start of every check.

check50.internal.check_dir = None

Directory containing the check and its associated files

check50.internal.import_file(name, path)

Import a file given a raw file path.

Parameters:
  • name (str) – Name of module to be imported
  • path (str / Path) – Path to Python file
check50.internal.load_config(check_dir)

Load configuration file from check_dir / ".cs50.yaml", applying defaults to unspecified values.

Parameters:check_dir (str / Path) – directory from which to load config file
Return type:dict
check50.internal.register = <check50.internal.Register object>

Sole instance of the check50.internal.Register class

check50.internal.run_dir = None

Temporary directory in which check is being run