Browse Source

documentation

tags/v14.0.0
adam j hartz 1 year ago
parent
commit
af1e1a7750
10 changed files with 143 additions and 37 deletions
  1. +4
    -1
      catsoop/base_context.py
  2. +79
    -22
      catsoop/check.py
  3. +12
    -4
      catsoop/cslog.py
  4. +13
    -0
      catsoop/debug_log.py
  5. +1
    -1
      catsoop/dispatch.py
  6. +23
    -0
      catsoop/fernet.py
  7. +9
    -4
      catsoop/lti.py
  8. +1
    -1
      catsoop/session.py
  9. +0
    -3
      catsoop/thirdparty/__init__.py
  10. +1
    -1
      catsoop/tutor.py

+ 4
- 1
catsoop/base_context.py View File

@@ -31,6 +31,9 @@ from catsoop import __version__
LOGGER = logging.getLogger("cs")

_nodoc = {
"LOGGER",
"config_loc",
"default_config_location",
"contents",
"cs_all_pieces",
"cs_all_thirdparty",
@@ -230,7 +233,7 @@ cs_question_type_defaults = {}
"""
Special: A dictionary mapping question type names to dictionaries containing
default values to use for that question type. These values are loaded in
before the values in a <question> tag are evaluated.
before the values in a &lt;question&gt; tag are evaluated.
"""

cs_checker_websocket = "ws://localhost:6011"


+ 79
- 22
catsoop/check.py View File

@@ -13,22 +13,37 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Library of common checker functions.
"""

import ast


def equal():
"""
Check whether two values are equivalent using Python's ==
Generate a check function that checks whether two values are equivalent
using Python's `==`.

**Parameters:** none

**Returns:** a function suitable for use as a `csq_check_function`.
"""
return lambda sub, soln: sub == soln


def check_result(f=None):
"""
Given a function that compares two Python values, return a new check
function that compares the 'result' fields of a submission and solution
using the given function.
Given a check function that directly compares values, return a new check
function that compares the `'result'` fields of a submission and solution
using the given function. For use with the `pythoncode` question type.

**Parameters:**

* `f` (optional:) a function that compares two values. Defaults to
comparison using `==`.

**Returns:** a function suitable for use as a `csq_check_function`.
"""
f = f if f is not None else equal()

@@ -43,7 +58,17 @@ def check_result(f=None):

def number_close(absolute_threshold=None, ratio_threshold=None):
"""
Check whether two numbers are close (within the given thresholds)
Generate a check function that checks whether two numbers are close (within
the given thresholds).

**Parameters:**

* `absolute_threshold` (optional): the maximum absolute difference that
will be accepted.
* `ration_threshold` (optional): the maximum ratio `submission / solution`
that will be accepted.

**Returns:** a function suitable for use as a `csq_check_function`.
"""

def _checker(sub, sol):
@@ -69,9 +94,16 @@ def number_close(absolute_threshold=None, ratio_threshold=None):

def evaled(f=None):
"""
Returns a new check function that evaluates its arguments (using
ast.literal_eval) before calling the given function on the evaluated
results
Given a check function that directly compares values, return a new check
function that evaluates its arguments (using `ast.literal_eval`) before
calling the given function on the evaluated results

**Parameters:**

* `f` (optional:) a function that compares two values. Defaults to
comparison using `==`.

**Returns:** a function suitable for use as a `csq_check_function`.
"""
f = f if f is not None else equal()

@@ -84,22 +116,38 @@ def evaled(f=None):
return _inner


def list_all(cmp_func=None):
def list_all(f=None):
"""
Check that two lists are equal (same values in the same order)
Given a function that compares two values, generate a new check function
that checks that two lists are equal (same values in the same order).

**Parameters:**

* `f` (optional:) a function that compares two values. Defaults to
comparison using `==`.

**Returns:** a function suitable for use as a `csq_check_function`.
"""
cmp_func = cmp_func or equal()
f = f or equal()
return lambda sub, soln: (
len(sub) == len(soln) and all(cmp_func(i, j) for i, j in zip(sub, soln))
len(sub) == len(soln) and all(f(i, j) for i, j in zip(sub, soln))
)


def list_all_unordered(cmp_func=None):
def list_all_unordered(f=None):
"""
Check function for lists containing all the same elements (including
duplicates), regardless of order.
Given a function that directly compares two values, generate a new check
function that checks whether two lists contain all the same elements
(including duplicates), regardless of order.

**Parameters:**

* `f` (optional:) a function that compares two values. Defaults to
comparison using `==`.

**Returns:** a function suitable for use as a `csq_check_function`.
"""
cmp_func = cmp_func or equal()
f = f or equal()

def _cmp(sub, soln):
sub = list(sub)
@@ -107,7 +155,7 @@ def list_all_unordered(cmp_func=None):
while sub:
elt = sub.pop()
for elt2 in soln:
if cmp_func(elt, elt2):
if f(elt, elt2):
soln.remove(elt2)
break
else:
@@ -117,12 +165,21 @@ def list_all_unordered(cmp_func=None):
return _cmp


def dict_all(cmp_func=None):
def dict_all(f=None):
"""
Checker function for dictionaries. Makes sure all keys and associated
values match.
Given a function that directly compares two valies, generate a new check
function that takes two dictionaries and checks that all keys are idntical,
and that the associated values are equivalent according to the given
function.

**Parameters:**

* `f` (optional:) a function that compares two values. Defaults to
comparison using `==`.

**Returns:** a function suitable for use as a `csq_check_function`.
"""
cmp_func = cmp_func or equal()
f = f or equal()
return lambda sub, soln: (
set(sub) == set(soln) and all(cmp_func(sub[i], soln[i]) for i in sub)
set(sub) == set(soln) and all(f(sub[i], soln[i]) for i in sub)
)

+ 12
- 4
catsoop/cslog.py View File

@@ -58,6 +58,18 @@ _nodoc = {
"OrderedDict",
"datetime",
"timedelta",
"COMPRESS",
"Cipher",
"ENCRYPT_KEY",
"ENCRYPT_PASS",
"RawFernet",
"compress_encrypt",
"decompress_decrypt",
"default_backend",
"log_lock",
"prep",
"sep",
"unprep",
}


@@ -295,10 +307,6 @@ def most_recent(db_name, path, logname, default=None, lock=True):
found, treating the piece of the file after the last separator as a log
entry, and using `unprep` to return the associated Python object.

Based on <a
href="http://stackoverflow.com/questions/136168/get-last-n-lines-of-a-file-with-python-similar-to-tail"
target="_blank">code by S.Lott and Pykler</a>

**Parameters:**

* `db_name`: the name of the database to read


+ 13
- 0
catsoop/debug_log.py View File

@@ -13,10 +13,20 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Module to initialize a debugger for CAT-SOOP.
"""
import logging


def setup_logging(context):
"""
Set up CAT-SOOP logging.

**Parameters**: none

**Returns:** `None`
"""
logging.getLogger("pylti.common").setLevel(
context.get("cs_lti_debug_level", "WARNING")
)
@@ -25,3 +35,6 @@ def setup_logging(context):


LOGGER = logging.getLogger("cs")
"""
An instance of `logging.Logger` for CAT-SOOP-related logging.
"""

+ 1
- 1
catsoop/dispatch.py View File

@@ -39,7 +39,7 @@ from . import language
from . import debug_log
from . import base_context

_nodoc = {"CSFormatter", "formatdate", "dict_from_cgi_form"}
_nodoc = {"CSFormatter", "formatdate", "dict_from_cgi_form", "LOGGER", "md5"}

LOGGER = debug_log.LOGGER



+ 23
- 0
catsoop/fernet.py View File

@@ -22,6 +22,12 @@
# Apache License, Version 2.0, and the BSD License. See
# https://github.com/pyca/cryptography/blob/master/LICENSE for complete
# details.
"""
Fernet-style encryption forked from the
[`cryptography`](https://cryptography.io/en/latest/) package. Implements
Fernet encryption, but without the bade64-encoding step (produces raw binary
data).
"""

import binascii
import os
@@ -36,6 +42,18 @@ from cryptography.hazmat.primitives import hashes, padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.hmac import HMAC

_nodoc = {
"InvalidSignature",
"default_backend",
"hashes",
"padding",
"Cipher",
"algorithms",
"modes",
"HMAC",
"InvalidToken",
}


class InvalidToken(Exception):
pass
@@ -45,6 +63,11 @@ _MAX_CLOCK_SKEW = 60


class RawFernet(object):
"""
Class (forked from the `Fernet` class in the `cryptography` package) that
implements a raw binary form of Fernet encryption.
"""

def __init__(self, key, backend=None):
if backend is None:
backend = default_backend()


+ 9
- 4
catsoop/lti.py View File

@@ -33,6 +33,8 @@ from . import debug_log

LOGGER = debug_log.LOGGER

_nodoc = {"Client", "ElementMaker", "etree", "LOGGER"}


class lti4cs(pylti.common.LTIBase):
"""
@@ -289,10 +291,13 @@ class LTI_Consumer(object):

def serve_lti(context, path_info, environment, params, dispatch_main):
"""
context: (dict) catsoop global context
path_info: (list) URL path components
environment: (dict-like) web server data, such as form input
dispatch_main: (proc) call this with environment to dispatch to render URL

**Parameters**:

* `context`: dictionary, the context associated with this request
* `path_info`: list of URL path components
* `environment`: dictionary containing web server data, such as form input
* `dispatch_main`: function of the same form as `catsoop.dispatch.main`
"""
if not "cs_lti_config" in context:
msg = "[lti] LTI not configured - missing cs_lti_config in config.py"


+ 1
- 1
catsoop/session.py View File

@@ -34,7 +34,7 @@ importlib.reload(base_context)

LOGGER = debug_log.LOGGER

_nodoc = {"SimpleCookie", "make_session_dir"}
_nodoc = {"SimpleCookie", "make_session_dir", "LOGGER"}

VALID_SESSION_RE = re.compile(r"^[A-Fa-f0-9]{32}$")
"""


+ 0
- 3
catsoop/thirdparty/__init__.py View File

@@ -1,6 +1,3 @@
"""
Third-party software used within CAT-SOOP

Note that some of these may not be terribly well documented here because they
all follow different documentation conventions.
"""

+ 1
- 1
catsoop/tutor.py View File

@@ -34,7 +34,7 @@ from . import base_context

importlib.reload(base_context)

_nodoc = {"timedelta"}
_nodoc = {"timedelta", "OrderedDict"}


def _get(context, key, default, cast=lambda x: x):


Loading…
Cancel
Save