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 existyaml.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 tocheck50
.
- 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 theenv
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
. Ifcode
isNone
, 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 withintimeout
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 aSIGINT
and finally aSIGKILL
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 emptytimeout (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 returnsself
.- 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 regextimeout (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 itcheck50.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 thatcommand
is run throughvalgrind
andvalgrind
’s output is automatically reviewed at the end of the check for memory leaks and other bugs. Ifvalgrind
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 thatcode
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 atpath
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 existsout_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