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).- FileNotFoundError – if
-
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 existExample 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 directoryParams 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 theenv
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
. 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, 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 returnsself
.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 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)
- output (str) – optional output to be expected from stdout, raises
-
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
.
-
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.
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)
-
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 returnsExample 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.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
-
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.-
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 check is being run