API docs

check50

exception check50.EOF(value)

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

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)
__init__(rationale, help=None)

Initialize self. See help(type(self)) for accurate signature.

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)
__init__(expected, actual, help=None)

Initialize self. See help(type(self)) for accurate signature.

exception check50.Missing(missing_item, collection, help=None)

Exception signifying check failure due to an item missing from a collection. This is typically a specific substring in a longer string, for instance the contents of stdout.

Parameters
  • item – the expected item / substring

  • collection – the collection / string

  • help (str) – optional help message to be displayed

Example usage:

actual = check50.run("./fibonacci 5").stdout()

if "5" not in actual and "3" in actual:
    help = "Be sure to start the sequence at 1"
    raise check50.Missing("5", actual, help=help)
__init__(missing_item, collection, help=None)

Initialize self. See help(type(self)) for accurate signature.

check50.check(dependency=None, timeout=60, max_log_lines=100)

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

  • max_log_lines (int) – maximum number of lines that can appear in the log

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

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)
__init__(command, env={})

Initialize self. See help(type(self)) for accurate signature.

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, str_line=None, prompt=True, timeout=3)

Send line to stdin, optionally expect a prompt.

Parameters
  • line (str) – line to be send to stdin

  • str_line (str) – what will be displayed as the delivered input, a human readable form of line

  • 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, show_timeout=False)

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, int, float, stream) – optional output to be expected from stdout, raises check50.Failure if no match In case output is a float or int, the check50.number_regex is used to match just that number”. In case output is a stream its contents are used via output.read().

  • 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

  • show_timeout (bool) – flag indicating whether the timeout in seconds should be displayed when a timeout occurs

Raises
  • check50.Mismatch – if output is specified and nothing that the process outputs 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.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', max_log_lines=50, **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.

Parameters
  • command (str) – command to be run

  • env (str) – environment in which to run command

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='application.py', 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)
__init__(path='application.py', app_name='app')

Initialize self. See help(type(self)) for accurate signature.

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

Parameters

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

check50.regex.decimal(number)

Create a regular expression to match the number exactly:

In case of a positive number:

(?<![\d\-])number(?!(\.?\d))

In case of a negative number:

number(?!(\.?\d))

(?<![\d\-]) = negative lookbehind, asserts that there are no digits and no - in front of the number.

(?!(\.?\d)) = negative lookahead, asserts that there are no digits and no additional . followed by digits after the number.

Parameters

number (any numbers.Number (such as int, float, ...)) – the number to match in the regex

Return type

str

Example usage:

# Check that 7.0000 is printed with 5 significant figures
check50.run("./prog").stdout(check50.regex.decimal("7.0000"))

check50.internal

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

check50.internal.CONFIG_LOADER = <lib50.config.Loader object>

lib50 config loader

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.

__init__()

Initialize self. See help(type(self)) for accurate signature.

after_check(func)

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

Parameters

func – callback to run after check

Raises

check50.internal.Error – if called when no check is being run

after_every(func)

Run func at the end of every check.

Parameters

func – callback to be run after every check

Raises

check50.internal.Error – if called when a check is being run

before_every(func)

Run func at the start of every check.

Parameters

func – callback to be run before every check

Raises

check50.internal.Error – if called when a check is being run

check50.internal.check_dir = None

Directory containing the check and its associated files

check50.internal.check_running = False

Boolean that indicates if a check is currently running

check50.internal.compile_checks(checks, prompt=False, out_file='__init__.py')

Compile YAML checks to a Python file

Parameters
  • checks – YAML checks read from config

  • prompt (bool) – prompt user if out_file already exists

  • out_file (str) – file to write compiled checks

Returns

out_file

Return type

str

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 the current check is being run

check50.internal.run_root_dir = None

Temporary directory that is the root (parent) of all run_dir(s)

check50.internal.slug = None

The user specified slug used to indentifies the set of checks

check50.internal.student_dir = None

Directory check50 was run from