API docs



Import checks module given relative path.


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


the imported module

  • 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 *


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


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)

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

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

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.

  • 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", 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.

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

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

Example usage:


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

Kill the process.

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


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.

  • 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

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.

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

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.

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

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

  • 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.check(exists) # Mark 'compiles' as a check that depends on 'exists'
def compiles():
    """hello.c 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.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.

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

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

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:



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.


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

Spawn a Flask app.

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

Example usage:

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.

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



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.

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

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.


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:


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


check50.py.append_code(original, codefile)

Append the contents of one file to another.

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

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

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

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.


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

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

lib50 config loader

exception check50.internal.Error

Exception for internal check50 errors.

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.


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

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

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

  • checks – YAML checks read from config
  • prompt (bool) – prompt user if out_file already exists
  • out_file (str) – file to write compiled checks


Return type:


check50.internal.import_file(name, path)

Import a file given a raw file path.

  • name (str) – Name of module to be imported
  • path (str / Path) – Path to Python file

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