From eab9b27fa549e3849150efbebde62582c11f4752 Mon Sep 17 00:00:00 2001 From: Simone Baffelli Date: Fri, 19 Apr 2024 14:47:00 +0200 Subject: [PATCH 1/8] Fixed tests. --- tutorial/tests/data/lines2.txt | 5 +++++ tutorial/tests/test_input_output.py | 21 +++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 tutorial/tests/data/lines2.txt diff --git a/tutorial/tests/data/lines2.txt b/tutorial/tests/data/lines2.txt new file mode 100644 index 00000000..72122eff --- /dev/null +++ b/tutorial/tests/data/lines2.txt @@ -0,0 +1,5 @@ +this file has +more words in each +line +but it is shorter I think +I hope you have fun solving this exercise diff --git a/tutorial/tests/test_input_output.py b/tutorial/tests/test_input_output.py index 5bcfe12b..c3319399 100644 --- a/tutorial/tests/test_input_output.py +++ b/tutorial/tests/test_input_output.py @@ -30,7 +30,7 @@ def test_print_odd(function_to_test, num: int): with contextlib.redirect_stdout(reference_stdout): reference_print_odd(num) - assert reference_stdout.getvalue() == solution_stdout.getvalue() + assert reference_stdout.getvalue() == solution_stdout.getvalue() def reference_find_all_files(f: pl.Path) -> List[pl.Path]: @@ -80,9 +80,16 @@ def test_write_file(function_to_test, tmp_path: pl.Path): def reference_read_write_file(input_file: pl.Path, output_file: pl.Path) -> None: + # We open the input and output file at the same time with input_file.open("r") as read_file, output_file.open("w") as write_file: + # Here we iterate over each line of the input file for line in read_file.readlines(): - write_file.write("{}, {}\n".format(line.strip("\n\r"), len(line))) + # We remove line breaks and write the line to the output file + clean_line = line.strip("\n\r") + # We crete the output line + output_line = f"{clean_line}, {len(clean_line)}\n" + # Finally we write the line and its length to the output file + write_file.write(output_line) def test_read_write_file(function_to_test, tmp_path: pl.Path): @@ -105,8 +112,9 @@ def reference_exercise1(file: pl.Path) -> Dict[str, List[str]]: } -def test_exercise1(function_to_test): - f = get_data("example.csv") +@pytest.mark.parametrize("file", ["example.csv"]) +def test_exercise1(function_to_test, file: str): + f = get_data(file) assert function_to_test(f) == reference_exercise1(f) @@ -121,8 +129,9 @@ def reference_exercise2(file: pl.Path) -> int: ) -def test_exercise2(function_to_test): - f = get_data("lines.txt") +@pytest.mark.parametrize("file", ["lines.txt", "lines2.txt"]) +def test_exercise2(function_to_test, file: str): + f = get_data(file) assert function_to_test(f) == reference_exercise2(f) From b014ac3f8af7148bdd30b7ba38cc7d89ceac2120 Mon Sep 17 00:00:00 2001 From: Simone Baffelli Date: Thu, 2 May 2024 14:21:17 +0200 Subject: [PATCH 2/8] Improved wording of exercise --- input_output.ipynb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/input_output.ipynb b/input_output.ipynb index f0bc31c5..c3ec1ffb 100644 --- a/input_output.ipynb +++ b/input_output.ipynb @@ -1104,7 +1104,7 @@ "tags": [] }, "source": [ - "Write a function to read all the lines from [`lines.txt`](./tutorial/tests/data/lines.txt) and count the number of words in the file. The solution should be a single number.\n", + "Write a function to read all the lines from `input_file` and count the number of words in the file. The solution should be a single number.\n", "\n", "For example, for the file\n", "```\n", @@ -1118,7 +1118,11 @@ "\n", "
\n", "

Hint

\n", - " The file is available as the parameter input_file of solution_exercise2 function\n", + "
    \n", + "
  • The file is available as the parameter input_file of solution_exercise2 function
  • \n", + "
  • A word consist of printable characters without line ends and spaces
  • \n", + "
\n", + " \n", "
\n", "\n", "\n" @@ -1338,7 +1342,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.10.14" }, "vscode": { "interpreter": { From 3ed76d9dfbb5d67a0fe15658a9ee3dcf1e330e4c Mon Sep 17 00:00:00 2001 From: Simone Baffelli Date: Fri, 3 May 2024 08:13:17 +0200 Subject: [PATCH 3/8] Update input_output.ipynb Co-authored-by: Despina Adamopoulou <16343312+despadam@users.noreply.github.com> --- input_output.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input_output.ipynb b/input_output.ipynb index c3ec1ffb..17a52916 100644 --- a/input_output.ipynb +++ b/input_output.ipynb @@ -1120,7 +1120,7 @@ "

Hint

\n", "
    \n", "
  • The file is available as the parameter input_file of solution_exercise2 function
  • \n", - "
  • A word consist of printable characters without line ends and spaces
  • \n", + "
  • A word consists of printable characters without line ends and spaces
  • \n", "
\n", " \n", "\n", From 27fbfd8442ea206b1429a2cbebb27859cdaebb18 Mon Sep 17 00:00:00 2001 From: Simone Baffelli Date: Fri, 3 May 2024 08:24:05 +0200 Subject: [PATCH 4/8] Fixed test for print_odd exercise --- tutorial/tests/test_input_output.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tutorial/tests/test_input_output.py b/tutorial/tests/test_input_output.py index c3319399..fe1e581a 100644 --- a/tutorial/tests/test_input_output.py +++ b/tutorial/tests/test_input_output.py @@ -15,22 +15,24 @@ def get_data(name: str, data_dir: str = "data") -> pl.Path: return (pl.Path.cwd() / f"tutorial/tests/{data_dir}/{name}").resolve() -def reference_print_odd(num: int) -> None: - for i in range(num): +def reference_print_odd(n: int) -> None: + for i in range(n): if i % 2 != 0: print(i) -@pytest.mark.parametrize("num", [1, 2, 3, 4, 5]) -def test_print_odd(function_to_test, num: int): +@pytest.mark.parametrize("n", [1, 2, 3, 4, 5]) +def test_print_odd(function_to_test, n: int): with StringIO() as solution_stdout, StringIO() as reference_stdout: with contextlib.redirect_stdout(solution_stdout): - function_to_test(num) + function_to_test(n) + solution_text = solution_stdout.getvalue() with contextlib.redirect_stdout(reference_stdout): - reference_print_odd(num) + reference_print_odd(n) + reference_text = reference_stdout.getvalue() - assert reference_stdout.getvalue() == solution_stdout.getvalue() + assert solution_text == reference_text def reference_find_all_files(f: pl.Path) -> List[pl.Path]: From 506ac734500285b69ac1c933661acc7f089d7515 Mon Sep 17 00:00:00 2001 From: Simone Baffelli Date: Fri, 3 May 2024 09:36:29 +0200 Subject: [PATCH 5/8] Fixed error message --- tutorial/tests/test_input_output.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tutorial/tests/test_input_output.py b/tutorial/tests/test_input_output.py index fe1e581a..99e275cf 100644 --- a/tutorial/tests/test_input_output.py +++ b/tutorial/tests/test_input_output.py @@ -75,10 +75,13 @@ def test_write_file(function_to_test, tmp_path: pl.Path): function_to_test(tmp_user) reference_write_file(tmp_test) - if not tmp_user.exists(): - pytest.fail("Cannot read from inexistent file.") + assert ( + tmp_user.exists() + ), """The file was not created. Make sure to create the file in the function. Hint: use `open(output_file, "w")`""" - assert tmp_user.read_text() == tmp_test.read_text() + assert ( + tmp_user.read_text() == tmp_test.read_text() + ), "The file content is not correct." def reference_read_write_file(input_file: pl.Path, output_file: pl.Path) -> None: From f0e83ea72c1a1efa5ac4db2bbca0f2afc913b10c Mon Sep 17 00:00:00 2001 From: Simone Baffelli Date: Fri, 3 May 2024 09:38:35 +0200 Subject: [PATCH 6/8] Fixed another misleading error message --- tutorial/tests/test_input_output.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tutorial/tests/test_input_output.py b/tutorial/tests/test_input_output.py index 99e275cf..419aa8f3 100644 --- a/tutorial/tests/test_input_output.py +++ b/tutorial/tests/test_input_output.py @@ -104,6 +104,7 @@ def test_read_write_file(function_to_test, tmp_path: pl.Path): function_to_test(input_file, output_file) reference_read_write_file(input_file, test_output_file) + assert output_file.exists(), "The output file was not created." assert output_file.read_text() == test_output_file.read_text() From eaac18ca68c9e8a3e7519250f779645f2b429f12 Mon Sep 17 00:00:00 2001 From: Simone Baffelli Date: Fri, 3 May 2024 09:59:13 +0200 Subject: [PATCH 7/8] Fixed TOC. --- input_output.ipynb | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/input_output.ipynb b/input_output.ipynb index 17a52916..7e40ce0d 100644 --- a/input_output.ipynb +++ b/input_output.ipynb @@ -5,21 +5,31 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Table of contents\n", - "\n", - "- [String input](#String-input)\n", - "- [File I/O](#File-I/O)\n", - "- [Writing to a file](#Writing-to-a-file)\n", - "- [Context managers](#Context-managers)\n", - "- [Binary I/O](#Binary-I/O)\n", - " - [Bytes and strings](#Bytes-and-strings)\n", - " - [Converting bytes to text](#Converting-bytes-to-text)\n", - "- [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", - "- [Exercises](#Exercises)\n", - " - [Exercise 1: CSV to dictionary 🌶️🌶️](#Exercise-1:-CSV-to-dictionary-🌶️🌶️)\n", - " - [Exercise 2: Counting words 🌶️](#Exercise-2:-Counting-words-🌶️)\n", - " - [Exercise 4: Translating words 🌶️🌶️](#Exercise-4:-Translating-words-🌶️🌶️)\n", - " - [Exercise 5: Binary format 🌶️🌶️🌶️](#Exercise-5:-Binary-format-🌶️🌶️🌶️)\n" + "# Table of Contents\n", + " - [References](#References)\n", + " - [Introduction](#Introduction)\n", + " - [String input and output ](#String-input-and-output)\n", + " - [String output](#String-output)\n", + " - [String input](#String-input)\n", + " - [Exercises](#Exercises)\n", + " - [File I/O](#File-I/O)\n", + " - [Paths](#Paths)\n", + " - [Exercises](#Exercises)\n", + " - [Reading from a file](#Reading-from-a-file)\n", + " - [Exercises](#Exercises)\n", + " - [Writing to a file](#Writing-to-a-file)\n", + " - [Exercises](#Exercises)\n", + " - [Context managers](#Context-managers)\n", + " - [Binary I/O](#Binary-I/O)\n", + " - [Bytes and strings](#Bytes-and-strings)\n", + " - [Converting bytes to text ](#Converting-bytes-to-text)\n", + " - [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", + " - [Exercises](#Exercises)\n", + " - [Exercise 1: CSV to dictionary 🌶️🌶️](#Exercise-1:-CSV-to-dictionary-🌶️🌶️)\n", + " - [Exercise 2: Counting words 🌶️](#Exercise-2:-Counting-words-🌶️)\n", + " - [Exercise 3: Letter statistics 🌶️🌶️](#Exercise-3:-Letter-statistics-🌶️🌶️)\n", + " - [Exercise 4: Translating words 🌶️🌶️](#Exercise-4:-Translating-words-🌶️🌶️)\n", + " - [Exercise 5: Binary format 🌶️🌶️🌶️](#Exercise-5:-Binary-format-🌶️🌶️🌶️)" ] }, { From 3819b06d28553a768c568f6486b1215a622358ba Mon Sep 17 00:00:00 2001 From: Despina Adamopoulou Date: Fri, 3 May 2024 14:59:02 +0200 Subject: [PATCH 8/8] updating TOC and titles for exercise sections --- input_output.ipynb | 111 +++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/input_output.ipynb b/input_output.ipynb index 7e40ce0d..12177580 100644 --- a/input_output.ipynb +++ b/input_output.ipynb @@ -6,30 +6,29 @@ "metadata": {}, "source": [ "# Table of Contents\n", - " - [References](#References)\n", - " - [Introduction](#Introduction)\n", - " - [String input and output ](#String-input-and-output)\n", - " - [String output](#String-output)\n", - " - [String input](#String-input)\n", - " - [Exercises](#Exercises)\n", - " - [File I/O](#File-I/O)\n", - " - [Paths](#Paths)\n", - " - [Exercises](#Exercises)\n", - " - [Reading from a file](#Reading-from-a-file)\n", - " - [Exercises](#Exercises)\n", - " - [Writing to a file](#Writing-to-a-file)\n", - " - [Exercises](#Exercises)\n", - " - [Context managers](#Context-managers)\n", - " - [Binary I/O](#Binary-I/O)\n", - " - [Bytes and strings](#Bytes-and-strings)\n", - " - [Converting bytes to text ](#Converting-bytes-to-text)\n", - " - [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", - " - [Exercises](#Exercises)\n", - " - [Exercise 1: CSV to dictionary 🌶️🌶️](#Exercise-1:-CSV-to-dictionary-🌶️🌶️)\n", - " - [Exercise 2: Counting words 🌶️](#Exercise-2:-Counting-words-🌶️)\n", - " - [Exercise 3: Letter statistics 🌶️🌶️](#Exercise-3:-Letter-statistics-🌶️🌶️)\n", - " - [Exercise 4: Translating words 🌶️🌶️](#Exercise-4:-Translating-words-🌶️🌶️)\n", - " - [Exercise 5: Binary format 🌶️🌶️🌶️](#Exercise-5:-Binary-format-🌶️🌶️🌶️)" + " - [References](#References)\n", + " - [Introduction](#Introduction)\n", + " - [String input and output ](#String-input-and-output)\n", + " - [String output](#String-output)\n", + " - [String input](#String-input)\n", + " - [Warm-up exercises](#Warm-up-exercises)\n", + " - [File I/O](#File-I/O)\n", + " - [Paths](#Paths)\n", + " - [Exercises on Paths](#Exercises-on-Paths)\n", + " - [Reading from a file](#Reading-from-a-file)\n", + " - [Writing to a file](#Writing-to-a-file)\n", + " - [Exercises on file reading and writing](#Exercises-on-file-reading-and-writing)\n", + " - [Context managers](#Context-managers)\n", + " - [Binary I/O](#Binary-I/O)\n", + " - [Bytes and strings](#Bytes-and-strings)\n", + " - [Converting bytes to text ](#Converting-bytes-to-text)\n", + " - [Reading/Writing CSV files](#Reading/Writing-CSV-files)\n", + " - [Exercises](#Exercises)\n", + " - [Exercise 1: CSV to dictionary 🌶️🌶️](#Exercise-1:-CSV-to-dictionary-🌶️🌶️)\n", + " - [Exercise 2: Counting words 🌶️](#Exercise-2:-Counting-words-🌶️)\n", + " - [Exercise 3: Letter statistics 🌶️🌶️](#Exercise-3:-Letter-statistics-🌶️🌶️)\n", + " - [Exercise 4: Translating words 🌶️🌶️](#Exercise-4:-Translating-words-🌶️🌶️)\n", + " - [Exercise 5: Binary format 🌶️🌶️🌶️](#Exercise-5:-Binary-format-🌶️🌶️🌶️)" ] }, { @@ -130,7 +129,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Exercises" + "### Warm-up exercises" ] }, { @@ -366,7 +365,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Exercises" + "### Exercises on Paths" ] }, { @@ -515,37 +514,6 @@ "This is the most *pythonic* way to read a file line-by-line instead of reading the full contents at once." ] }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercises" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "1. Modify the function `solution_read_file` to return the content of the file passed as the `input_file` argument. Return the content as a **list of strings**, one string per line.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%ipytest\n", - "\n", - "from pathlib import Path\n", - "\n", - "def solution_read_file(input_file: Path) -> \"list[str]\": \n", - " \"\"\"Write your solution here\"\"\"\n", - " pass" - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -615,7 +583,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Exercises" + "### Exercises on file reading and writing" ] }, { @@ -632,7 +600,30 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "1. Modify the function `solution_write_file` to write the sentence \"python tutorial 2023\" (**without quotes**) to the file `output_file`, which is available as a `Path` object as argument to the function." + "1. Modify the function `solution_read_file` to return the content of the file passed as the `input_file` argument. Return the content as a **list of strings**, one string per line.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%ipytest\n", + "\n", + "from pathlib import Path\n", + "\n", + "def solution_read_file(input_file: Path) -> \"list[str]\": \n", + " \"\"\"Write your solution here\"\"\"\n", + " pass" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Modify the function `solution_write_file` to write the sentence \"python tutorial 2023\" (**without quotes**) to the file `output_file`, which is available as a `Path` object as argument to the function." ] }, { @@ -657,7 +648,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "1. Modify the function `solution_read_write_file` to read the lines from the file `input_file` and write them in the form `line, length`, to the file `output_file`. Here `line` is the line of text in `input_file` **without the line ending**, `length` is **number of characters** in that line.\n", + "3. Modify the function `solution_read_write_file` to read the lines from the file `input_file` and write them in the form `line, length`, to the file `output_file`. Here `line` is the line of text in `input_file` **without the line ending**, `length` is **number of characters** in that line.\n", "If `input_file` contains these lines:\n", " ```\n", " first\n",