diff --git a/docs/src/check.ipynb b/docs/src/check.ipynb index 3ee51f8..d109844 100644 --- a/docs/src/check.ipynb +++ b/docs/src/check.ipynb @@ -9,11 +9,14 @@ "slideshow": { "slide_type": "" }, - "tags": [] + "tags": [], + "vscode": { + "languageId": "raw" + } }, "source": [ ".. important::\n", - " Jupyter widgets cannot be run within in documentation. To be able to interact with the widget you must run a mybinder instance. To run a mybinder instance of this notebook please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fexercises.ipynb." + " Jupyter widgets cannot be run within the documentation. To interact with the widget, you must run a mybinder instance. To run a mybinder instance of this notebook, please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fcheck.ipynb." ] }, { @@ -29,7 +32,7 @@ "id": "2", "metadata": {}, "source": [ - "Checks intention is to give students a way to validate their code solution. The student's code can be validated by providing a list of inputs and references comparing the output of the student's code with the references, or by directy testing certain functional behavior of the code. In cases when reference outputs need to be obfuscated the outputs that are compared can be passed through a _fingerprint_ function. This notebook goes through each of these features and presents an example." + "The purpose of a check is to give students a way to validate their code solutions. The student's code can be validated by providing a list of inputs and references, which are compared to the output of the student's code, or by directly testing certain functional behavior of the code. In cases when reference outputs need to be obfuscated, the outputs that are compared can be passed through a _fingerprint_ function. This notebook goes through each of these features and presents an example." ] }, { @@ -51,7 +54,7 @@ "id": "4", "metadata": {}, "source": [ - "Similar as for the `ExerciseRegistry` we need to define a `CheckRegistry` that registers the checks for each exercise." + "Similar to the `ExerciseRegistry`, we need to define a `CheckRegistry` that registers the checks for each exercise." ] }, { @@ -89,7 +92,7 @@ "\n", "\n", "check_code_ex = CodeExercise(\n", - " key=\"sinus_with_references_1\",\n", + " key=\"sine_with_references_1\",\n", " code=sin,\n", " check_registry=check_registry,\n", ")\n", @@ -121,7 +124,7 @@ "id": "8", "metadata": {}, "source": [ - "Because there are asserts that are repeatedly needed for almost any kind of exercise, we provide of a set of asserts" + "Since some asserts are frequently needed across various exercises, we provide a common set of asserts." ] }, { @@ -139,14 +142,14 @@ " assert_type,\n", ")\n", "\n", - "def sinus(arr):\n", + "def sine(arr):\n", " import numpy as np\n", " return np.cos(arr) # oops! wrong solution\n", "\n", "check_code_ex = CodeExercise(\n", - " key=\"sinus_with_references_2\",\n", - " title=\"sinus\",\n", - " code=sinus,\n", + " key=\"sine_with_references_2\",\n", + " title=\"sine\",\n", + " code=sine,\n", " check_registry=check_registry,\n", ")\n", "\n", @@ -216,13 +219,13 @@ }, "outputs": [], "source": [ - "def sinus(arr):\n", + "def sine(arr):\n", " import numpy as np\n", " return np.cos(arr) # oops! wrong solution\n", "\n", "code_ex_functional_behavior = CodeExercise(\n", - " key=\"sinus_functional_behavior\",\n", - " code=sinus,\n", + " key=\"sine_functional_behavior\",\n", + " code=sine,\n", " check_registry=check_registry,\n", ")\n", "\n", @@ -250,7 +253,7 @@ "tags": [] }, "source": [ - "## Obfuscating the reference solution with fingerprint" + "## Obfuscating the reference solution with a fingerprint" ] }, { @@ -272,19 +275,19 @@ " \n", " What has wings but in the air it not swings.\n", " I looked to the north, but it was not worth.\n", - " What I am looking for?\n", + " What am I looking for?\n", " \"\"\"\n", " return \"\"\n", - "code_input_sinus = CodeInput(riddle)\n", + "code_input_sine = CodeInput(riddle)\n", "\n", "check_code_ex = CodeExercise(\n", " key=\"riddle\",\n", - " code=code_input_sinus,\n", + " code=code_input_sine,\n", " check_registry=check_registry,\n", ")\n", "\n", "#def assert_equal(output, reference):\n", - "# return \"\" if output == reference else \"Not correct solution. Hint: it is an animal in the antarctica.\"\n", + "# return \"\" if output == reference else \"Not correct solution. Hint: it is an animal in the Antarctica.\"\n", "\n", "char_to_num = {char: num for num, char in enumerate(\"abcdefghijklmnopqrmnstuvwxyz\")}\n", "def string_to_int(output):\n", @@ -318,7 +321,7 @@ "id": "18", "metadata": {}, "source": [ - "The check registry also provides the possibility to check all the widgets. " + "The check registry also allows checking of all the widgets simultaneously. " ] }, { @@ -338,7 +341,7 @@ "id": "20", "metadata": {}, "source": [ - "For the demo to automatically we simulate a button press using the private function that should not be used" + "For the demo, we simulate a button press using the private function that should not be used" ] }, { diff --git a/docs/src/developers.rst b/docs/src/developers.rst index 1404bf7..387d9ec 100644 --- a/docs/src/developers.rst +++ b/docs/src/developers.rst @@ -1,12 +1,12 @@ Developers ========== -This is an instruction of the developer tools that help you contributing. +This is an instruction of the developer tools that help you contribute. Running tests ------------- -Tests can be with run using +Tests can be run by using .. code-block:: bash @@ -21,15 +21,14 @@ You can source the test environment of tox after a run using Converting notebook test files ############################## -The we store the notebooks as converted Python file using `jupytext` for the conversion -for better versioning +We store the notebooks as converted Python file using `jupytext` for better versioning .. code-block:: bash jupytext tests/notebooks/*.py --to ipynb -Be aware that when runnng the tests with tox all `*.ipynb` are overwriten by their -corresponding `*.py` file. For example, the file `test_widget_cue_box.ipynb` is +Be aware that when running the tests with tox all `*.ipynb` are overwritten by the +corresponding `*.py` files. For example, the file `test_widget_cue_box.ipynb` is overwritten by the conversion of `test_widget_cue_box.py` when running the test. @@ -37,9 +36,9 @@ Running in browser ################## We use selenium to test the widgets on user actions (like a button click). To run it in -in the CI where no display is available. We run the browsers in headless model to not -load the UI. For debugging a test however, it is often benificial to see what is -happening in the window. To run the tests with the browser UI please use +the CI where no display is available. We run the browsers in the headless mode to not +load the UI. For debugging a test, however, it is often beneficial to see what is +happening in the window. To run the tests with the browser UI, please use .. code-block:: bash @@ -48,15 +47,16 @@ happening in the window. To run the tests with the browser UI please use Port issues ########### -For the notebook server we fixed the port to 8815, if this port is not available you . -It can happen that notebook process is not properly killed and remains as zombie -process. You can check if a notebook with this port is already running using +For the notebook server, we fixed the port to 8815. If this port is not available, you +you need to choose a different one. It can happen that notebook process is not properly +killed and remains as zombie process. You can check if a notebook with the 8815 port +is already running using .. code-block:: bash jupyter notebook list | grep 8815 -or if some process is using it +or if some other process is using it .. code-block:: bash @@ -88,7 +88,7 @@ To build the docs please use tox -e docs -To open the doc with for example firefox you can run +To open the documentation with Firefox, for example, you can run: .. code-block:: bash diff --git a/docs/src/exercises.ipynb b/docs/src/exercises.ipynb index 2144e3e..e64f99e 100644 --- a/docs/src/exercises.ipynb +++ b/docs/src/exercises.ipynb @@ -9,11 +9,14 @@ "slideshow": { "slide_type": "" }, - "tags": [] + "tags": [], + "vscode": { + "languageId": "raw" + } }, "source": [ ".. important::\n", - " Jupyter widgets cannot be run within in documentation. To be able to interact with the widget you must run a mybinder instance. To run a mybinder instance of this notebook please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fexercises.ipynb." + " Jupyter widgets cannot be run within the documentation. To interact with the widget, you must run a mybinder instance. To run a mybinder instance of this notebook, please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fexercises.ipynb." ] }, { @@ -72,7 +75,7 @@ "id": "5", "metadata": {}, "source": [ - "Due to limitations of jupyter notebooks in saving the widgets state, we store and load the widget state by ourself using the `ExerciseRegstry`. It allows you specify a filename for the json file to store all the answers to all registered exercises. The exercises are registered by passing the `ExerciseRegstry` instance as input argument." + "Due to limitations of jupyter notebooks in saving the widgets state, we store and load the widget state by ourselves using the `ExerciseRegistry`. It allows you specify a filename for the JSON file to store all the answers to all registered exercises. The exercises are registered by passing the `ExerciseRegistry` instance as input argument." ] }, { @@ -101,7 +104,7 @@ "id": "8", "metadata": {}, "source": [ - "A simple textarea to save students answers. Note that the save and load buttons only appear when a exercise key and registry is given" + "A simple text area to save students' answers. Note that the save and load buttons only appear when an exercise key and registry are given." ] }, { @@ -138,7 +141,7 @@ "id": "11", "metadata": {}, "source": [ - "This is an example how to create a simple exercise." + "This is an example on how to create a simple exercise." ] }, { @@ -197,7 +200,7 @@ "id": "14", "metadata": {}, "source": [ - "More complex widgets might need creation beforehand to allow full customization" + "More complex widgets might need to be created beforehand to allow full customization" ] }, { @@ -276,7 +279,7 @@ "id": "17", "metadata": {}, "source": [ - "So far we always added the imports required for the code inside the code input. We need to do this because the widget is its creates its own environment (own globals) so no function from the notebook is accidently used. This however does not solve the problem when one wants to include typehints in the function signature that require imports. For that one can add the library to the globals." + "So far we always added the imports required for the code inside the code input. We need to do this because the widget is its creates its own environment (own globals), so no function from the notebook is accidently used. However, this does not solve the problem when one wants to include typehints in the function signature that require imports. For this, one can add the library to the globals." ] }, { @@ -387,7 +390,7 @@ "id": "23", "metadata": {}, "source": [ - "The `ParametersPanel` can be also used with the same shorthand constructors as [interact](https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html). Here some examples" + "The `ParametersPanel` can be also used with the same shorthand constructors as [interact](https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html). Here are some examples." ] }, { @@ -413,7 +416,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "scicode", "language": "python", "name": "python3" }, @@ -427,7 +430,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.10" + "version": "3.11.11" } }, "nbformat": 4, diff --git a/docs/src/getting_started.ipynb b/docs/src/getting_started.ipynb index 926c613..d4da612 100644 --- a/docs/src/getting_started.ipynb +++ b/docs/src/getting_started.ipynb @@ -16,7 +16,7 @@ "scicode-widgets can be installed with:\n", "\n", "```bash\n", - " pip install scicode-widgets\n", + " pip install scwidgets\n", "```" ] }, @@ -29,11 +29,14 @@ "slideshow": { "slide_type": "" }, - "tags": [] + "tags": [], + "vscode": { + "languageId": "raw" + } }, "source": [ ".. important::\n", - " Jupyter widgets cannot be run within in documentation. To be able to interact with the widget you must run a mybinder instance. To run a mybinder instance of this notebook please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fgetting_started.ipynb." + " Jupyter widgets cannot be run within the documentation. To interact with the widget, you must run a mybinder instance. To run a mybinder instance of this notebook, please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fgetting_started.ipynb." ] }, { @@ -47,7 +50,7 @@ "tags": [] }, "source": [ - "## Creating a code execrcise\n", + "## Creating a code exercise\n", "This is how a simple coding exercise can look like." ] }, @@ -72,7 +75,7 @@ "# This is what the students sees and can adapt\n", "def sin(x, omega):\n", " import numpy as np\n", - " # We provide already the solution for the demo\n", + " # We already provide the solution for the demo\n", " return np.sin(x*omega)\n", "\n", "\n", @@ -88,7 +91,7 @@ " parameters={\"omega\": (0.5, 3.14, 0.1)},\n", " update=update_func,\n", " update_mode=\"continuous\",\n", - " title=\"Sinus function\",\n", + " title=\"Sine function\",\n", " description=\"Implements $\\\\sin(x\\\\omega)$\",\n", ")\n", "\n", @@ -101,7 +104,7 @@ "id": "4", "metadata": {}, "source": [ - "Please look at __[section exercises](./exercises.html)__ for more information about the execrises that can be created and their customization options." + "Please look at __[exercises section](./exercises.html)__ for more information about the exercises that can be created and their customization options." ] }, { @@ -109,9 +112,9 @@ "id": "5", "metadata": {}, "source": [ - "## Check students solution\n", + "## Check student's solution\n", "\n", - "You can create checks for student that work like unit tests helping the student to verify the students solution" + "You can create checks (like unit tests) to help students verify their solutions:" ] }, { @@ -134,12 +137,12 @@ "\n", "check_registry = CheckRegistry()\n", "\n", - "def sinus(arr):\n", + "def sine(arr):\n", " import numpy as np\n", " return np.cos(arr) # oops! wrong solution\n", "\n", "check_code_ex = CodeExercise(\n", - " code=sinus,\n", + " code=sine,\n", " update=lambda code_ex: print(code_ex.code(np.pi)),\n", " check_registry=check_registry,\n", ")\n", @@ -179,7 +182,7 @@ "lines_to_next_cell": 2 }, "source": [ - "Please look at the __[section how to add checks](check.html)__ for all options to create checks." + "Please look at the __[section on how to add checks](check.html)__ for all options to create them." ] }, { @@ -189,24 +192,24 @@ "lines_to_next_cell": 2 }, "source": [ - "## nbgrader integration \n", + "## `nbgrader` integration \n", "\n", - "One can use nbgrader by using their macros.\n", + "One can use `nbgrader` by using their macros.\n", "\n", "```python\n", "def sin(arr: np.ndarray):\n", " \"\"\"\n", " :param arr: array of arbitrary shape\n", - " :return: returns the sinus\n", + " :return: returns the sine\n", " \"\"\"\n", " import numpy as np\n", " ### BEGIN SOLUTION\n", " sin_arr = np.sin(arr)\n", - " ### END SOLTUION\n", + " ### END SOLUTION\n", " return sin_arr\n", "```\n", "\n", - "Then nbgrader will convert this to" + "Then, `nbgrader` will convert this to" ] }, { @@ -221,7 +224,7 @@ "def sin(arr: np.ndarray):\n", " \"\"\"\n", " :param arr: array of arbitrary shape\n", - " :return: returns the sinus\n", + " :return: returns the sine\n", " \"\"\"\n", " import numpy as np\n", " # YOUR CODE HERE\n", @@ -242,13 +245,13 @@ "tags": [] }, "source": [ - "It requires to add a hook in the config to copy over the students answers to the grading subfolder. A step-by-step tutorial how to make an nbgrader project compatible with scicode-widget can be seen in the repo TODO." + "It requires to add a hook in the config to copy over the student's answers to the grading subfolder. A step-by-step tutorial how to make an `nbgrader` project compatible with `scwidget` can be seen in the repo." ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "scicode", "language": "python", "name": "python3" }, @@ -262,7 +265,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.10" + "version": "3.11.11" }, "tags": [] }, diff --git a/docs/src/nbgrader.ipynb b/docs/src/nbgrader.ipynb index 3edf65a..85a6f63 100644 --- a/docs/src/nbgrader.ipynb +++ b/docs/src/nbgrader.ipynb @@ -9,11 +9,14 @@ "slideshow": { "slide_type": "" }, - "tags": [] + "tags": [], + "vscode": { + "languageId": "raw" + } }, "source": [ ".. important::\n", - " Jupyter widgets cannot be run within in documentation. To be able to interact with the widget you must run a mybinder instance. To run a mybinder instance of this notebook please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fnbgrader.ipynb." + " Jupyter widgets cannot be run within the documentation. To interact with the widget, you must run a mybinder instance. To run a mybinder instance of this notebook, please use this link https://mybinder.org/v2/gh/osscar-org/scicode-widgets/HEAD?labpath=docs%2Fsrc%2Fnbgrader.ipynb." ] }, { @@ -55,7 +58,7 @@ } }, "source": [ - "When you initialize the quickstart project from nbgrader a `problem1.ipynb` file is generated. In this notebook we show how scwidgets can be used with nbgrader. We transformed the `problem1.ipynb` to a version that uses scwidgets." + "When you initialize the quickstart project from `nbgrader`, a `problem1.ipynb` file is generated. In this notebook, we show how `scwidgets` can be used with `nbgrader`. We transformed the `problem1.ipynb` to a version that uses `scwidgets`." ] }, { @@ -460,7 +463,7 @@ " ### END SOLUTION\n", "\n", "description = \"\"\"\n", - "Find a usecase for your `sum_of_squares` function and implement that usecase in the cell below.\n", + "Find a use case for your `sum_of_squares` function and implement that use case in the cell below.\n", "\"\"\"\n", "\n", "code_ex_pyramidal_number = CodeExercise(\n", diff --git a/src/scwidgets/check/_asserts.py b/src/scwidgets/check/_asserts.py index 202f4c7..b42c164 100644 --- a/src/scwidgets/check/_asserts.py +++ b/src/scwidgets/check/_asserts.py @@ -14,6 +14,10 @@ def assert_equal( output_references: Check.FunOutParamsT, parameters_to_check: Union[Iterable[int], str] = "all", ) -> AssertResult: + """ + Check if output_parameters are equal to output_references using simple Python + equality check. + """ assert len(output_parameters) == len( output_references ), "output_parameters and output_references have to have the same length" @@ -59,6 +63,9 @@ def assert_shape( output_references: Check.FunOutParamsT, parameters_to_check: Union[Iterable[int], str] = "auto", ) -> AssertResult: + """ + Check that the shape of output parameters matches the reference. + """ assert len(output_parameters) == len( output_references ), "output_parameters and output_references have to have the same length" @@ -114,6 +121,10 @@ def assert_numpy_allclose( atol=1e-08, equal_nan=False, ) -> AssertResult: + """ + Check if output_parameters are numerically close to output_references using + numpy.allclose(). + """ assert len(output_parameters) == len( output_references ), "output_parameters and output_references have to have the same length" @@ -196,6 +207,9 @@ def assert_type( output_references: Check.FunOutParamsT, parameters_to_check: Union[Iterable[int], str] = "all", ) -> AssertResult: + """ + Check that output parameters have the correct type. + """ assert len(output_parameters) == len( output_references ), "output_parameters and output_references have to have the same length" @@ -242,6 +256,9 @@ def assert_numpy_sub_dtype( numpy_type: Union[np.dtype, type], parameters_to_check: Union[Iterable[int], str] = "all", ) -> AssertResult: + """ + Check that output parameters have the correct numpy sub-dtype. + """ if parameters_to_check == "all": parameter_indices = range(len(output_parameters)) elif isinstance(parameters_to_check, abc.Iterable): diff --git a/src/scwidgets/check/_check.py b/src/scwidgets/check/_check.py index 2212e3d..d239296 100644 --- a/src/scwidgets/check/_check.py +++ b/src/scwidgets/check/_check.py @@ -25,37 +25,36 @@ class Check: """ - A check verifies the correctness of a function for a set of inputs parameters using + A check verifies the correctness of a function for a set of input parameters using a list of univariate and bivariate asserts with the option to obscure the reference outputs. :param function_to_check: - The function must that accepts each input parameters in :params - input_parameters: - :param inputs_parameters: + The function that accepts each input parameters in `input_parameters` + :param input_parameters: A dict or a list of dictionaries each containing the argument name and its value - as (key, value) pair that is used as input for the function - :param function_to_check: + as (key, value) pair that is used as input for the function `function_to_check`. :param outputs_references: - A list or a list of lists each containing the expected output of the function - :param function_to_check: of :param function_to_check: for the inputs in the - :param input_parameters: + A tuple or a list of tuples each containing the expected output of the function + `function_to_check` for the inputs in the `input_parameters` :param asserts: - A list of assert functions. An assert function can the output parameters of - :param function_to_check: to run assert. If output references has been set it - can take additional output references to compare with. If a fingerprint is given - then the fingerprints are compared while assert functions with a single argument - are always applied on the output parameters. + A list of assert functions. Each assert function verifies the outputs from + `function_to_check`. Assert functions can be univariate (accepting only output), + bivariate (accepting output and reference output), or nullvariate (accepting no + arguments). If output references have been set, it can take additional output + references to compare with. If a fingerprint is given, the it is compared while + assert functions with a single argument are always applied on the output + parameters. :param fingerprint: - A one-way function that takes as input the output parameters of function :param - function_to_check: and obscures the :param output_references:. + A one-way function that transforms the outputs from `function_to_check`, + obscuring direct comparisons with the `output_references`. :param suppress_fingerprint_asserts: Specifies if the assert messages that use the fingerprint function output for - tests are surpressed. The message might be confusing to a student as the output + tests are suppressed. The message might be confusing to a student as the output is converted by the fingerprint function. :param stop_on_assert_error_raised: Specifies if running the asserts is stopped as soon as an error is raised in an - assert. If a lot of asserts are specified the printing of a lot of error + assert. If a lot of asserts are specified, the printing of a lot of error tracebacks might make debugging harder. """ @@ -207,7 +206,7 @@ def compute_and_set_references(self): def check_function(self) -> CheckResult: """ - Returns for each input (first depth list) the result message for each assert + For each input (first depth list) returns the result message for each assert (second depth list). If a result message is empty, the assert was successful, otherwise it contains information about the failure. """ @@ -218,7 +217,7 @@ def check_function(self) -> CheckResult: "outputs_references (second positional argument)" ) assert len(self._inputs_parameters) == len(self._outputs_references), ( - "Number of inputs and reference outputs " + "Number of inputs and reference outputs " "are mismatching: len inputs parameters != len outputs parameters " f"[{len(self._inputs_parameters)} != {len(self._outputs_references)}]." ) @@ -303,6 +302,11 @@ def check_function(self) -> CheckResult: class CheckResult: + """ + Represents the result of a check, storing information about the assert results, + assert names, input parameters, and suppressed assert messages. + """ + def __init__(self): self._assert_results = [] self._assert_names = [] @@ -432,11 +436,22 @@ def inputs_parameters(self): class AssertResult: """ + Represents the result of an assertion check, storing information about + the assertion name, parameter indices, parameter values, and messages. + If any of `parameter_indices`, `parameter_values`, or `messages` is a list, + then all must be lists of the same length. + :param assert_name: - TODO + The name of the assertion being checked. :param parameter_indices: - TODO - TODO... + The index or indices of the parameters that were evaluated in the assertion. + If a single index is provided, it will be converted into a list. + :param parameter_values: + The value(s) of the parameters at the given indices that were checked in the + assertion. If a single value is provided, it will be converted into a list. + :param messages: + A message or list of messages describing the assertion result for each parameter + index. If a single message is provided, it will be converted into a list. """ def __init__( @@ -457,14 +472,14 @@ def __init__( or not (isinstance(messages, list)) ): raise ValueError( - "If one of the inputs parameter_indices, paramater_values or " + "If one of the inputs parameter_indices, parameter_values or " "messages is a list, then all must be lists of the same size." ) elif len(parameter_indices) != len(parameter_values) or len( parameter_indices ) != len(messages): raise ValueError( - "If one of the inputs parameter_indices, paramater_values or " + "If one of the inputs parameter_indices, parameter_values or " "messages is a list, then all must be lists of the same size, " "but got len(parameter_indices), len(parameter_values), " f"len(messages) [{len(parameter_indices)}, " diff --git a/src/scwidgets/check/_widget_check_registry.py b/src/scwidgets/check/_widget_check_registry.py index bfdda95..9e0212c 100644 --- a/src/scwidgets/check/_widget_check_registry.py +++ b/src/scwidgets/check/_widget_check_registry.py @@ -15,7 +15,8 @@ class CheckableWidget: """ A base class for any widget to inherit from to be compatible with the - :py:class:`CheckRegistry`. The logic is th + :py:class:`CheckRegistry`. The widget can be registered to a `CheckRegistry`, + which allows applying checks. :param check_registry: the check registry that registers the checks for this widget @@ -35,7 +36,7 @@ def compute_output_to_check( self, *input_args: Check.FunInParamT ) -> Check.FunOutParamsT: """ - The widget returns the output that will verified by the added checks. + The widget returns the output that will be verified by the added checks. """ raise NotImplementedError("compute_output_to_check has not been implemented") @@ -48,6 +49,10 @@ def handle_checks_result( raise NotImplementedError("handle_checks_result has not been implemented") def add_check(self, *args, **kwargs): + """ + Adds checks to the widget. Accepts either a `Check` object (or list of + `Check` objects) directly, or parameters that define a new check. + """ # simple dispatch logic if len(args) + len(kwargs) == 1: self._add_check_from_check(*args, **kwargs) @@ -174,7 +179,7 @@ def __init__(self, *args, **kwargs): @property def checks(self): """ - all registerd checks from widgets to checks + Returns all checks registered for each widget. """ return self._checks @@ -196,6 +201,9 @@ def display_set_all_references_button(self, value: bool): @property def registered_widgets(self): + """ + Returns a copy of all widgets currently registered with this registry. + """ return self._widgets.copy() def nb_conducted_asserts(self, widget: CheckableWidget): @@ -227,6 +235,23 @@ def add_check( ] = None, suppress_fingerprint_asserts: bool = True, ): + """ + Adds a new check for the specified widget. The check is defined using assert + functions, and optional input parameters, output references, and fingerprint + function. + :param widget: + The widget to which the check is being added. + :param asserts: + Functions to validate the widget's output. + :param inputs_parameters: + Inputs to provide when calling the widget's output computation method. + :param outputs_references: + Expected reference outputs used for assertions. + :param fingerprint: + Optional function to obfuscate outputs before assertions. + :param suppress_fingerprint_asserts: + If True, suppresses assert messages involving fingerprinted outputs. + """ if not (issubclass(type(widget), CheckableWidget)): raise ValueError("Argument widget must be subclass of CheckableWidget") if widget not in self._checks.keys(): diff --git a/src/scwidgets/code/_widget_code_input.py b/src/scwidgets/code/_widget_code_input.py index 83cd42f..41b0d4a 100644 --- a/src/scwidgets/code/_widget_code_input.py +++ b/src/scwidgets/code/_widget_code_input.py @@ -24,19 +24,18 @@ class CodeInput(WidgetCodeInput): """ Small wrapper around WidgetCodeInput that controls the output - :param function: We can automatically parse the function. Note that during - parsing the source code might be differently formatted and certain - python functionalities are not formatted. If you notice undesired - changes by the parsing, please directly specify the function as string - using the other parameters. + :param function: + A Python function to be parse automatically. Note that the parsing + may alter the original formatting or lose certain syntactical nuances. If this + behavior is undesired, provide the function explicitly using other parameters. :param function_name: The name of the function - :param function_paramaters: The parameters as continuous string as specified in + :param function_paramaters: The parameters as a continuous string as specified in the signature of the function. e.g for `foo(x, y = 5)` it should be `"x, y = 5"` :param docstring: The docstring of the function :param function_body: The function definition without indentation - :param builtins: A dict of variable name and value that is added to the - globals __builtins__ and thus available on initialization + :param builtins: A dictionary containing variable names and values that are added + to the globals __builtins__ and thus available on initialization """ valid_code_themes = ["nord", "solarizedLight", "basicLight"] @@ -88,9 +87,9 @@ def __init__( @property def unwrapped_function(self) -> types.FunctionType: """ - Return the compiled function object. + Returns the compiled function object. - This can be assigned to a variable and then called, for instance:: + This can be assigned to a variable and then called, for instance: func = widget.wrapped_function # This can raise a SyntaxError retval = func(parameters) @@ -133,7 +132,7 @@ def __call__(self, *args, **kwargs) -> Check.FunOutParamsT: def compatible_with_signature(self, parameters: List[str]) -> str: """ This function checks if the arguments are compatible with the function signature - and returns a nonempty message if this is not the case explaining what the issue + and returns an explanatory message if this is not the case. """ if "**" in self.function_parameters: # function has keyword arguments so it is compatible @@ -148,10 +147,17 @@ def compatible_with_signature(self, parameters: List[str]) -> str: @property def function_parameters_name(self) -> List[str]: + """ + Returns the names of the function parameters + """ return self.function_parameters.replace(",", "").split(" ") @staticmethod def get_docstring(function: types.FunctionType) -> Union[str, None]: + """ + Returns the docstring of a function, if it exists, without leading or trailing + whitespace or triple quotes. + """ docstring = function.__doc__ return ( None @@ -180,6 +186,10 @@ def _get_function_source_and_def( @staticmethod def get_function_parameters(function: types.FunctionType) -> str: + """ + Returns the parameters of a function as a continuous string, + e.g for `foo(x, y = 5)` it would return `"x, y = 5"` + """ function_parameters = [] function_source, function_definition = CodeInput._get_function_source_and_def( function @@ -216,6 +226,10 @@ def get_function_parameters(function: types.FunctionType) -> str: @staticmethod def get_function_body(function: types.FunctionType) -> str: + """ + Extracts the body of the given function, removing the signature, docstrings, + and adjusting indentation appropriately. + """ source_lines, _ = inspect.getsourcelines(function) found_def = False @@ -228,7 +242,7 @@ def get_function_body(function: types.FunctionType) -> str: if not (found_def): raise ValueError( "Did not find any def definition. Only functions with a " - "defition are supported" + "definition are supported" ) # Remove function definition @@ -259,15 +273,15 @@ def get_function_body(function: types.FunctionType) -> str: @property def function(self) -> types.FunctionType: """ - Return the compiled function object wrapped by an try-catch block + Returns the compiled function object wrapped by an try-catch block raising a `CodeValidationError`. - This can be assigned to a variable and then called, for instance:: + This can be assigned to a variable and then called, for instance: - func = widget.wrapped_function # This can raise a SyntaxError + func = widget.function # This can raise a CodeValidationError retval = func(parameters) - :raise SyntaxError: if the function code has syntax errors (or if + :raise CodeValidationError: if the function code has syntax errors (or if the function name is not a valid identifier) """ @@ -300,12 +314,12 @@ def builtins(self, value: dict[str, Any]): # is merged def format_generic_error_msg(exc, code_widget): """ - Return a string reproducing the traceback of a typical error. + Returns a string reproducing the traceback of a typical error. This includes line numbers, as well as neighboring lines. It will require also the code_widget instance, to get the actual source code. - :note: this must be called from withou the exception, as it will get the + :note: this must be called from within the exception, as it will get the current traceback state. :param exc: The exception that is being processed. diff --git a/src/scwidgets/code/_widget_parameters_panel.py b/src/scwidgets/code/_widget_parameters_panel.py index 2a84aba..6a53d44 100644 --- a/src/scwidgets/code/_widget_parameters_panel.py +++ b/src/scwidgets/code/_widget_parameters_panel.py @@ -8,14 +8,12 @@ class ParametersPanel(VBox): """ - A wrapper around ipywidgets.interactive to have more control how to connect the - parameters and the observation of parameters by buttons and the panels - + A wrapper around `ipywidgets.interactive` to have more control on how the + parameters are connected and the parameters are observed by the buttons and panels :param parameters: - Can be any input that is allowed as keyword arguments in ipywidgets.interactive - for the parameters. _options and other widget layout parameter are controlled - by CodeExercise. - + Can be any input that is allowed as keyword arguments in + `ipywidgets.interactive` for the parameters. `_option` and other widget layout + parameters are controlled by `CodeExercise`. """ def __init__( @@ -24,8 +22,8 @@ def __init__( ): if "_option" in parameters.keys(): raise ValueError( - "Found interactive argument `_option` in paramaters, but " - "ParametersPanels should be controled by an exercise widget " + "Found interactive argument `_option` in parameters, but " + "ParametersPanel should be controlled by an exercise widget " "to ensure correct initialization." ) @@ -52,6 +50,9 @@ def dummy_function(**kwargs): @property def param_to_widget_map(self) -> dict[str, Widget]: + """ + :return: A dictionary mapping parameter names to their corresponding widgets. + """ return self._param_to_widget_map @property @@ -74,7 +75,7 @@ def panel_parameters_widget(self) -> List[Widget]: def parameters(self) -> Dict[str, Any]: """ :return: All parameters that were given on initialization are returned, - also including also fixed parameters. + also including fixed parameters. """ return {key: widget.value for key, widget in self._param_to_widget_map.items()} @@ -100,7 +101,6 @@ def observe_parameters( trait_name: Union[str, Sentinel, List[str]], notification_type: Union[None, str, Sentinel] = "change", ): - """ """ for widget in self.panel_parameters_widget: widget.observe(handler, trait_name, notification_type) diff --git a/src/scwidgets/css_style.py b/src/scwidgets/css_style.py index d11df24..53ad11a 100644 --- a/src/scwidgets/css_style.py +++ b/src/scwidgets/css_style.py @@ -5,7 +5,7 @@ class CssStyle(HTML): """ - This HTML widget has to be displayed so the css style is loaded in the notebook. + This HTML widget has to be displayed so the CSS style is loaded in the notebook. :param preamble: Text to appear before the style sheet """ @@ -19,6 +19,6 @@ def __init__(self, preamble: str = ""): def get_css_style() -> HTML: return CssStyle( - preamble="HTML with scicode-widget css style sheet. " + preamble="HTML with scicode-widget CSS style sheet. " "Please keep this cell output alive." ) diff --git a/src/scwidgets/cue/_widget_cue.py b/src/scwidgets/cue/_widget_cue.py index d4a599a..11f7ab8 100644 --- a/src/scwidgets/cue/_widget_cue.py +++ b/src/scwidgets/cue/_widget_cue.py @@ -6,13 +6,13 @@ class CueWidget: """ - Observes a list of traits of widgets and sets cue when one of the widgets traits + Observes a list of traits of widgets and sets cue when one of the widgets' traits change. The behavior when the cue is set has to be implemented by children class. :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. + The widget to observe if the `traits_to_observe` has changed. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe if changed. Specify `traitlets.All` to observe all traits. :param cued: Specifies if it is cued on initialization diff --git a/src/scwidgets/cue/_widget_cue_box.py b/src/scwidgets/cue/_widget_cue_box.py index bfaf0de..2f7cb30 100644 --- a/src/scwidgets/cue/_widget_cue_box.py +++ b/src/scwidgets/cue/_widget_cue_box.py @@ -8,28 +8,27 @@ class CueBox(VBox, CueWidget): """ - A box around the widget :param widget_to_cue: that adds a visual cue defined in the - :param css_style: when the trait :param traits_to_observe: in the widget :param - widgets_to_observe: changes. If the :param widgets_to_observe: is a list then for - each widget is observed. + A box around the widget `widget_to_cue` that adds a visual cue defined in the + `css_style` when the trait `traits_to_observe` in the widget `widgets_to_observe` + changes. If the `widgets_to_observe` is a list, each widget is observed separately. :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. + The widget to observe if the `traits_to_observe` has changed. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe if changed. Specify `traitlets.All` to observe all traits. :param widget_to_cue: - The widget to wrap the box around to give a visual cue, once :param - traits_to_observe: has changed - If None, then the :param widget_to_cue: is set to :param widgets_to_observe:. + The widget to wrap the box around to give a visual cue, once + `traits_to_observe` has changed + If None, then the `widget_to_cue` is set to `widgets_to_observe`. :param cued: Specifies if it is cued on initialization - :param css_syle: + :param css_style: - **base**: the css style of the box during initialization - - **cue**: the css style that is added when :param - traits_to_observe: in widget :param widgets_to_observe: changes. + - **cue**: the css style that is added when `traits_to_observe` + in widget `widgets_to_observe` changes. It is supposed to change the style of the box such that the user has a visual - cue that :param widget_to_cue: has changed. + cue that `widget_to_cue` has changed. Further accepts the same (keyword) arguments as :py:class:`ipywidgets.Box`. """ @@ -92,21 +91,20 @@ def cued(self, cued: bool): class SaveCueBox(CueBox): """ - A box around the widget :param widget_to_cue: that adds a visual cue defined in the - :param css_style: when the trait :param traits_to_observe: in the widget :param - widgets_to_observe: changes. + A box around the widget `widget_to_cue` that adds a visual cue related to saving + when the trait `traits_to_observe` in the widget `widgets_to_observe` changes. :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. + The widget to observe if the `traits_to_observe` has changed. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe if changed. Specify `traitlets.All` to observe all traits. :param cued: Specifies if it is cued on initialization :param widget_to_cue: - The widget to wrap the box around to give a visual cue, once :param - traits_to_observe: has changed - If None, then the :param widget_to_cue: is set to :param widgets_to_observe:. + The widget to wrap the box around to give a visual cue, once + `traits_to_observe` has changed + If None, then the `widget_to_cue` is set to `widgets_to_observe`. Further accepts the same (keyword) arguments as :py:class:`ipywidgets.Box`. """ @@ -133,19 +131,18 @@ def __init__( class CheckCueBox(CueBox): """ - A box around the widget :param widget_to_cue: that adds a visual cue defined in the - :param css_style: when the trait :param traits_to_observe: in the widget :param - widgets_to_observe: changes. + A box around the widget `widget_to_cue` that adds a visual cue related to checking + when the trait `traits_to_observe` in the widget `widgets_to_observe` changes. :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. + The widget to observe if the `traits_to_observe` has changed. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe if changed. Specify `traitlets.All` to observe all traits. :param widget_to_cue: - The widget to wrap the box around to give a visual cue, once :param - traits_to_observe: has changed - If None, then the :param widget_to_cue: is set to :param widgets_to_observe:. + The widget to wrap the box around to give a visual cue, once + `traits_to_observe` has changed + If None, then the `widget_to_cue` is set to `widgets_to_observe`. :param cued: Specifies if it is cued on initialization @@ -174,21 +171,20 @@ def __init__( class UpdateCueBox(CueBox): """ - A box around the widget :param widget_to_cue: that adds a visual cue defined in the - :param css_style: when the trait :param traits_to_observe: in the widget :param - widgets_to_observe: changes. + A box around the widget `widget_to_cue` that adds a visual cue related to updating + when the trait `traits_to_observe` in the widget `widgets_to_observe` changes. :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. + The widget to observe if the `traits_to_observe` has changed. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe if changed. Specify `traitlets.All` to observe all traits. - :param widget_to_cue: - The widget to wrap the box around to give a visual cue, once :param - traits_to_observe: has changed - If None, then the :param widget_to_cue: is set to :param widgets_to_observe:. :param cued: Specifies if it is cued on initialization + :param widget_to_cue: + The widget to wrap the box around to give a visual cue, once + `traits_to_observe` has changed + If None, then the `widget_to_cue` is set to `widgets_to_observe`. Further accepts the same (keyword) arguments as :py:class:`ipywidgets.Box`. """ diff --git a/src/scwidgets/cue/_widget_cue_figure.py b/src/scwidgets/cue/_widget_cue_figure.py index 9597744..acc9ed9 100644 --- a/src/scwidgets/cue/_widget_cue_figure.py +++ b/src/scwidgets/cue/_widget_cue_figure.py @@ -16,15 +16,15 @@ class CueFigure(CueOutput): """ - A cued displayable ipywidget.Output for a matplotlib figure. Provides utilities to - clear and draw the updated figure. For the matplotlib inline backend it closes the - active figure to prevent any display outside of the container, that happens on the - creation of the figure because pyplot does magic behind the curtain that is hard to - suppress. For the matplot interactive widget backend, named "nbagg", it wraps te - figure within. + A cued, displayable `ipywidget.Output` for a `matplotlib` figure. Provides + utilities to clear and draw the updated figure. For the `matplotlib inline` + backend, it closes the active figure to prevent any display outside of the + container, which happens on the creation of the figure because `pyplot` does magic + behind the curtain that is hard to suppress. For the matplotlib interactive widget + backend, named "nbagg", it wraps the figure within. :param figure: - The matplotlib figure + The `matplotlib` figure :param widgets_to_observe: The widget to observe if the :param traits_to_observe: has changed. :param traits_to_observe: @@ -34,7 +34,7 @@ class CueFigure(CueOutput): Specifies if it is cued on initialization :param show_toolbars: Hide toolbars and headers when using in widget mode. - :param css_syle: + :param css_style: - **base**: the css style of the box during initialization - **cue**: the css style that is added when :param traits_to_observe: in widget :param widgets_to_observe: changes. @@ -99,7 +99,7 @@ def __init__( def clear_display(self, wait=False): """ :param wait: - same meaning as for the `wait` parameter in the ipywidgets.clear_output + same meaning as for the `wait` parameter in the `ipywidgets.clear_output` function """ if matplotlib.backends.backend in [ @@ -154,7 +154,7 @@ def draw_display(self): def clear_figure(self): """ - Clears the figure while retainin axes. figure.clear() removes the axes + Clears the figure while retaining axes as figure.clear() removes the axes sometimes. """ for ax in self.figure.get_axes(): diff --git a/src/scwidgets/cue/_widget_cue_object.py b/src/scwidgets/cue/_widget_cue_object.py index f1925dc..20867a7 100644 --- a/src/scwidgets/cue/_widget_cue_object.py +++ b/src/scwidgets/cue/_widget_cue_object.py @@ -13,24 +13,24 @@ class CueObject(CueOutput): """ - A cued displayable ipywidget.Output for any Python object. Provides utilities to - clear and redraw the object, for example after an update. + A cued displayable `ipywidget.Output` for any Python object. Provides utilities to + clear and redraw the object, for example, after an update. :param object: The object to display :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. + The widget to observe if the `traits_to_observe` has changed. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe if changed. Specify `traitlets.All` to observe all traits. :param cued: Specifies if it is cued on initialization - :param css_syle: + :param css_style: - **base**: the css style of the box during initialization - - **cue**: the css style that is added when :param - traits_to_observe: in widget :param widgets_to_observe: changes. + - **cue**: the css style that is added when + `traits_to_observe` in widget `widgets_to_observe` changes. It is supposed to change the style of the box such that the user has a visual - cue that :param widget_to_cue: has changed. + cue that `widget_to_cue` has changed. """ def __init__( diff --git a/src/scwidgets/cue/_widget_cue_output.py b/src/scwidgets/cue/_widget_cue_output.py index c63a44b..6df84da 100644 --- a/src/scwidgets/cue/_widget_cue_output.py +++ b/src/scwidgets/cue/_widget_cue_output.py @@ -12,21 +12,21 @@ class CueOutput(Output, CueWidget): """ - A cued displayable ipywidget.Output for any Python object. + A cued displayable `ipywidget.Output` for any Python object. :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. + The widget to observe if the `traits_to_observe` has changed. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe if changed. Specify `traitlets.All` to observe all traits. :param cued: Specifies if it is cued on initialization - :param css_syle: + :param css_style: - **base**: the css style of the box during initialization - - **cue**: the css style that is added when :param - traits_to_observe: in widget :param widgets_to_observe: changes. + - **cue**: the css style that is added when `traits_to_observe` + in widget `widgets_to_observe` changes. It is supposed to change the style of the box such that the user has a visual - cue that :param widget_to_cue: has changed. + cue that `widget_to_cue` has changed. """ def __init__( diff --git a/src/scwidgets/cue/_widget_reset_cue_button.py b/src/scwidgets/cue/_widget_reset_cue_button.py index 0b53a09..8e9d669 100644 --- a/src/scwidgets/cue/_widget_reset_cue_button.py +++ b/src/scwidgets/cue/_widget_reset_cue_button.py @@ -8,36 +8,35 @@ class ResetCueButton(Button, CueWidget): """ - A button that resets the cueing of the :param cue_widgets: on a successful action. + A button that resets the cueing of the `cue_widgets` on a successful action. :param cue_widgets: - List of cue boxes the button resets on successuful click + List of cue boxes the button resets on successful click We assert that all boxes observe the same traits of the same widget :param action: - A callable that returns a boolean that specifies if the action was successul. - If is called on a button click. The cues in :param cue_widgets: - are removed if it - was successful, if False nothing happens. + A callable that returns a boolean that specifies if the action was successful. + It is called on a button click, and the cues in `cue_widgets` are removed + if it was successful. If set to ``False``, nothing happens. :param disable_on_successful_action: Specifies if the button should be disabled on a successful action :param disable_during_action: Specifies if the button should be disabled during the action - :param css_syle: + :param css_style: - **base**: the css style of the box during initialization - - **cue**: the css style that is added when :param - traits_to_observe: in widget :param widget_to_observe: changes. + - **cue**: the css style that is added when `traits_to_observe` + in widget `widget_to_observe` changes. It is supposed to change the style of the box such that the user has a visual - cue that :param widget_to_cue: has changed. + cue that `widget_to_cue` has changed. :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. If ``None`` - then widgets from :param cue_widgets: are taken. + The widget to observe if the `traits_to_observe` has changed. If ``None`` + then widgets from `cue_widgets` are taken. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. + The trait from the `widgets_to_observe` to observe changes of. Specify `traitlets.All` to observe all traits. If ``None`` then traits - from :param cue_widgets: are taken. + from `cue_widgets` are taken. :param cued: Specifies if it is cued on initialization. If ``None`` then the button is - cued when :param cue_widgets: is cued. + cued when `cue_widgets` is cued. Further accepts the same (keyword) arguments as :py:class:`ipywidgets.Button`. """ @@ -105,12 +104,12 @@ def set_cue_widgets( ): """ :param cue_widgets: - List of cue boxes the button resets on successuful click + List of cue boxes the button resets on successful click We assert that all boxes observe the same traits of the same widget - :param overwrite_observes: - specifies if observes related to cueing of button should be overwritten by - the widgets ant traits of the :param cue_widgets: + :param overwrite_cue_observes: + If `True`, the function will override the existing observation settings + based on `cue_widgets`. """ # set new cue widgets widgets_to_observe = [] @@ -168,30 +167,29 @@ def _on_click(self, button: Button): class SaveResetCueButton(ResetCueButton): """ - A button that resets the cueing of the :param cue_widgets: on a successful action. + A button that resets the cueing of the `cue_widgets` on a successful Save action. :param cue_widgets: - List of cue boxes the button resets on successuful click + List of cue boxes the button resets on successful click We assert that all boxes observe the same traits of the same widget :param action: - A callable that returns a boolean that specifies if the action was successul. - If is called on a button click. The cues in :param cue_widgets: - are removed if it - was successful, if False nothing happens. + A callable that returns a boolean that specifies if the action was successful. + It is called on a button click, and the cues in `cue_widgets` are removed + if it was successful. If set to ``False``, nothing happens. :param disable_on_successful_action: Specifies if the button should be disabled on a successful action :param disable_during_action: Specifies if the button should be disabled during the action :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. If ``None`` - then widgets from :param cue_widgets: are taken. + The widget to observe if the `traits_to_observe` has changed. If ``None`` + then widgets from `cue_widgets` are taken. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. - Specify `traitlets.All` to observe all traits. If ``None`` then traits - from :param cue_widgets: are taken. + The trait from the `widgets_to_observe` to observe changes of. + Specify `traitlets.All` to observe all traits. If ``None``, traits + from `cue_widgets` are taken. :param cued: Specifies if it is cued on initialization. If ``None`` then the button is - cued when :param cue_widgets: is cued. + cued when `cue_widget`: is cued. Further accepts the same (keyword) arguments as :py:class:`ipywidgets.Button`. """ @@ -230,30 +228,30 @@ def __init__( class CheckResetCueButton(ResetCueButton): """ - A button that resets the cueing of the :param cue_widgets: on a successful action. + A button that resets the cueing of the :param cue_widgets: on a successful check + action. :param cue_widgets: - List of cue boxes the button resets on successuful click + List of cue boxes the button resets on successful click We assert that all boxes observe the same traits of the same widget :param action: - A callable that returns a boolean that specifies if the action was successul. - If is called on a button click. The cues in :param cue_widgets: - are removed if it - was successful, if False nothing happens. + A callable that returns a boolean that specifies if the action was successful. + It is called on a button click, and the cues in `cue_widgets` are removed + if it was successful. If set to ``False``, nothing happens. :param disable_on_successful_action: Specifies if the button should be disabled on a successful action :param disable_during_action: Specifies if the button should be disabled during the action :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. If ``None`` - then widgets from :param cue_widgets: are taken. + The widget to observe if the `traits_to_observe` has changed. If ``None`` + then widgets from `cue_widgets` are taken. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. - Specify `traitlets.All` to observe all traits. If ``None`` then traits - from :param cue_widgets: are taken. + The trait from the `widgets_to_observe` to observe changes of. + Specify `traitlets.All` to observe all traits. If ``None``, traits + from `cue_widgets` are taken. :param cued: Specifies if it is cued on initialization. If ``None`` then the button is - cued when :param cue_widgets: is cued. + cued when `cue_widget`: is cued. Further accepts the same (keyword) arguments as :py:class:`ipywidgets.Button`. """ @@ -292,30 +290,30 @@ def __init__( class UpdateResetCueButton(ResetCueButton): """ - A button that resets the cueing of the :param cue_widgets: on a successful action. + A button that resets the cueing of the :param cue_widgets: on a successful update + action. :param cue_widgets: - List of cue boxes the button resets on successuful click + List of cue boxes the button resets on successful click We assert that all boxes observe the same traits of the same widget :param action: - A callable that returns a boolean that specifies if the action was successul. - If is called on a button click. The cues in :param cue_widgets: - are removed if it - was successful, if False nothing happens. + A callable that returns a boolean that specifies if the action was successful. + It is called on a button click, and the cues in `cue_widgets` are removed + if it was successful. If set to ``False``, nothing happens. :param disable_on_successful_action: Specifies if the button should be disabled on a successful action :param disable_during_action: Specifies if the button should be disabled during the action :param widgets_to_observe: - The widget to observe if the :param traits_to_observe: has changed. If ``None`` - then widgets from :param cue_widgets: are taken. + The widget to observe if the `traits_to_observe` has changed. If ``None`` + then widgets from `cue_widgets` are taken. :param traits_to_observe: - The trait from the :param widgets_to_observe: to observe if changed. - Specify `traitlets.All` to observe all traits. If ``None`` then traits - from :param cue_widgets: are taken. + The trait from the `widgets_to_observe` to observe changes of. + Specify `traitlets.All` to observe all traits. If ``None``, traits + from `cue_widgets` are taken. :param cued: Specifies if it is cued on initialization. If ``None`` then the button is - cued when :param cue_widgets: is cued. + cued when `cue_widget`: is cued. Further accepts the same (keyword) arguments as :py:class:`ipywidgets.Button`. """ diff --git a/src/scwidgets/exercise/_widget_code_exercise.py b/src/scwidgets/exercise/_widget_code_exercise.py index c0ec163..d918d4e 100644 --- a/src/scwidgets/exercise/_widget_code_exercise.py +++ b/src/scwidgets/exercise/_widget_code_exercise.py @@ -34,17 +34,17 @@ class CodeExercise(VBox, CheckableWidget, ExerciseWidget): """ A widget to demonstrate code interactively in a variety of ways. It is a combination - of the several widgets that allow to check check, run and visualize code. + of the several widgets that allow to check, run and visualize code. :param code: - A function or CodeInput that is the input of code + A function or :py:class:`CodeInput` that is the input of code :param check_registry: A check registry that is used to register checks :param exercise_registry: A exercise registry that is used to register the answers to save them - later. If specified the save and load panel will appear. + later. If specified, the save and load panel will appear. :param key: The key that is used to store the exercise in the json file. @@ -59,18 +59,18 @@ class CodeExercise(VBox, CheckableWidget, ExerciseWidget): or parameters :param outputs: - List of CueOuputs that are drawn and refreshed + List of CueOutputs that are drawn and refreshed :param update: A function that is run during the update process. The function takes as argument - the CodeExercise, so it can update all cue_ouputs + the CodeExercise, so it can update all cue_outputs :param description: A string describing the exercises that will be put into an HTML widget above the exercise. :param title: - A title for the exercise. If not given the key is used. + A title for the exercise. If not given, `key` is used. """ def __init__( @@ -173,7 +173,7 @@ def __init__( f"WidgetCodeInput but got {type(code)!r}" ) - # check compability between code and parameters, can only be checked if + # check compatibility between code and parameters, can only be checked if # update_func is not used because we cannot know how the code input is used if update is None and code is not None and parameters is not None: if isinstance(parameters, dict): @@ -565,7 +565,7 @@ def answer(self, answer: dict): @property def parameters_panel(self) -> Union[ParametersPanel, None]: """ - :return: The parametergs panel widget. + :return: The parameters panel widget. """ return self._parameters_panel @@ -586,7 +586,7 @@ def panel_parameters(self) -> Dict[str, Check.FunInParamT]: def parameters(self) -> Dict[str, Check.FunInParamT]: """ :return: All parameters that were given on initialization are returned, - also including also fixed parameters. + including fixed parameters. """ return ( {} if self._parameters_panel is None else self._parameters_panel.parameters @@ -772,9 +772,8 @@ def _on_click_update_action(self) -> bool: def run_update(self): """ - Invokes an update run, the same that is invoked by a click on the update button - or for :param update_mode: "release" and "continuous" when a parameter panel - parameter is changed + Invokes an update run, the same that is invoked by a click on the update button, + or when a parameter is changed for `update_mode` "release" and "continuous" """ if self._update_button is not None: # to also invoke the reset cue action, we click the cued button @@ -787,7 +786,7 @@ def run_update(self): def run_code(self, *args, **kwargs) -> Check.FunOutParamsT: """ Runs the `code` with the given (keyword) arguments and returns the output of the - `code`. If no `code` was given on intialization, then a `ValueError` is raised. + `code`. If no `code` was given on initialization, then a `ValueError` is raised. """ try: if self._code is None: diff --git a/src/scwidgets/exercise/_widget_exercise_registry.py b/src/scwidgets/exercise/_widget_exercise_registry.py index faa6410..abf6e94 100644 --- a/src/scwidgets/exercise/_widget_exercise_registry.py +++ b/src/scwidgets/exercise/_widget_exercise_registry.py @@ -18,14 +18,14 @@ class ExerciseWidget: """ Any widget inheriting from this class can be (de)serialized - :py:class:`WidgetStateRegistry`. The serialization offered by ipywidgets - cannot be loaded out-of-the-box for restarted notebook since the widget IDs change + by :py:class:`WidgetStateRegistry`. The serialization offered by `ipywidgets` + cannot be loaded out-of-the-box for a restarted notebook since the widget IDs change :param exercise_registry: the exercise registry that registers the answers for this widget :param exercise_key: - Identifier for the widget, must be unique for each regestired widget + Identifier for the widget, must be unique for each registered widget Reference --------- @@ -70,14 +70,14 @@ def answer(self, answer: dict): def handle_save_result(self, result: Union[str, Exception]) -> None: """ Function that controls how a save result is handled. If the result is a string, - the saving was successfull. The result contains a string that can be outputed. + the saving was successful. The result contains a string that can be outputed. """ raise NotImplementedError("handle_save_result has not been implemented") def handle_load_result(self, result: Union[str, Exception]) -> None: """ Function that controls how a load result is handled. If the result is a string, - the loading was successfull. The result contains a string that can be outputed. + the loading was successful. The result contains a string that can be outputed. """ raise NotImplementedError("handle_load_result has not been implemented") @@ -99,7 +99,7 @@ def load(self) -> Union[str, Exception]: ) if self._exercise_key is None: raise ValueError( - "No exercise key given on initialization, save cannot be used" + "No exercise key given on initialization, load cannot be used" ) return self._exercise_registry.load_answer_from_loaded_file(self._exercise_key) @@ -147,7 +147,7 @@ def extract_forbidden_characters(name): @staticmethod def verify_valid_student_name(student_name: str): if FilenameParser.is_name_empty(student_name): - raise ValueError("Your name is empty. Please provide a new one.") + raise ValueError("Your name is empty. Please provide one.") forbidden_characters = FilenameParser.extract_forbidden_characters(student_name) if len(forbidden_characters) > 0: @@ -277,10 +277,10 @@ def loaded_file_name(self) -> Union[str, None]: def register_widget(self, widget: ExerciseWidget, exercise_key: Hashable): """ :param widget: - widget answer is save on click of save button + widget answer that is saved on click of the save button :param exercise_key: - unique exercise key for widget to store, so it can be reloaded persistently - after a restart of the python kernel + unique exercise key for the widget to be stored under, + so it can be reloaded persistently after a restart of the python kernel """ self._widgets[exercise_key] = widget @@ -288,8 +288,8 @@ def create_new_file_from_dropdown(self) -> str: """Creates a new file containing all students answers from the selected file in the dropdown menu. - :raises FileExistsError: If the file arleady exists - :return: A message to print + :raises FileExistsError: If the file already exists + :return: The success message """ self.create_new_file_from_student_name(self._student_name_text.value) return f"File {self._loaded_file_name!r} created and loaded." @@ -297,8 +297,8 @@ def create_new_file_from_dropdown(self) -> str: def get_answer_filename(self, student_name: str) -> str: """Returns the filename containing all answers for the student name. - :param student_name: The name of the student used for the filename - :raises ValueError: If student name is not valid + :param student_name: The name of the student used in the filename + :raises ValueError: If the student name is not valid :return: The filename """ FilenameParser.verify_valid_student_name(student_name) @@ -312,10 +312,10 @@ def get_answer_filename(self, student_name: str) -> str: return answers_filename def create_new_file_from_student_name(self, student_name: str): - """Creates a new exercise file containing all students answers. + """Creates a new exercise file containing all the student's answers. :param student_name: The name of the student used for the exercise file - :raises FileExistsError: If the file arleady exists + :raises FileExistsError: If the file already exists :return: A message to print """ answers_filename = self.get_answer_filename(student_name) @@ -348,7 +348,7 @@ def load_answer_from_student_name( Loads the answer with key `exercise_key` from the file corresponding to `student_name`. - :raises KeyError: Corresponding widget to exercise_key cannot be found + :raises KeyError: Corresponding widget to `exercise_key` cannot be found :raises KeyError: Corresponding key in file cannot be found :raises FileNotFoundError: If the file cannot be found :param student_name: The name of the student @@ -363,8 +363,8 @@ def load_answer_from_loaded_file( """ Loads the answer with key `exercise_key` from the currently loaded file. - :raises ValueError: No have has been loaded - :raises KeyError: Corresponding widget to exercise_key cannot be found + :raises ValueError: No file has been loaded + :raises KeyError: Corresponding widget to `exercise_key` cannot be found :raises KeyError: Corresponding key in file cannot be found :raises FileNotFoundError: If the file cannot be found :param exercise_key: Unique exercise key for widget to store, so it can @@ -382,7 +382,7 @@ def load_answer( """ Loads the answer with key `exercise_key` from the `answer_filename`. - :raises KeyError: Corresponding widget to exercise_key cannot be found + :raises KeyError: Corresponding widget to `exercise_key` cannot be found :raises KeyError: Corresponding key in file cannot be found :raises FileNotFoundError: If the file cannot be found :param answers_filename: The file with the answer @@ -500,6 +500,7 @@ def save_answer(self, exercise_key: Hashable) -> str: def save_all_answers(self) -> str: """ + Saves all answers to the loaded JSON file. Returns a success message or raises an error when failed """ if self._loaded_file_name is None: diff --git a/src/scwidgets/exercise/_widget_text_exercise.py b/src/scwidgets/exercise/_widget_text_exercise.py index e2a9aea..5cee4ad 100644 --- a/src/scwidgets/exercise/_widget_text_exercise.py +++ b/src/scwidgets/exercise/_widget_text_exercise.py @@ -11,18 +11,18 @@ class TextExercise(VBox, ExerciseWidget): """ :param textarea: - a custom textarea with custom styling, if not specified the standard parameters - are given. + a custom `textarea` with custom styling. If not specified, the standard + parameters are given. :param key: - The key that is used to store the exercise in the json file. + The key that is used to store the exercise in the JSON file. :param description: A string describing the exercises that will be put into an HTML widget above the exercise. :param title: - A title for the exercise. If not given the key is used. + A title for the exercise. If not given, the key is used. """ def __init__(